├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.MD
└── workflows
│ ├── dart.yml
│ ├── pub-score.yml
│ └── pubdev.yml
├── .gitignore
├── .idea
├── .gitignore
├── libraries
│ ├── Dart_SDK.xml
│ └── Flutter_Plugins.xml
├── misc.xml
├── modules.xml
├── runConfigurations
│ └── example_lib_main_dart.xml
└── vcs.xml
├── .metadata
├── .vscode
└── launch.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── agora_flutter_uikit.iml
├── analysis_options.yaml
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── io
│ └── agora
│ └── agora_uikit
│ └── AgoraUikitPlugin.kt
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── io
│ │ │ │ │ └── agora
│ │ │ │ │ ├── agora_flutter_uikit_example
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── agora_uikit
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Example_ScreenSharing_Extension
│ │ ├── Info.plist
│ │ └── SampleHandler.swift
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ └── main.dart
├── linux
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ │ ├── CMakeLists.txt
│ │ ├── generated_plugin_registrant.cc
│ │ ├── generated_plugin_registrant.h
│ │ └── generated_plugins.cmake
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── macos
│ ├── .gitignore
│ ├── Flutter
│ │ ├── Flutter-Debug.xcconfig
│ │ ├── Flutter-Release.xcconfig
│ │ └── GeneratedPluginRegistrant.swift
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── app_icon_1024.png
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_256.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_512.png
│ │ │ └── app_icon_64.png
│ │ ├── Base.lproj
│ │ └── MainMenu.xib
│ │ ├── Configs
│ │ ├── AppInfo.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
├── pubspec.yaml
├── test
│ └── widget_test.dart
├── web
│ ├── favicon.png
│ ├── icons
│ │ ├── Icon-192.png
│ │ └── Icon-512.png
│ ├── index.html
│ └── manifest.json
└── windows
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
│ └── runner
│ ├── CMakeLists.txt
│ ├── Runner.rc
│ ├── flutter_window.cpp
│ ├── flutter_window.h
│ ├── main.cpp
│ ├── resource.h
│ ├── resources
│ └── app_icon.ico
│ ├── run_loop.cpp
│ ├── run_loop.h
│ ├── runner.exe.manifest
│ ├── utils.cpp
│ ├── utils.h
│ ├── win32_window.cpp
│ └── win32_window.h
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── AgoraUikitPlugin.h
│ ├── AgoraUikitPlugin.m
│ └── SwiftAgoraUikitPlugin.swift
└── agora_uikit.podspec
├── lib
├── agora_uikit.dart
├── controllers
│ ├── rtc_buttons.dart
│ ├── rtc_event_handlers.dart
│ ├── rtc_token_handler.dart
│ ├── rtm_channel_event_handler.dart
│ ├── rtm_client_event_handler.dart
│ ├── rtm_controller.dart
│ ├── rtm_controller_helper.dart
│ ├── rtm_mute_request.dart
│ ├── rtm_token_handler.dart
│ └── session_controller.dart
├── models
│ ├── agora_channel_data.dart
│ ├── agora_connection_data.dart
│ ├── agora_rtc_event_handlers.dart
│ ├── agora_rtm_channel_event_handler.dart
│ ├── agora_rtm_client_event_handler.dart
│ ├── agora_rtm_mute_request.dart
│ ├── agora_settings.dart
│ ├── agora_user.dart
│ └── rtm_message.dart
└── src
│ ├── agora_client.dart
│ ├── buttons
│ ├── buttons.dart
│ └── cloud_recording_button.dart
│ ├── enums.dart
│ └── layout
│ ├── floating_layout.dart
│ ├── grid_layout.dart
│ ├── layout.dart
│ ├── one_to_one_layout.dart
│ └── widgets
│ ├── disabled_video_widget.dart
│ ├── host_controls.dart
│ ├── number_of_users.dart
│ └── user_av_state_widget.dart
├── pubspec.yaml
└── test
└── agora_flutter_uikit_test.dart
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: Meherdeep
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: Meherdeep
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.MD:
--------------------------------------------------------------------------------
1 |
2 |
3 | > Release Version:
4 |
5 | ## Release Notes
6 |
7 | -
8 | -
9 |
10 | ## Pull request checklist
11 |
12 | Please check if your PR fulfills the following requirements:
13 | - [ ] Tests for the changes have been added (for bug fixes / features)
14 | - [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features)
15 | - [ ] The GitHub Actions pass building and linting. Linter returns no warnings or errors.
16 | - [ ] The QA checklist below has been completed
17 |
18 | ## Pull request type
19 |
20 |
21 |
22 |
23 |
24 | Please check the type of change your PR introduces:
25 | - [ ] Bugfix
26 | - [ ] Feature
27 | - [ ] Code style update (formatting, renaming)
28 | - [ ] Refactoring (no functional changes, no api changes)
29 | - [ ] Build related changes
30 | - [ ] Documentation content changes
31 | - [ ] Other (please describe):
32 |
33 |
34 | ## What is the current behavior?
35 |
36 |
37 | Issue Number: N/A
38 |
39 |
40 | ## What is the new behavior?
41 |
42 |
43 | -
44 | -
45 | -
46 |
47 | ## Does this introduce a breaking change?
48 |
49 | - [ ] Yes
50 | - [ ] No
51 |
52 |
53 |
54 |
55 | ## QA Checklist
56 |
57 | ### VideoUIKit Update Checklist (Minor or Patch Release)
58 |
59 | - [ ] Using the latest version of Agora's Video SDK
60 | - [ ] Example apps are all functional
61 | - [ ] Core features are still working (both ways across platforms)
62 | - [ ] Camera + Mic muting works for local and remote users
63 | - [ ] Users are added and removed correctly when they join and leave the channel
64 | - [ ] Older versions of the library gracefully handle changes (Create issue if not)
65 | - [ ] Builtin buttons all work as expected
66 | - [ ] Any newly deprecated methods are flagged as such inline and in documentation
67 |
68 |
69 |
70 | ### VideoUIKit Update Checklist (Major Release)
71 |
72 | - [ ] The above checklist is completed (except backwards compatibility)
73 | - [ ] Thoroughly tested for crashes, across multiple platforms at the same time
74 |
75 | #### QA Notes
76 |
77 | ## Other information
78 |
79 |
--------------------------------------------------------------------------------
/.github/workflows/dart.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub.
2 | # They are provided by a third-party and are governed by
3 | # separate terms of service, privacy policy, and support
4 | # documentation.
5 |
6 | name: Flutter
7 |
8 | on:
9 | push:
10 | branches: [dev]
11 | pull_request:
12 | branches: [dev]
13 |
14 | jobs:
15 | build:
16 | runs-on: macos-latest
17 |
18 | steps:
19 | - name: Export Release Timestamp
20 | run: echo "APP_VERSION=v$(date +'%Y.%m.%d.%H.%M.%S')" >> $GITHUB_ENV
21 |
22 | - name: Checkout repository
23 | uses: actions/checkout@v2
24 |
25 | - name: Set up Java
26 | uses: actions/setup-java@v2
27 | with:
28 | java-version: "15"
29 | distribution: "adopt"
30 |
31 | - name: Set up Flutter
32 | uses: subosito/flutter-action@v1
33 | with:
34 | channel: "stable"
35 |
36 | - name: Install pub Dependencies
37 | run: flutter pub get
38 |
39 | - name: Check Format
40 | run: flutter format --set-exit-if-changed .
41 |
42 | - name: Analyze project source
43 | run: flutter analyze
44 |
45 | - name: Run Tests
46 | run: flutter test
47 |
48 | - name: Build Android App
49 | working-directory: ./example
50 | run: flutter build appbundle
51 | - name: Build iOS App
52 | working-directory: ./example
53 | run: flutter build ios --no-codesign
54 |
--------------------------------------------------------------------------------
/.github/workflows/pub-score.yml:
--------------------------------------------------------------------------------
1 | name: Pub Score Checker
2 | on:
3 | pull_request:
4 | branches:
5 | - "main"
6 | push:
7 | branches: ["main"]
8 |
9 | jobs:
10 |
11 | package-analysis:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 | - name: Check Versions 🔎
18 | run: |
19 | export VRSN=$(grep 'String version = ' lib/models/agora_rtm_mute_request.dart | sed -e 's,.*\"\(.*\)\";,\1,')
20 | export PUBSPEC_VERSION=$(grep 'version: ' pubspec.yaml | sed -e 's,.*: \(.*\),\1,')
21 | if [ "$PUBSPEC_VERSION" != "$VRSN" ]; then
22 | echo "Version in 'lib/models/agora_rtm_mute_request.dart' does not match pubspec.";
23 | exit 1;
24 | fi
25 | - uses: axel-op/dart-package-analyzer@v3
26 | id: analysis
27 | with:
28 | githubToken: ${{ secrets.GITHUB_TOKEN }}
29 | - name: Check scores
30 | env:
31 | TOTAL: ${{ steps.analysis.outputs.total }}
32 | TOTAL_MAX: ${{ steps.analysis.outputs.total_max }}
33 | run: |
34 | echo Score is: $TOTAL
35 | if (( $TOTAL < 120 ))
36 | then
37 | echo Pub Score too low.
38 | exit 1
39 | fi
40 |
--------------------------------------------------------------------------------
/.github/workflows/pubdev.yml:
--------------------------------------------------------------------------------
1 | name: Publish to Pub.dev 🚀
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | publishing:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout 🛎️
13 | uses: actions/checkout@v3
14 | - name: Check Versions 🔎
15 | run: |
16 | set -eo pipefail
17 | export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
18 | export VRSN=$(grep 'String version = ' lib/models/agora_rtm_mute_request.dart | sed -e 's,.*\"\(.*\)\";,\1,')
19 | export PUBSPEC_VERSION=$(grep 'version: ' pubspec.yaml | sed -e 's,.*: \(.*\),\1,')
20 | if [ "$LIB_VERSION" != "$VRSN" ]; then
21 | echo "Version in 'lib/models/agora_rtm_mute_request.dart' does not match tag.";
22 | exit 1;
23 | fi
24 | if [ "$LIB_VERSION" != "$PUBSPEC_VERSION" ]; then
25 | echo "Version in 'pubspec.yaml' does not match tag.";
26 | exit 1;
27 | fi
28 | - name: Publish Dart Package 🚢
29 | uses: k-paxian/dart-package-publisher@1.5
30 | with:
31 | accessToken: ${{ secrets.OAUTH_ACCESS_TOKEN }}
32 | refreshToken: ${{ secrets.OAUTH_REFRESH_TOKEN }}
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | # If you're building an application, you may want to check-in your pubspec.lock
10 | pubspec.lock
11 |
12 | # Directory created by dartdoc
13 | # If you don't generate documentation locally you can remove this line.
14 | doc/api/
15 |
16 | # dotenv environment variables file
17 | .env*
18 |
19 | # Avoid committing generated Javascript files:
20 | *.dart.js
21 | *.info.json # Produced by the --dump-info flag.
22 | *.js # When generated by dart2js. Don't specify *.js if your
23 | # project includes source files written in JavaScript.
24 | *.js_
25 | *.js.deps
26 | *.js.map
27 |
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example_lib_main_dart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 8264cb3e8a797eef39cbcd32bb56fd07790efb7f
8 | channel: dev
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "agora_flutter_uikit",
9 | "request": "launch",
10 | "type": "dart"
11 | },
12 | {
13 | "name": "example",
14 | "cwd": "example",
15 | "request": "launch",
16 | "type": "dart"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.3.10
2 | - Upper bounds for lints
3 |
4 | ## 1.3.9
5 | - Update to support newer lints
6 |
7 | ## 1.3.8
8 | - Update types from agora_rtc_engine 6.3.0
9 |
10 | ## 1.3.7
11 | - Fixes active speaker bug when user overrides with manual pin
12 | - Updates permission_handler to v11.0.0
13 |
14 | ## 1.3.6
15 |
16 | - Fixes error [[#151](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/issues/151)
17 | - Updating RTC to v6.2.2
18 |
19 | ## 1.3.5
20 |
21 | - Allow for wider range of `http` packages
22 |
23 | ## 1.3.4
24 |
25 | - Fixes error [#137](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/issues/137)
26 | - Setting RTC to v6.1.0 and RTM to v1.5.0
27 |
28 | ## 1.3.3
29 |
30 | - BREAKING CHANGE: VideoSourceType for `onFirstLocalVideoFrame` callback
31 |
32 | ## 1.3.2
33 |
34 | - Added callback for cloud recording destination URL
35 | - Added loading state for cloud recording button
36 | - Update to use new cloud recording schema
37 |
38 | ## 1.3.1
39 |
40 | - BUG FIX: No longer shows cloud recording by default. Need to enable it with `cloudRecordingEnabled: true`
41 |
42 | ## 1.3.0
43 |
44 | - Added Cloud Recording. Follow the [Guide](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/wiki/Examples#cloud-recording)
45 |
46 | ## 1.2.1
47 |
48 | - Fixes requestPort bug when joining from separate screen
49 | - Fixes post call exception when going back to previous screen
50 |
51 |
52 | ## 1.2.0
53 |
54 | - Added screen sharing for Android/iOS
55 | - Added layout for one to one video calling
56 | - Fixes issue [#122](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/issues/122)
57 |
58 |
59 | ## 1.1.2
60 |
61 | - Fixes issue [#115](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/issues/115)
62 | - Fixes view of remote user with a disabled video
63 | - Fixes pinning a remote user causing white screen error
64 |
65 | ## 1.1.1
66 |
67 | - Updated RTC to v6.1.0
68 | - Updated RTM to v1.5.0
69 |
70 | ## 1.1.0
71 |
72 | - Updated RTC to 4.x SDK
73 |
74 | ## 1.0.4
75 |
76 | - Updated README badges
77 | - Added GitHub actions for publishing to pub.dev
78 |
79 | ## 1.0.3
80 |
81 | - Updated permission_handler to [10.0.0](https://pub.dev/packages/permission_handler/changelog#1000). `compileSdkVersion` must be set to 33 as a minimum to handle the new notifications.
82 | - Fixes issue [#98](https://github.com/AgoraIO-Community/Flutter-UIKit/issues/98). Added `onDisconnect` function inside the `AgoraVideoButtons` widget that can be used to define any navigation or function that occurs when the end call button is pressed.
83 | - Fixes issue [#96](https://github.com/AgoraIO-Community/Flutter-UIKit/issues/96). Added a broadcaster check before adding a user view to the grid layout.
84 |
85 | > When updating to version 1.0.3 make sure to update the android/app/build.gradle file and set the compileSdkVersion to 33.
86 |
87 | ## 1.0.2
88 |
89 | - Fixes the issue with `enabledButtons`(issue [#86](https://github.com/AgoraIO-Community/Flutter-UIKit/issues/86))
90 |
91 | ## 1.0.1
92 |
93 | - Add Error param to delegate methods to pass through the error
94 | - Show correct versions of RTC and RTM
95 | - Show RTM classes
96 | - Update RTM to v1.1.1
97 |
98 | ## 1.0.0
99 |
100 | - Updated RTC to v5.2.0
101 | - JSON serialization for all the host controls
102 | - Cleaning the `session_controller`
103 | - Added RTM event handlers
104 | - RTM is enabled by default
105 | - Supports Flutter 3.0
106 |
107 | ## 1.0.0-beta.2
108 |
109 | - Updated RTC to v5.2.0
110 | - JSON serialization for all the host controls
111 | - Cleaning the `session_controller`
112 | - Added RTM event handlers
113 | - RTM is enabled by default
114 | - Supports Flutter 3.0
115 |
116 | ## 1.0.0-beta.1
117 |
118 | - Added Agora RTM SDK v1.1.0
119 | - Host Controls (Mute remote users audio and video)
120 | - Auto-permission for camera and mic (for all the broadcasters)
121 | - Fixes RTM IPV6 issue.
122 |
123 | ## 0.0.4
124 |
125 | - Upgraded the agora_rtc_engine to v4.0.6
126 | - New method of initialization
127 | - Added all the event handlers
128 | - Fixed disabled video widget for grid view
129 | - Added video render mode
130 |
131 | ## 0.0.3
132 |
133 | - Upgraded the agora_rtc_engine to v4.0.5
134 | - Black Screen Fix
135 | - Remove unnecessary null checks
136 | - AreaCode bug fix
137 | - Added roadmaps to README.md
138 |
139 | ## 0.0.2
140 |
141 | - Update README
142 |
143 | ## 0.0.1
144 |
145 | Agora Flutter UIKit Release.
146 | Features:
147 | * Automatically layout all video streams
148 | * Builtin floating and grid layouts
149 | * Display the active speaker in the larger display while using the floating layout
150 | * Allowing you to pin any stream to the larger display while using the floating layout
151 | * Default buttons for disabling camera or microphone, switch cameras, end call
152 | * Add, remove or customize buttons
153 | * Auto hide buttons after a fixed period of time
154 | * Icon for signalling the local and remote user microphone and camera state
155 | * Display number of users in the channel
156 | * Automatically fetch token from a given token server
157 | * Subscribing to high or low-quality video streams
158 |
159 |
160 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | devrel@agora.io.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute to Agora VideoUIKit
2 |
3 | ## **Did you find a bug?**
4 |
5 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/issues).
6 |
7 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/AgoraIO-Community/VideoUIKit-Flutter/issues). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
8 |
9 | * If possible, use the relevant bug report templates to create the issue.
10 |
11 | ## **Did you write a patch that fixes a bug?**
12 |
13 | * Open a new GitHub pull request with the patch.
14 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
15 | * Ensure that the patch compiles and runs on iOS, and Android.
16 |
17 | ## **Did you fix whitespace, format code, or make a purely cosmetic patch?**
18 |
19 | Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Agora VideoUIKit will generally not be accepted.
20 |
21 | ## **Do you intend to add a new feature or change an existing one?**
22 |
23 | * Suggest your change in the form of a Feature Request Issue on this repository.
24 |
25 | * One of the team at Agora will respond to your issue, and together we can figure out whether this feature is applicable to be added to Agora VideoUIKit.
26 |
27 | ## **Do you have questions about the source code?**
28 |
29 | * Ask any question about how to use VideoUIKit on the [RTE Dev Slack](https://www.agora.io/en/join-slack/).
30 |
31 | ---
32 |
33 | Thanks for taking the time to read through our contributor guidelines,
34 |
35 | Agora Developer Relations Team
36 | devrel@agora.io
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Agora.io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/agora_flutter_uikit.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:lints/recommended.yaml
2 |
3 | linter:
4 | rules:
5 | - unawaited_futures
6 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'io.agora.agora_flutter_uikit'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.6.0'
6 | repositories {
7 | google()
8 | jcenter()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:7.0.1'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdkVersion 33
29 |
30 | sourceSets {
31 | main.java.srcDirs += 'src/main/kotlin'
32 | }
33 | defaultConfig {
34 | minSdkVersion 16
35 | }
36 | }
37 |
38 | dependencies {
39 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
40 | }
41 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'agora_flutter_uikit'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/io/agora/agora_uikit/AgoraUikitPlugin.kt:
--------------------------------------------------------------------------------
1 | package io.agora.agora_uikit
2 |
3 | import androidx.annotation.NonNull
4 |
5 | import io.flutter.embedding.engine.plugins.FlutterPlugin
6 | import io.flutter.plugin.common.MethodCall
7 | import io.flutter.plugin.common.MethodChannel
8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
9 | import io.flutter.plugin.common.MethodChannel.Result
10 |
11 | /** AgoraUikitPlugin */
12 | class AgoraUikitPlugin: FlutterPlugin, MethodCallHandler {
13 | /// The MethodChannel that will the communication between Flutter and native Android
14 | ///
15 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
16 | /// when the Flutter Engine is detached from the Activity
17 | private lateinit var channel : MethodChannel
18 |
19 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
20 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "agora_uikit")
21 | channel.setMethodCallHandler(this)
22 | }
23 |
24 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
25 | if (call.method == "getPlatformVersion") {
26 | result.success("Android ${android.os.Build.VERSION.RELEASE}")
27 | } else {
28 | result.notImplemented()
29 | }
30 | }
31 |
32 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
33 | channel.setMethodCallHandler(null)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 | /ios/Podfile.lock
48 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 8264cb3e8a797eef39cbcd32bb56fd07790efb7f
8 | channel: dev
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # Example app
2 |
3 | Demonstrates how to use the agora_uikit plugin.
4 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 33
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | defaultConfig {
36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
37 | applicationId "io.agora.agora_uikit_example"
38 | minSdkVersion 16
39 | targetSdkVersion 30
40 | versionCode flutterVersionCode.toInteger()
41 | versionName flutterVersionName
42 | }
43 |
44 | buildTypes {
45 | release {
46 | // TODO: Add your own signing config for the release build.
47 | // Signing with the debug keys for now, so `flutter run --release` works.
48 | signingConfig signingConfigs.debug
49 | }
50 | }
51 | }
52 |
53 | flutter {
54 | source '../..'
55 | }
56 |
57 | dependencies {
58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
59 | }
60 |
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | ## Flutter wrapper
2 | -keep class io.flutter.app.** { *; }
3 | -keep class io.flutter.plugin.** { *; }
4 | -keep class io.flutter.util.** { *; }
5 | -keep class io.flutter.view.** { *; }
6 | -keep class io.flutter.** { *; }
7 | -keep class io.flutter.plugins.** { *; }
8 | -dontwarn io.flutter.embedding.**
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
23 |
27 |
31 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/io/agora/agora_flutter_uikit_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.agora.agora_flutter_uikit_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/io/agora/agora_uikit/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.agora.agora_uikit
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.0'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.0.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | maven { url 'https://www.jitpack.io' }
19 | }
20 | }
21 |
22 | rootProject.buildDir = '../build'
23 | subprojects {
24 | project.buildDir = "${rootProject.buildDir}/${project.name}"
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | tasks.register("clean", Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Sep 07 01:51:26 IST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/example/ios/Example_ScreenSharing_Extension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSExtension
6 |
7 | NSExtensionPointIdentifier
8 | com.apple.broadcast-services-upload
9 | NSExtensionPrincipalClass
10 | $(PRODUCT_MODULE_NAME).SampleHandler
11 | RPBroadcastProcessMode
12 | RPBroadcastProcessModeSampleBuffer
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/ios/Example_ScreenSharing_Extension/SampleHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleHandler.swift
3 | // Example_ScreenSharing_Extension
4 | //
5 | // Created by Meherdeep Thakur on 03/02/23.
6 | //
7 |
8 | import ReplayKit
9 | import AgoraBroadcastExtensionHelper
10 |
11 | class SampleHandler: AgoraBroadcastSampleHandler {
12 | override func getBroadcastData() -> AgoraBroadcastExtData? {
13 | return AgoraBroadcastExtData(
14 | appId: "<#Agora App ID#>",
15 | channel: "<#Channel Name#>",
16 | token: <#Token or nil#>,
17 | uid: 0
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
43 | target 'Example_ScreenSharing_Extension' do
44 | use_frameworks!
45 | use_modular_headers!
46 |
47 | # pod 'AgoraRtcEngine_iOS', '4.1.0'
48 | pod 'AgoraBroadcastExtensionHelper_iOS', '4.1.1'
49 | end
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 | import ReplayKit
4 |
5 |
6 | @UIApplicationMain
7 | @objc class AppDelegate: FlutterAppDelegate {
8 | override func application(
9 | _ application: UIApplication,
10 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
11 | ) -> Bool {
12 |
13 | var controller = window.rootViewController as? FlutterViewController
14 | var screensharingIOSChannel = FlutterMethodChannel(
15 | name: "example_screensharing_ios",
16 | binaryMessenger: controller as! FlutterBinaryMessenger)
17 |
18 | screensharingIOSChannel.setMethodCallHandler({
19 | (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
20 | if #available(iOS 12.0, *) {
21 | DispatchQueue.main.async(execute: {
22 | let systemBroadcastPicker = RPSystemBroadcastPickerView(
23 | frame: CGRect(x: 50, y: 200, width: 60, height: 60))
24 | systemBroadcastPicker.showsMicrophoneButton = true
25 | systemBroadcastPicker.autoresizingMask = [.flexibleBottomMargin, .flexibleRightMargin]
26 | if let url = Bundle.main.url(forResource: "Example_ScreenSharing_Extension", withExtension: "appex", subdirectory: "PlugIns"),
27 | let bundle = Bundle(url: url) {
28 | systemBroadcastPicker.preferredExtension = bundle.bundleIdentifier
29 | }
30 | controller?.view.addSubview(systemBroadcastPicker)
31 | })
32 | }
33 | })
34 |
35 | GeneratedPluginRegistrant.register(with: self)
36 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | Agora UIKit
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | NSCameraUsageDescription
26 | The app tries to use your camera
27 | NSMicrophoneUsageDescription
28 | The app tries to use your microphone
29 | UIBackgroundModes
30 |
31 | fetch
32 | audio
33 | processing
34 | voip
35 |
36 | UIFileSharingEnabled
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIMainStoryboardFile
41 | Main
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 | UIViewControllerBasedStatusBarAppearance
56 |
57 | io.flutter.embedded_views_preview
58 |
59 | CADisableMinimumFrameDurationOnPhone
60 |
61 | UIApplicationSupportsIndirectInputEvents
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_uikit/agora_uikit.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | void main() {
5 | runApp(MyApp());
6 | }
7 |
8 | class MyApp extends StatefulWidget {
9 | const MyApp({super.key});
10 |
11 | @override
12 | State createState() => _MyAppState();
13 | }
14 |
15 | class _MyAppState extends State {
16 | final AgoraClient client = AgoraClient(
17 | agoraConnectionData: AgoraConnectionData(
18 | appId: "<--Add your App Id here-->",
19 | channelName: "test",
20 | username: "user",
21 | ),
22 | );
23 |
24 | @override
25 | void initState() {
26 | super.initState();
27 | initAgora();
28 | }
29 |
30 | void initAgora() async {
31 | await client.initialize();
32 | }
33 |
34 | @override
35 | Widget build(BuildContext context) {
36 | return MaterialApp(
37 | home: Scaffold(
38 | appBar: AppBar(
39 | title: const Text('Agora VideoUIKit'),
40 | centerTitle: true,
41 | ),
42 | body: SafeArea(
43 | child: Stack(
44 | children: [
45 | AgoraVideoViewer(
46 | client: client,
47 | layoutType: Layout.floating,
48 | enableHostControls: true, // Add this to enable host controls
49 | ),
50 | AgoraVideoButtons(
51 | client: client,
52 | addScreenSharing: false, // Add this to enable screen sharing
53 | ),
54 | ],
55 | ),
56 | ),
57 | ),
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/example/linux/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral
2 |
--------------------------------------------------------------------------------
/example/linux/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(runner LANGUAGES CXX)
3 |
4 | set(BINARY_NAME "Agora UIKit")
5 | set(APPLICATION_ID "io.agora.agora_uikit_example")
6 |
7 | cmake_policy(SET CMP0063 NEW)
8 |
9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
10 |
11 | # Root filesystem for cross-building.
12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT)
13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
19 | endif()
20 |
21 | # Configure build options.
22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
23 | set(CMAKE_BUILD_TYPE "Debug" CACHE
24 | STRING "Flutter build mode" FORCE)
25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
26 | "Debug" "Profile" "Release")
27 | endif()
28 |
29 | # Compilation settings that should be applied to most targets.
30 | function(APPLY_STANDARD_SETTINGS TARGET)
31 | target_compile_features(${TARGET} PUBLIC cxx_std_14)
32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror)
33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
35 | endfunction()
36 |
37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
38 |
39 | # Flutter library and tool build rules.
40 | add_subdirectory(${FLUTTER_MANAGED_DIR})
41 |
42 | # System-level dependencies.
43 | find_package(PkgConfig REQUIRED)
44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
45 |
46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
47 |
48 | # Application build
49 | add_executable(${BINARY_NAME}
50 | "main.cc"
51 | "my_application.cc"
52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
53 | )
54 | apply_standard_settings(${BINARY_NAME})
55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter)
56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
57 | add_dependencies(${BINARY_NAME} flutter_assemble)
58 | # Only the install-generated bundle's copy of the executable will launch
59 | # correctly, since the resources must in the right relative locations. To avoid
60 | # people trying to run the unbundled copy, put it in a subdirectory instead of
61 | # the default top-level location.
62 | set_target_properties(${BINARY_NAME}
63 | PROPERTIES
64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
65 | )
66 |
67 | # Generated plugin build rules, which manage building the plugins and adding
68 | # them to the application.
69 | include(flutter/generated_plugins.cmake)
70 |
71 |
72 | # === Installation ===
73 | # By default, "installing" just makes a relocatable bundle in the build
74 | # directory.
75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
78 | endif()
79 |
80 | # Start with a clean build bundle directory every time.
81 | install(CODE "
82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
83 | " COMPONENT Runtime)
84 |
85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
87 |
88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
89 | COMPONENT Runtime)
90 |
91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
92 | COMPONENT Runtime)
93 |
94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
95 | COMPONENT Runtime)
96 |
97 | if(PLUGIN_BUNDLED_LIBRARIES)
98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
100 | COMPONENT Runtime)
101 | endif()
102 |
103 | # Fully re-copy the assets directory on each build to avoid having stale files
104 | # from a previous install.
105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
106 | install(CODE "
107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
108 | " COMPONENT Runtime)
109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
111 |
112 | # Install the AOT library on non-Debug builds only.
113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
115 | COMPONENT Runtime)
116 | endif()
117 |
--------------------------------------------------------------------------------
/example/linux/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 |
3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
4 |
5 | # Configuration provided via flutter tool.
6 | include(${EPHEMERAL_DIR}/generated_config.cmake)
7 |
8 | # TODO: Move the rest of this into files in ephemeral. See
9 | # https://github.com/flutter/flutter/issues/57146.
10 |
11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...),
12 | # which isn't available in 3.10.
13 | function(list_prepend LIST_NAME PREFIX)
14 | set(NEW_LIST "")
15 | foreach(element ${${LIST_NAME}})
16 | list(APPEND NEW_LIST "${PREFIX}${element}")
17 | endforeach(element)
18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
19 | endfunction()
20 |
21 | # === Flutter Library ===
22 | # System-level dependencies.
23 | find_package(PkgConfig REQUIRED)
24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
27 |
28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
29 |
30 | # Published to parent scope for install step.
31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
35 |
36 | list(APPEND FLUTTER_LIBRARY_HEADERS
37 | "fl_basic_message_channel.h"
38 | "fl_binary_codec.h"
39 | "fl_binary_messenger.h"
40 | "fl_dart_project.h"
41 | "fl_engine.h"
42 | "fl_json_message_codec.h"
43 | "fl_json_method_codec.h"
44 | "fl_message_codec.h"
45 | "fl_method_call.h"
46 | "fl_method_channel.h"
47 | "fl_method_codec.h"
48 | "fl_method_response.h"
49 | "fl_plugin_registrar.h"
50 | "fl_plugin_registry.h"
51 | "fl_standard_message_codec.h"
52 | "fl_standard_method_codec.h"
53 | "fl_string_codec.h"
54 | "fl_value.h"
55 | "fl_view.h"
56 | "flutter_linux.h"
57 | )
58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
59 | add_library(flutter INTERFACE)
60 | target_include_directories(flutter INTERFACE
61 | "${EPHEMERAL_DIR}"
62 | )
63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
64 | target_link_libraries(flutter INTERFACE
65 | PkgConfig::GTK
66 | PkgConfig::GLIB
67 | PkgConfig::GIO
68 | )
69 | add_dependencies(flutter flutter_assemble)
70 |
71 | # === Flutter tool backend ===
72 | # _phony_ is a non-existent file to force this command to run every time,
73 | # since currently there's no way to get a full input/output list from the
74 | # flutter tool.
75 | add_custom_command(
76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_
78 | COMMAND ${CMAKE_COMMAND} -E env
79 | ${FLUTTER_TOOL_ENVIRONMENT}
80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
82 | VERBATIM
83 | )
84 | add_custom_target(flutter_assemble DEPENDS
85 | "${FLUTTER_LIBRARY}"
86 | ${FLUTTER_LIBRARY_HEADERS}
87 | )
88 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 |
10 | void fl_register_plugins(FlPluginRegistry* registry) {
11 | }
12 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void fl_register_plugins(FlPluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | )
7 |
8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
9 | )
10 |
11 | set(PLUGIN_BUNDLED_LIBRARIES)
12 |
13 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
18 | endforeach(plugin)
19 |
20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
23 | endforeach(ffi_plugin)
24 |
--------------------------------------------------------------------------------
/example/linux/main.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | int main(int argc, char** argv) {
4 | g_autoptr(MyApplication) app = my_application_new();
5 | return g_application_run(G_APPLICATION(app), argc, argv);
6 | }
7 |
--------------------------------------------------------------------------------
/example/linux/my_application.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | #include
4 | #ifdef GDK_WINDOWING_X11
5 | #include
6 | #endif
7 |
8 | #include "flutter/generated_plugin_registrant.h"
9 |
10 | struct _MyApplication {
11 | GtkApplication parent_instance;
12 | char** dart_entrypoint_arguments;
13 | };
14 |
15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
16 |
17 | // Implements GApplication::activate.
18 | static void my_application_activate(GApplication* application) {
19 | MyApplication* self = MY_APPLICATION(application);
20 | GtkWindow* window =
21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
22 |
23 | // Use a header bar when running in GNOME as this is the common style used
24 | // by applications and is the setup most users will be using (e.g. Ubuntu
25 | // desktop).
26 | // If running on X and not using GNOME then just use a traditional title bar
27 | // in case the window manager does more exotic layout, e.g. tiling.
28 | // If running on Wayland assume the header bar will work (may need changing
29 | // if future cases occur).
30 | gboolean use_header_bar = TRUE;
31 | #ifdef GDK_WINDOWING_X11
32 | GdkScreen *screen = gtk_window_get_screen(window);
33 | if (GDK_IS_X11_SCREEN(screen)) {
34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
36 | use_header_bar = FALSE;
37 | }
38 | }
39 | #endif
40 | if (use_header_bar) {
41 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
42 | gtk_widget_show(GTK_WIDGET(header_bar));
43 | gtk_header_bar_set_title(header_bar, "Agora UIKit");
44 | gtk_header_bar_set_show_close_button(header_bar, TRUE);
45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
46 | }
47 | else {
48 | gtk_window_set_title(window, "Agora UIKit");
49 | }
50 |
51 | gtk_window_set_default_size(window, 1280, 720);
52 | gtk_widget_show(GTK_WIDGET(window));
53 |
54 | g_autoptr(FlDartProject) project = fl_dart_project_new();
55 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
56 |
57 | FlView* view = fl_view_new(project);
58 | gtk_widget_show(GTK_WIDGET(view));
59 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
60 |
61 | fl_register_plugins(FL_PLUGIN_REGISTRY(view));
62 |
63 | gtk_widget_grab_focus(GTK_WIDGET(view));
64 | }
65 |
66 | // Implements GApplication::local_command_line.
67 | static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) {
68 | MyApplication* self = MY_APPLICATION(application);
69 | // Strip out the first argument as it is the binary name.
70 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
71 |
72 | g_autoptr(GError) error = nullptr;
73 | if (!g_application_register(application, nullptr, &error)) {
74 | g_warning("Failed to register: %s", error->message);
75 | *exit_status = 1;
76 | return TRUE;
77 | }
78 |
79 | g_application_activate(application);
80 | *exit_status = 0;
81 |
82 | return TRUE;
83 | }
84 |
85 | // Implements GObject::dispose.
86 | static void my_application_dispose(GObject *object) {
87 | MyApplication* self = MY_APPLICATION(object);
88 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
89 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
90 | }
91 |
92 | static void my_application_class_init(MyApplicationClass* klass) {
93 | G_APPLICATION_CLASS(klass)->activate = my_application_activate;
94 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
95 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
96 | }
97 |
98 | static void my_application_init(MyApplication* self) {}
99 |
100 | MyApplication* my_application_new() {
101 | return MY_APPLICATION(g_object_new(my_application_get_type(),
102 | "application-id", APPLICATION_ID,
103 | "flags", G_APPLICATION_NON_UNIQUE,
104 | nullptr));
105 | }
106 |
--------------------------------------------------------------------------------
/example/linux/my_application.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_MY_APPLICATION_H_
2 | #define FLUTTER_MY_APPLICATION_H_
3 |
4 | #include
5 |
6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
7 | GtkApplication)
8 |
9 | /**
10 | * my_application_new:
11 | *
12 | * Creates a new Flutter-based application.
13 | *
14 | * Returns: a new #MyApplication.
15 | */
16 | MyApplication* my_application_new();
17 |
18 | #endif // FLUTTER_MY_APPLICATION_H_
19 |
--------------------------------------------------------------------------------
/example/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/xcuserdata/
7 |
--------------------------------------------------------------------------------
/example/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import agora_rtc_engine
9 | import iris_method_channel
10 |
11 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
12 | AgoraRtcNgPlugin.register(with: registry.registrar(forPlugin: "AgoraRtcNgPlugin"))
13 | IrisMethodChannelPlugin.register(with: registry.registrar(forPlugin: "IrisMethodChannelPlugin"))
14 | }
15 |
--------------------------------------------------------------------------------
/example/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.14'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_macos_podfile_setup
28 |
29 | target 'Runner' do
30 | use_frameworks!
31 | use_modular_headers!
32 |
33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
34 | end
35 |
36 | post_install do |installer|
37 | installer.pods_project.targets.each do |target|
38 | flutter_additional_macos_build_settings(target)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/example/macos/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - FlutterMacOS (1.0.0)
3 |
4 | DEPENDENCIES:
5 | - FlutterMacOS (from `Flutter/ephemeral`)
6 |
7 | EXTERNAL SOURCES:
8 | FlutterMacOS:
9 | :path: Flutter/ephemeral
10 |
11 | SPEC CHECKSUMS:
12 | FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
13 |
14 | PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
15 |
16 | COCOAPODS: 1.11.0.beta.1
17 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
64 |
65 |
71 |
73 |
79 |
80 |
81 |
82 |
84 |
85 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "app_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "app_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "app_icon_32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "app_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "app_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "app_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "app_icon_256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "app_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "app_icon_512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "app_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = Agora UIKit;
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = io.agora.agora_uikit_example;
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2021 io.agora. All rights reserved.
15 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/example/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/example/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | $(PRODUCT_COPYRIGHT)
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController.init()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: agora_uikit_example
2 | description: Demonstrates how to use the agora_flutter_uikit plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
7 |
8 | environment:
9 | sdk: ">=2.18.1 <3.0.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | agora_uikit:
16 | # When depending on this package from a real application you should use:
17 | # agora_uikit: ^x.y.z
18 | # See https://dart.dev/tools/pub/dependencies#version-constraints
19 | # The example app is bundled with the plugin so we use a path dependency on
20 | # the parent directory to use the current plugin's version.
21 | path: ../
22 |
23 | # The following adds the Cupertino Icons font to your application.
24 | # Use with the CupertinoIcons class for iOS style icons.
25 | cupertino_icons: ^1.0.2
26 |
27 | dev_dependencies:
28 | flutter_test:
29 | sdk: flutter
30 |
31 | # For information on the generic Dart part of this file, see the
32 | # following page: https://dart.dev/tools/pub/pubspec
33 |
34 | # The following section is specific to Flutter.
35 | flutter:
36 | # The following line ensures that the Material Icons font is
37 | # included with your application, so that you can use the icons in
38 | # the material Icons class.
39 | uses-material-design: true
40 |
41 | # To add assets to your application, add an assets section, like this:
42 | # assets:
43 | # - images/a_dot_burr.jpeg
44 | # - images/a_dot_ham.jpeg
45 |
46 | # An image asset can refer to one or more resolution-specific "variants", see
47 | # https://flutter.dev/assets-and-images/#resolution-aware.
48 |
49 | # For details regarding adding assets from package dependencies, see
50 | # https://flutter.dev/assets-and-images/#from-packages
51 |
52 | # To add custom fonts to your application, add a fonts section here,
53 | # in this "flutter" section. Each entry in this list should have a
54 | # "family" key with the font family name, and a "fonts" key with a
55 | # list giving the asset and other descriptors for the font. For
56 | # example:
57 | # fonts:
58 | # - family: Schyler
59 | # fonts:
60 | # - asset: fonts/Schyler-Regular.ttf
61 | # - asset: fonts/Schyler-Italic.ttf
62 | # style: italic
63 | # - family: Trajan Pro
64 | # fonts:
65 | # - asset: fonts/TrajanPro.ttf
66 | # - asset: fonts/TrajanPro_Bold.ttf
67 | # weight: 700
68 | #
69 | # For details regarding fonts from package dependencies,
70 | # see https://flutter.dev/custom-fonts/#from-packages
71 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:agora_uikit_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) =>
22 | widget is Text && widget.data!.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/example/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/web/favicon.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | agora_uikit
27 |
28 |
29 |
30 |
33 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/example/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora_uikit",
3 | "short_name": "agora_uikit",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/example/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/example/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(agora_uikit LANGUAGES CXX)
3 |
4 | set(BINARY_NAME "agora_uikit")
5 |
6 | cmake_policy(SET CMP0063 NEW)
7 |
8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
9 |
10 | # Configure build options.
11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
12 | if(IS_MULTICONFIG)
13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
14 | CACHE STRING "" FORCE)
15 | else()
16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
17 | set(CMAKE_BUILD_TYPE "Debug" CACHE
18 | STRING "Flutter build mode" FORCE)
19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
20 | "Debug" "Profile" "Release")
21 | endif()
22 | endif()
23 |
24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
28 |
29 | # Use Unicode for all projects.
30 | add_definitions(-DUNICODE -D_UNICODE)
31 |
32 | # Compilation settings that should be applied to most targets.
33 | function(APPLY_STANDARD_SETTINGS TARGET)
34 | target_compile_features(${TARGET} PUBLIC cxx_std_17)
35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
36 | target_compile_options(${TARGET} PRIVATE /EHsc)
37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
39 | endfunction()
40 |
41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
42 |
43 | # Flutter library and tool build rules.
44 | add_subdirectory(${FLUTTER_MANAGED_DIR})
45 |
46 | # Application build
47 | add_subdirectory("runner")
48 |
49 | # Generated plugin build rules, which manage building the plugins and adding
50 | # them to the application.
51 | include(flutter/generated_plugins.cmake)
52 |
53 |
54 | # === Installation ===
55 | # Support files are copied into place next to the executable, so that it can
56 | # run in place. This is done instead of making a separate bundle (as on Linux)
57 | # so that building and running from within Visual Studio will work.
58 | set(BUILD_BUNDLE_DIR "$")
59 | # Make the "install" step default, as it's required to run.
60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
63 | endif()
64 |
65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
67 |
68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
69 | COMPONENT Runtime)
70 |
71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
72 | COMPONENT Runtime)
73 |
74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
75 | COMPONENT Runtime)
76 |
77 | if(PLUGIN_BUNDLED_LIBRARIES)
78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
80 | COMPONENT Runtime)
81 | endif()
82 |
83 | # Fully re-copy the assets directory on each build to avoid having stale files
84 | # from a previous install.
85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
86 | install(CODE "
87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
88 | " COMPONENT Runtime)
89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
91 |
92 | # Install the AOT library on non-Debug builds only.
93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
94 | CONFIGURATIONS Profile;Release
95 | COMPONENT Runtime)
96 |
--------------------------------------------------------------------------------
/example/windows/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 |
3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
4 |
5 | # Configuration provided via flutter tool.
6 | include(${EPHEMERAL_DIR}/generated_config.cmake)
7 |
8 | # TODO: Move the rest of this into files in ephemeral. See
9 | # https://github.com/flutter/flutter/issues/57146.
10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
11 |
12 | # === Flutter Library ===
13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
14 |
15 | # Published to parent scope for install step.
16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
20 |
21 | list(APPEND FLUTTER_LIBRARY_HEADERS
22 | "flutter_export.h"
23 | "flutter_windows.h"
24 | "flutter_messenger.h"
25 | "flutter_plugin_registrar.h"
26 | "flutter_texture_registrar.h"
27 | )
28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
29 | add_library(flutter INTERFACE)
30 | target_include_directories(flutter INTERFACE
31 | "${EPHEMERAL_DIR}"
32 | )
33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
34 | add_dependencies(flutter flutter_assemble)
35 |
36 | # === Wrapper ===
37 | list(APPEND CPP_WRAPPER_SOURCES_CORE
38 | "core_implementations.cc"
39 | "standard_codec.cc"
40 | )
41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
43 | "plugin_registrar.cc"
44 | )
45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
46 | list(APPEND CPP_WRAPPER_SOURCES_APP
47 | "flutter_engine.cc"
48 | "flutter_view_controller.cc"
49 | )
50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
51 |
52 | # Wrapper sources needed for a plugin.
53 | add_library(flutter_wrapper_plugin STATIC
54 | ${CPP_WRAPPER_SOURCES_CORE}
55 | ${CPP_WRAPPER_SOURCES_PLUGIN}
56 | )
57 | apply_standard_settings(flutter_wrapper_plugin)
58 | set_target_properties(flutter_wrapper_plugin PROPERTIES
59 | POSITION_INDEPENDENT_CODE ON)
60 | set_target_properties(flutter_wrapper_plugin PROPERTIES
61 | CXX_VISIBILITY_PRESET hidden)
62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
63 | target_include_directories(flutter_wrapper_plugin PUBLIC
64 | "${WRAPPER_ROOT}/include"
65 | )
66 | add_dependencies(flutter_wrapper_plugin flutter_assemble)
67 |
68 | # Wrapper sources needed for the runner.
69 | add_library(flutter_wrapper_app STATIC
70 | ${CPP_WRAPPER_SOURCES_CORE}
71 | ${CPP_WRAPPER_SOURCES_APP}
72 | )
73 | apply_standard_settings(flutter_wrapper_app)
74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter)
75 | target_include_directories(flutter_wrapper_app PUBLIC
76 | "${WRAPPER_ROOT}/include"
77 | )
78 | add_dependencies(flutter_wrapper_app flutter_assemble)
79 |
80 | # === Flutter tool backend ===
81 | # _phony_ is a non-existent file to force this command to run every time,
82 | # since currently there's no way to get a full input/output list from the
83 | # flutter tool.
84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
86 | add_custom_command(
87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
89 | ${CPP_WRAPPER_SOURCES_APP}
90 | ${PHONY_OUTPUT}
91 | COMMAND ${CMAKE_COMMAND} -E env
92 | ${FLUTTER_TOOL_ENVIRONMENT}
93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
94 | windows-x64 $
95 | VERBATIM
96 | )
97 | add_custom_target(flutter_assemble DEPENDS
98 | "${FLUTTER_LIBRARY}"
99 | ${FLUTTER_LIBRARY_HEADERS}
100 | ${CPP_WRAPPER_SOURCES_CORE}
101 | ${CPP_WRAPPER_SOURCES_PLUGIN}
102 | ${CPP_WRAPPER_SOURCES_APP}
103 | )
104 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | void RegisterPlugins(flutter::PluginRegistry* registry) {
14 | AgoraRtcEnginePluginRegisterWithRegistrar(
15 | registry->GetRegistrarForPlugin("AgoraRtcEnginePlugin"));
16 | IrisMethodChannelPluginCApiRegisterWithRegistrar(
17 | registry->GetRegistrarForPlugin("IrisMethodChannelPluginCApi"));
18 | PermissionHandlerWindowsPluginRegisterWithRegistrar(
19 | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
20 | }
21 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | agora_rtc_engine
7 | iris_method_channel
8 | permission_handler_windows
9 | )
10 |
11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
12 | )
13 |
14 | set(PLUGIN_BUNDLED_LIBRARIES)
15 |
16 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
21 | endforeach(plugin)
22 |
23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
26 | endforeach(ffi_plugin)
27 |
--------------------------------------------------------------------------------
/example/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(runner LANGUAGES CXX)
3 |
4 | add_executable(${BINARY_NAME} WIN32
5 | "flutter_window.cpp"
6 | "main.cpp"
7 | "run_loop.cpp"
8 | "utils.cpp"
9 | "win32_window.cpp"
10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
11 | "Runner.rc"
12 | "runner.exe.manifest"
13 | )
14 | apply_standard_settings(${BINARY_NAME})
15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
18 | add_dependencies(${BINARY_NAME} flutter_assemble)
19 |
--------------------------------------------------------------------------------
/example/windows/runner/Runner.rc:
--------------------------------------------------------------------------------
1 | // Microsoft Visual C++ generated resource script.
2 | //
3 | #pragma code_page(65001)
4 | #include "resource.h"
5 |
6 | #define APSTUDIO_READONLY_SYMBOLS
7 | /////////////////////////////////////////////////////////////////////////////
8 | //
9 | // Generated from the TEXTINCLUDE 2 resource.
10 | //
11 | #include "winres.h"
12 |
13 | /////////////////////////////////////////////////////////////////////////////
14 | #undef APSTUDIO_READONLY_SYMBOLS
15 |
16 | /////////////////////////////////////////////////////////////////////////////
17 | // English (United States) resources
18 |
19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
21 |
22 | #ifdef APSTUDIO_INVOKED
23 | /////////////////////////////////////////////////////////////////////////////
24 | //
25 | // TEXTINCLUDE
26 | //
27 |
28 | 1 TEXTINCLUDE
29 | BEGIN
30 | "resource.h\0"
31 | END
32 |
33 | 2 TEXTINCLUDE
34 | BEGIN
35 | "#include ""winres.h""\r\n"
36 | "\0"
37 | END
38 |
39 | 3 TEXTINCLUDE
40 | BEGIN
41 | "\r\n"
42 | "\0"
43 | END
44 |
45 | #endif // APSTUDIO_INVOKED
46 |
47 |
48 | /////////////////////////////////////////////////////////////////////////////
49 | //
50 | // Icon
51 | //
52 |
53 | // Icon with lowest ID value placed first to ensure application icon
54 | // remains consistent on all systems.
55 | IDI_APP_ICON ICON "resources\\app_icon.ico"
56 |
57 |
58 | /////////////////////////////////////////////////////////////////////////////
59 | //
60 | // Version
61 | //
62 |
63 | #ifdef FLUTTER_BUILD_NUMBER
64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
65 | #else
66 | #define VERSION_AS_NUMBER 1,0,0
67 | #endif
68 |
69 | #ifdef FLUTTER_BUILD_NAME
70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME
71 | #else
72 | #define VERSION_AS_STRING "1.0.0"
73 | #endif
74 |
75 | VS_VERSION_INFO VERSIONINFO
76 | FILEVERSION VERSION_AS_NUMBER
77 | PRODUCTVERSION VERSION_AS_NUMBER
78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
79 | #ifdef _DEBUG
80 | FILEFLAGS VS_FF_DEBUG
81 | #else
82 | FILEFLAGS 0x0L
83 | #endif
84 | FILEOS VOS__WINDOWS32
85 | FILETYPE VFT_APP
86 | FILESUBTYPE 0x0L
87 | BEGIN
88 | BLOCK "StringFileInfo"
89 | BEGIN
90 | BLOCK "040904e4"
91 | BEGIN
92 | VALUE "CompanyName", "io.agora" "\0"
93 | VALUE "FileDescription", "A new Flutter project." "\0"
94 | VALUE "FileVersion", VERSION_AS_STRING "\0"
95 | VALUE "InternalName", "agora_uikit" "\0"
96 | VALUE "LegalCopyright", "Copyright (C) 2021 io.agora. All rights reserved." "\0"
97 | VALUE "OriginalFilename", "agora_uikit.exe" "\0"
98 | VALUE "ProductName", "agora_uikit" "\0"
99 | VALUE "ProductVersion", VERSION_AS_STRING "\0"
100 | END
101 | END
102 | BLOCK "VarFileInfo"
103 | BEGIN
104 | VALUE "Translation", 0x409, 1252
105 | END
106 | END
107 |
108 | #endif // English (United States) resources
109 | /////////////////////////////////////////////////////////////////////////////
110 |
111 |
112 |
113 | #ifndef APSTUDIO_INVOKED
114 | /////////////////////////////////////////////////////////////////////////////
115 | //
116 | // Generated from the TEXTINCLUDE 3 resource.
117 | //
118 |
119 |
120 | /////////////////////////////////////////////////////////////////////////////
121 | #endif // not APSTUDIO_INVOKED
122 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.cpp:
--------------------------------------------------------------------------------
1 | #include "flutter_window.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | FlutterWindow::FlutterWindow(RunLoop* run_loop,
8 | const flutter::DartProject& project)
9 | : run_loop_(run_loop), project_(project) {}
10 |
11 | FlutterWindow::~FlutterWindow() {}
12 |
13 | bool FlutterWindow::OnCreate() {
14 | if (!Win32Window::OnCreate()) {
15 | return false;
16 | }
17 |
18 | RECT frame = GetClientArea();
19 |
20 | // The size here must match the window dimensions to avoid unnecessary surface
21 | // creation / destruction in the startup path.
22 | flutter_controller_ = std::make_unique(
23 | frame.right - frame.left, frame.bottom - frame.top, project_);
24 | // Ensure that basic setup of the controller was successful.
25 | if (!flutter_controller_->engine() || !flutter_controller_->view()) {
26 | return false;
27 | }
28 | RegisterPlugins(flutter_controller_->engine());
29 | run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
30 | SetChildContent(flutter_controller_->view()->GetNativeWindow());
31 | return true;
32 | }
33 |
34 | void FlutterWindow::OnDestroy() {
35 | if (flutter_controller_) {
36 | run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
37 | flutter_controller_ = nullptr;
38 | }
39 |
40 | Win32Window::OnDestroy();
41 | }
42 |
43 | LRESULT
44 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
45 | WPARAM const wparam,
46 | LPARAM const lparam) noexcept {
47 | // Give Flutter, including plugins, an opportunity to handle window messages.
48 | if (flutter_controller_) {
49 | std::optional result =
50 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
51 | lparam);
52 | if (result) {
53 | return *result;
54 | }
55 | }
56 |
57 | switch (message) {
58 | case WM_FONTCHANGE:
59 | flutter_controller_->engine()->ReloadSystemFonts();
60 | break;
61 | }
62 |
63 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
64 | }
65 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "run_loop.h"
10 | #include "win32_window.h"
11 |
12 | // A window that does nothing but host a Flutter view.
13 | class FlutterWindow : public Win32Window {
14 | public:
15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a
16 | // Flutter view running |project|.
17 | explicit FlutterWindow(RunLoop* run_loop,
18 | const flutter::DartProject& project);
19 | virtual ~FlutterWindow();
20 |
21 | protected:
22 | // Win32Window:
23 | bool OnCreate() override;
24 | void OnDestroy() override;
25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
26 | LPARAM const lparam) noexcept override;
27 |
28 | private:
29 | // The run loop driving events for this window.
30 | RunLoop* run_loop_;
31 |
32 | // The project to run.
33 | flutter::DartProject project_;
34 |
35 | // The Flutter instance hosted by this window.
36 | std::unique_ptr flutter_controller_;
37 | };
38 |
39 | #endif // RUNNER_FLUTTER_WINDOW_H_
40 |
--------------------------------------------------------------------------------
/example/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "run_loop.h"
7 | #include "utils.h"
8 |
9 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
10 | _In_ wchar_t *command_line, _In_ int show_command) {
11 | // Attach to console when present (e.g., 'flutter run') or create a
12 | // new console when running with a debugger.
13 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
14 | CreateAndAttachConsole();
15 | }
16 |
17 | // Initialize COM, so that it is available for use in the library and/or
18 | // plugins.
19 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
20 |
21 | RunLoop run_loop;
22 |
23 | flutter::DartProject project(L"data");
24 |
25 | std::vector command_line_arguments =
26 | GetCommandLineArguments();
27 |
28 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
29 |
30 | FlutterWindow window(&run_loop, project);
31 | Win32Window::Point origin(10, 10);
32 | Win32Window::Size size(1280, 720);
33 | if (!window.CreateAndShow(L"agora_uikit", origin, size)) {
34 | return EXIT_FAILURE;
35 | }
36 | window.SetQuitOnClose(true);
37 |
38 | run_loop.Run();
39 |
40 | ::CoUninitialize();
41 | return EXIT_SUCCESS;
42 | }
43 |
--------------------------------------------------------------------------------
/example/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/example/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/example/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/example/windows/runner/run_loop.cpp:
--------------------------------------------------------------------------------
1 | #include "run_loop.h"
2 |
3 | #include
4 |
5 | #include
6 |
7 | RunLoop::RunLoop() {}
8 |
9 | RunLoop::~RunLoop() {}
10 |
11 | void RunLoop::Run() {
12 | bool keep_running = true;
13 | TimePoint next_flutter_event_time = TimePoint::clock::now();
14 | while (keep_running) {
15 | std::chrono::nanoseconds wait_duration =
16 | std::max(std::chrono::nanoseconds(0),
17 | next_flutter_event_time - TimePoint::clock::now());
18 | ::MsgWaitForMultipleObjects(
19 | 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000),
20 | QS_ALLINPUT);
21 | bool processed_events = false;
22 | MSG message;
23 | // All pending Windows messages must be processed; MsgWaitForMultipleObjects
24 | // won't return again for items left in the queue after PeekMessage.
25 | while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
26 | processed_events = true;
27 | if (message.message == WM_QUIT) {
28 | keep_running = false;
29 | break;
30 | }
31 | ::TranslateMessage(&message);
32 | ::DispatchMessage(&message);
33 | // Allow Flutter to process messages each time a Windows message is
34 | // processed, to prevent starvation.
35 | next_flutter_event_time =
36 | std::min(next_flutter_event_time, ProcessFlutterMessages());
37 | }
38 | // If the PeekMessage loop didn't run, process Flutter messages.
39 | if (!processed_events) {
40 | next_flutter_event_time =
41 | std::min(next_flutter_event_time, ProcessFlutterMessages());
42 | }
43 | }
44 | }
45 |
46 | void RunLoop::RegisterFlutterInstance(
47 | flutter::FlutterEngine* flutter_instance) {
48 | flutter_instances_.insert(flutter_instance);
49 | }
50 |
51 | void RunLoop::UnregisterFlutterInstance(
52 | flutter::FlutterEngine* flutter_instance) {
53 | flutter_instances_.erase(flutter_instance);
54 | }
55 |
56 | RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
57 | TimePoint next_event_time = TimePoint::max();
58 | for (auto instance : flutter_instances_) {
59 | std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
60 | if (wait_duration != std::chrono::nanoseconds::max()) {
61 | next_event_time =
62 | std::min(next_event_time, TimePoint::clock::now() + wait_duration);
63 | }
64 | }
65 | return next_event_time;
66 | }
67 |
--------------------------------------------------------------------------------
/example/windows/runner/run_loop.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_RUN_LOOP_H_
2 | #define RUNNER_RUN_LOOP_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | // A runloop that will service events for Flutter instances as well
10 | // as native messages.
11 | class RunLoop {
12 | public:
13 | RunLoop();
14 | ~RunLoop();
15 |
16 | // Prevent copying
17 | RunLoop(RunLoop const&) = delete;
18 | RunLoop& operator=(RunLoop const&) = delete;
19 |
20 | // Runs the run loop until the application quits.
21 | void Run();
22 |
23 | // Registers the given Flutter instance for event servicing.
24 | void RegisterFlutterInstance(
25 | flutter::FlutterEngine* flutter_instance);
26 |
27 | // Unregisters the given Flutter instance from event servicing.
28 | void UnregisterFlutterInstance(
29 | flutter::FlutterEngine* flutter_instance);
30 |
31 | private:
32 | using TimePoint = std::chrono::steady_clock::time_point;
33 |
34 | // Processes all currently pending messages for registered Flutter instances.
35 | TimePoint ProcessFlutterMessages();
36 |
37 | std::set flutter_instances_;
38 | };
39 |
40 | #endif // RUNNER_RUN_LOOP_H_
41 |
--------------------------------------------------------------------------------
/example/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | void CreateAndAttachConsole() {
11 | if (::AllocConsole()) {
12 | FILE *unused;
13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
14 | _dup2(_fileno(stdout), 1);
15 | }
16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
17 | _dup2(_fileno(stdout), 2);
18 | }
19 | std::ios::sync_with_stdio();
20 | FlutterDesktopResyncOutputStreams();
21 | }
22 | }
23 |
24 | std::vector GetCommandLineArguments() {
25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
26 | int argc;
27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
28 | if (argv == nullptr) {
29 | return std::vector();
30 | }
31 |
32 | std::vector command_line_arguments;
33 |
34 | // Skip the first argument as it's the binary name.
35 | for (int i = 1; i < argc; i++) {
36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
37 | }
38 |
39 | ::LocalFree(argv);
40 |
41 | return command_line_arguments;
42 | }
43 |
44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) {
45 | if (utf16_string == nullptr) {
46 | return std::string();
47 | }
48 | int target_length = ::WideCharToMultiByte(
49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
50 | -1, nullptr, 0, nullptr, nullptr);
51 | if (target_length == 0) {
52 | return std::string();
53 | }
54 | std::string utf8_string;
55 | utf8_string.resize(target_length);
56 | int converted_length = ::WideCharToMultiByte(
57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
58 | -1, utf8_string.data(),
59 | target_length, nullptr, nullptr);
60 | if (converted_length == 0) {
61 | return std::string();
62 | }
63 | return utf8_string;
64 | }
65 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_WIN32_WINDOW_H_
2 | #define RUNNER_WIN32_WINDOW_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be
11 | // inherited from by classes that wish to specialize with custom
12 | // rendering and input handling
13 | class Win32Window {
14 | public:
15 | struct Point {
16 | unsigned int x;
17 | unsigned int y;
18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {}
19 | };
20 |
21 | struct Size {
22 | unsigned int width;
23 | unsigned int height;
24 | Size(unsigned int width, unsigned int height)
25 | : width(width), height(height) {}
26 | };
27 |
28 | Win32Window();
29 | virtual ~Win32Window();
30 |
31 | // Creates and shows a win32 window with |title| and position and size using
32 | // |origin| and |size|. New windows are created on the default monitor. Window
33 | // sizes are specified to the OS in physical pixels, hence to ensure a
34 | // consistent size to will treat the width height passed in to this function
35 | // as logical pixels and scale to appropriate for the default monitor. Returns
36 | // true if the window was created successfully.
37 | bool CreateAndShow(const std::wstring& title,
38 | const Point& origin,
39 | const Size& size);
40 |
41 | // Release OS resources associated with window.
42 | void Destroy();
43 |
44 | // Inserts |content| into the window tree.
45 | void SetChildContent(HWND content);
46 |
47 | // Returns the backing Window handle to enable clients to set icon and other
48 | // window properties. Returns nullptr if the window has been destroyed.
49 | HWND GetHandle();
50 |
51 | // If true, closing this window will quit the application.
52 | void SetQuitOnClose(bool quit_on_close);
53 |
54 | // Return a RECT representing the bounds of the current client area.
55 | RECT GetClientArea();
56 |
57 | protected:
58 | // Processes and route salient window messages for mouse handling,
59 | // size change and DPI. Delegates handling of these to member overloads that
60 | // inheriting classes can handle.
61 | virtual LRESULT MessageHandler(HWND window,
62 | UINT const message,
63 | WPARAM const wparam,
64 | LPARAM const lparam) noexcept;
65 |
66 | // Called when CreateAndShow is called, allowing subclass window-related
67 | // setup. Subclasses should return false if setup fails.
68 | virtual bool OnCreate();
69 |
70 | // Called when Destroy is called.
71 | virtual void OnDestroy();
72 |
73 | private:
74 | friend class WindowClassRegistrar;
75 |
76 | // OS callback called by message pump. Handles the WM_NCCREATE message which
77 | // is passed when the non-client area is being created and enables automatic
78 | // non-client DPI scaling so that the non-client area automatically
79 | // responsponds to changes in DPI. All other messages are handled by
80 | // MessageHandler.
81 | static LRESULT CALLBACK WndProc(HWND const window,
82 | UINT const message,
83 | WPARAM const wparam,
84 | LPARAM const lparam) noexcept;
85 |
86 | // Retrieves a class instance pointer for |window|
87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept;
88 |
89 | bool quit_on_close_ = false;
90 |
91 | // window handle for top level window.
92 | HWND window_handle_ = nullptr;
93 |
94 | // window handle for hosted content.
95 | HWND child_content_ = nullptr;
96 | };
97 |
98 | #endif // RUNNER_WIN32_WINDOW_H_
99 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/ephemeral/
38 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-Flutter/f45b3a4bc29ea1c2c5124c2dc5a6ee83b87691eb/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/AgoraUikitPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface AgoraUikitPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/AgoraUikitPlugin.m:
--------------------------------------------------------------------------------
1 | #import "AgoraUikitPlugin.h"
2 | #if __has_include()
3 | #import
4 | #else
5 | // Support project import fallback if the generated compatibility header
6 | // is not copied when this plugin is created as a library.
7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
8 | #import "agora_uikit-Swift.h"
9 | #endif
10 |
11 | @implementation AgoraUikitPlugin
12 | + (void)registerWithRegistrar:(NSObject*)registrar {
13 | [SwiftAgoraUikitPlugin registerWithRegistrar:registrar];
14 | }
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/Classes/SwiftAgoraUikitPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | public class SwiftAgoraUikitPlugin: NSObject, FlutterPlugin {
5 | public static func register(with registrar: FlutterPluginRegistrar) {
6 | let channel = FlutterMethodChannel(name: "agora_uikit", binaryMessenger: registrar.messenger())
7 | let instance = SwiftAgoraUikitPlugin()
8 | registrar.addMethodCallDelegate(instance, channel: channel)
9 | }
10 |
11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12 | result("iOS " + UIDevice.current.systemVersion)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ios/agora_uikit.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint agora_flutter_uikit.podspec` to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'agora_uikit'
7 | s.version = '0.0.1'
8 | s.summary = 'A new flutter plugin project.'
9 | s.description = <<-DESC
10 | A new flutter plugin project.
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.dependency 'Flutter'
18 | s.platform = :ios, '8.0'
19 |
20 | # Flutter.framework does not contain a i386 slice.
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/lib/agora_uikit.dart:
--------------------------------------------------------------------------------
1 | export 'package:agora_rtc_engine/agora_rtc_engine.dart'
2 | show
3 | VideoEncoderConfiguration,
4 | ChannelProfileType,
5 | ClientRoleType,
6 | StreamFallbackOptions,
7 | AudioProfileType,
8 | AudioScenarioType,
9 | BeautyOptions,
10 | LighteningContrastLevel,
11 | ErrorCodeType,
12 | RtcStats,
13 | UserOfflineReasonType,
14 | RemoteVideoState,
15 | RemoteVideoStateReason,
16 | RemoteAudioState,
17 | RemoteAudioStateReason,
18 | LocalAudioStreamState,
19 | LocalAudioStreamReason,
20 | AudioVolumeInfo,
21 | LocalVideoStreamState,
22 | LocalVideoStreamReason,
23 | AreaCode,
24 | UserInfo,
25 | ConnectionStateType,
26 | ConnectionChangedReasonType,
27 | NetworkType,
28 | Rectangle,
29 | LastmileProbeResult,
30 | LocalVideoStats,
31 | LocalAudioStats,
32 | RemoteVideoStats,
33 | RemoteAudioStats,
34 | AudioMixingStateType,
35 | AudioMixingReasonType,
36 | RtmpStreamPublishState,
37 | RtmpStreamPublishReason,
38 | InjectStreamStatus,
39 | ChannelMediaRelayState,
40 | ChannelMediaRelayError,
41 | StreamPublishState,
42 | StreamSubscribeState,
43 | RtmpStreamingEvent,
44 | UploadErrorReason;
45 | export 'package:agora_rtm/agora_rtm.dart'
46 | show
47 | AgoraRtmChannelException,
48 | AgoraRtmClientException,
49 | RtmMessage,
50 | RtmAttribute,
51 | RtmChannelAttribute,
52 | RtmChannelMember,
53 | RtmChannelMemberCount,
54 | RtmAreaCode,
55 | RtmCloudProxyType,
56 | RtmConnectionChangeReason,
57 | RtmConnectionState,
58 | RtmLocalInvitationState,
59 | RtmServiceContext,
60 | RtmLogFilter,
61 | RtmMessageType,
62 | RtmPeerOnlineState,
63 | RtmPeerSubscriptionOption,
64 | RtmRemoteInvitationState;
65 | export 'package:permission_handler/permission_handler.dart';
66 |
67 | export 'models/agora_channel_data.dart' show AgoraChannelData;
68 | export 'models/agora_connection_data.dart' show AgoraConnectionData;
69 | export 'models/agora_rtc_event_handlers.dart' show AgoraRtcEventHandlers;
70 | export 'models/agora_rtm_channel_event_handler.dart'
71 | show AgoraRtmChannelEventHandler;
72 | export 'models/agora_rtm_client_event_handler.dart'
73 | show AgoraRtmClientEventHandler;
74 | export 'src/agora_client.dart' show AgoraClient;
75 | export 'src/buttons/buttons.dart' show AgoraVideoButtons;
76 | export 'src/enums.dart';
77 | export 'src/layout/layout.dart' show AgoraVideoViewer;
78 |
--------------------------------------------------------------------------------
/lib/controllers/rtc_token_handler.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:developer';
3 | import 'dart:io';
4 |
5 | import 'package:agora_uikit/controllers/session_controller.dart';
6 | import 'package:agora_uikit/src/enums.dart';
7 | import 'package:http/http.dart' as http;
8 |
9 | /// Function to get the RTC token from the server. Follow t
10 | Future getToken({
11 | String? tokenUrl,
12 | String? channelName,
13 | int? uid = 0,
14 | required SessionController sessionController,
15 | }) async {
16 | final response = await http
17 | .get(Uri.parse('$tokenUrl/rtc/$channelName/publisher/uid/$uid'));
18 | if (response.statusCode == HttpStatus.ok) {
19 | sessionController.value = sessionController.value
20 | .copyWith(generatedToken: jsonDecode(response.body)['rtcToken']);
21 | } else {
22 | log("${response.reasonPhrase}",
23 | level: Level.error.value, name: "AgoraVideoUIKit");
24 | log("Failed to generate the token : ${response.statusCode}",
25 | level: Level.error.value, name: "AgoraVideoUIKit");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/controllers/rtm_channel_event_handler.dart:
--------------------------------------------------------------------------------
1 | import 'dart:developer';
2 |
3 | import 'package:agora_rtm/agora_rtm.dart';
4 | import 'package:agora_uikit/controllers/rtm_controller.dart';
5 | import 'package:agora_uikit/controllers/rtm_controller_helper.dart';
6 | import 'package:agora_uikit/controllers/session_controller.dart';
7 | import 'package:agora_uikit/models/agora_rtm_channel_event_handler.dart';
8 | import 'package:agora_uikit/models/rtm_message.dart';
9 | import 'package:agora_uikit/src/enums.dart';
10 |
11 | Future rtmChannelEventHandler({
12 | required AgoraRtmChannel channel,
13 | required AgoraRtmChannelEventHandler agoraRtmChannelEventHandler,
14 | required SessionController sessionController,
15 | }) async {
16 | const String tag = "AgoraVideoUIKit";
17 | channel.onMessageReceived = (RtmMessage message, RtmChannelMember member) {
18 | agoraRtmChannelEventHandler.onMessageReceived?.call(
19 | message,
20 | member,
21 | );
22 |
23 | log(
24 | 'Channel msg : ${message.text}, from : ${member.userId}',
25 | level: Level.info.value,
26 | name: tag,
27 | );
28 | Message msg = Message(text: message.text);
29 | messageReceived(
30 | messageType: "UserData",
31 | message: msg.toJson(),
32 | sessionController: sessionController,
33 | );
34 | };
35 |
36 | channel.onMemberJoined = (RtmChannelMember member) {
37 | agoraRtmChannelEventHandler.onMemberJoined?.call(member);
38 |
39 | log(
40 | 'Member joined : ${member.userId}',
41 | level: Level.info.value,
42 | name: tag,
43 | );
44 | sendUserData(
45 | toChannel: false,
46 | username: sessionController.value.connectionData!.username!,
47 | peerRtmId: member.userId,
48 | sessionController: sessionController,
49 | );
50 | };
51 |
52 | channel.onMemberLeft = (RtmChannelMember member) {
53 | agoraRtmChannelEventHandler.onMemberLeft?.call(member);
54 |
55 | log(
56 | 'Member left : ${member.userId}',
57 | level: Level.info.value,
58 | name: tag,
59 | );
60 |
61 | if (sessionController.value.userRtmMap!.containsKey(member.userId)) {
62 | removeFromUserRtmMap(
63 | rtmId: member.userId,
64 | sessionController: sessionController,
65 | );
66 | }
67 |
68 | if (sessionController.value.uidToUserIdMap!.containsValue(member.userId)) {
69 | for (var i = 0; i < sessionController.value.uidToUserIdMap!.length; i++) {
70 | int rtcId = sessionController.value.uidToUserIdMap!.keys.elementAt(i);
71 | removeFromUidToUserMap(
72 | rtcId: rtcId,
73 | sessionController: sessionController,
74 | );
75 | }
76 | }
77 | };
78 |
79 | channel.onMemberCountUpdated = (int count) {
80 | agoraRtmChannelEventHandler.onMemberCountUpdated?.call(count);
81 |
82 | log(
83 | 'Member count updated : $count',
84 | level: Level.info.value,
85 | name: tag,
86 | );
87 | };
88 |
89 | channel.onAttributesUpdated = (List attributes) {
90 | agoraRtmChannelEventHandler.onAttributesUpdated?.call(attributes);
91 |
92 | log(
93 | 'Channel attributes updated : $attributes',
94 | level: Level.info.value,
95 | name: tag,
96 | );
97 | };
98 |
99 | channel.onError = (dynamic error) {
100 | agoraRtmChannelEventHandler.onError?.call(error);
101 |
102 | log(
103 | 'RTM Channel error: $error',
104 | level: Level.info.value,
105 | name: tag,
106 | );
107 | };
108 | }
109 |
--------------------------------------------------------------------------------
/lib/controllers/rtm_client_event_handler.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:developer';
3 |
4 | import 'package:agora_rtm/agora_rtm.dart';
5 | import 'package:agora_uikit/controllers/rtm_controller_helper.dart';
6 | import 'package:agora_uikit/controllers/rtm_token_handler.dart';
7 | import 'package:agora_uikit/controllers/session_controller.dart';
8 | import 'package:agora_uikit/models/agora_rtm_client_event_handler.dart';
9 | import 'package:agora_uikit/models/rtm_message.dart';
10 | import 'package:agora_uikit/src/enums.dart';
11 |
12 | Future rtmClientEventHandler({
13 | required AgoraRtmClient agoraRtmClient,
14 | required AgoraRtmClientEventHandler agoraRtmClientEventHandler,
15 | required SessionController sessionController,
16 | }) async {
17 | const String tag = "AgoraVideoUIKit";
18 |
19 | agoraRtmClient.onMessageReceived = (RtmMessage message, String peerId) {
20 | agoraRtmClientEventHandler.onMessageReceived?.call(message, peerId);
21 | Message msg = Message(text: message.text);
22 | String? messageType;
23 |
24 | final body = json.decode(message.text);
25 | messageType = body['messageType'];
26 |
27 | messageReceived(
28 | messageType: messageType!,
29 | message: msg.toJson(),
30 | sessionController: sessionController,
31 | );
32 | };
33 |
34 | agoraRtmClient.onConnectionStateChanged2 = (state, reason) {
35 | agoraRtmClientEventHandler.onConnectionStateChanged2?.call(state, reason);
36 |
37 | log(
38 | 'Connection state changed : ${state.toString()}, reason : ${reason.toString()}',
39 | level: Level.info.value,
40 | name: tag,
41 | );
42 | if (state == RtmConnectionState.aborted) {
43 | agoraRtmClient.logout();
44 | }
45 | };
46 |
47 | agoraRtmClient.onError = (error) {
48 | agoraRtmClientEventHandler.onError?.call(error);
49 |
50 | log(
51 | 'Error Occurred while initializing the RTM client: ${error.hashCode}',
52 | level: Level.error.value,
53 | name: tag,
54 | );
55 | };
56 |
57 | agoraRtmClient.onTokenExpired = () {
58 | agoraRtmClientEventHandler.onTokenExpired?.call();
59 |
60 | getRtmToken(
61 | tokenUrl: sessionController.value.connectionData!.tokenUrl,
62 | sessionController: sessionController,
63 | );
64 | };
65 |
66 | agoraRtmClient.onPeersOnlineStatusChanged = (peersStatus) {
67 | agoraRtmClientEventHandler.onPeersOnlineStatusChanged?.call(peersStatus);
68 | };
69 |
70 | agoraRtmClient.onTokenPrivilegeWillExpire = () {
71 | agoraRtmClientEventHandler.onTokenPrivilegeWillExpire?.call();
72 | };
73 | }
74 |
--------------------------------------------------------------------------------
/lib/controllers/rtm_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:developer';
3 |
4 | import 'package:agora_rtm/agora_rtm.dart';
5 | import 'package:agora_uikit/controllers/rtm_channel_event_handler.dart';
6 | import 'package:agora_uikit/controllers/session_controller.dart';
7 | import 'package:agora_uikit/models/agora_rtm_channel_event_handler.dart';
8 | import 'package:agora_uikit/models/agora_rtm_mute_request.dart';
9 | import 'package:agora_uikit/models/rtm_message.dart';
10 | import 'package:agora_uikit/src/enums.dart';
11 |
12 | /// Function to join the RTM channel and send the user data to everyone inside that channel.
13 | Future rtmMethods(
14 | {required AgoraRtmChannelEventHandler agoraRtmChannelEventHandler,
15 | required SessionController sessionController}) async {
16 | await _loginToRtm(sessionController);
17 | await _joinRtmChannel(
18 | agoraRtmChannelEventHandler,
19 | sessionController,
20 | );
21 | await sendUserData(
22 | toChannel: true,
23 | username: sessionController.value.connectionData!.username!,
24 | sessionController: sessionController,
25 | );
26 | }
27 |
28 | Future _loginToRtm(SessionController sessionController) async {
29 | if (!sessionController.value.isLoggedIn) {
30 | try {
31 | await sessionController.value.agoraRtmClient?.login(
32 | sessionController.value.connectionData!.tempRtmToken ??
33 | sessionController.value.generatedRtmToken,
34 | sessionController.value.generatedRtmId!,
35 | );
36 | sessionController.value =
37 | sessionController.value.copyWith(isLoggedIn: true);
38 | log(
39 | 'Username : ${sessionController.value.connectionData!.username} and rtmId : ${sessionController.value.generatedRtmId} logged in',
40 | level: Level.info.value,
41 | );
42 | } catch (e) {
43 | log(
44 | 'Error occurred while trying to login. ${e.toString()}',
45 | level: Level.error.value,
46 | );
47 | }
48 | }
49 | }
50 |
51 | Future _createChannel({
52 | required String rtmChannelName,
53 | required AgoraRtmChannelEventHandler agoraRtmChannelEventHandler,
54 | required SessionController sessionController,
55 | }) async {
56 | AgoraRtmChannel? channel = await sessionController.value.agoraRtmClient
57 | ?.createChannel(rtmChannelName);
58 |
59 | if (channel != null) {
60 | await rtmChannelEventHandler(
61 | channel: channel,
62 | agoraRtmChannelEventHandler: agoraRtmChannelEventHandler,
63 | sessionController: sessionController,
64 | );
65 | }
66 | return channel;
67 | }
68 |
69 | Future _joinRtmChannel(
70 | AgoraRtmChannelEventHandler agoraRtmChannelEventHandler,
71 | SessionController sessionController) async {
72 | if (!sessionController.value.isInChannel) {
73 | try {
74 | sessionController.value = sessionController.value.copyWith(
75 | agoraRtmChannel: await _createChannel(
76 | rtmChannelName:
77 | sessionController.value.connectionData?.rtmChannelName ??
78 | sessionController.value.connectionData!.channelName,
79 | agoraRtmChannelEventHandler: agoraRtmChannelEventHandler,
80 | sessionController: sessionController,
81 | ),
82 | );
83 | await sessionController.value.agoraRtmChannel?.join();
84 | sessionController.value =
85 | sessionController.value.copyWith(isInChannel: true);
86 | } catch (e) {
87 | log('RTM Join channel error : ${e.toString()}', level: Level.error.value);
88 | }
89 | }
90 | }
91 |
92 | Future sendUserData({
93 | required bool toChannel,
94 | required String username,
95 | String? peerRtmId,
96 | required SessionController sessionController,
97 | }) async {
98 | int ts = DateTime.now().millisecondsSinceEpoch;
99 |
100 | var userData = UserData(
101 | rtmId: sessionController.value.generatedRtmId!,
102 | rtcId: sessionController.value.localUid,
103 | username: username,
104 | role: sessionController.value.clientRoleType.index,
105 | );
106 |
107 | var json = jsonEncode(userData);
108 |
109 | Message message = Message(text: json, ts: ts, offline: false);
110 | RtmMessage msg = RtmMessage.fromText(message.text);
111 |
112 | if (sessionController.value.agoraRtmChannel != null && toChannel) {
113 | await sessionController.value.agoraRtmChannel?.sendMessage2(msg);
114 | log('User data sent to channel', level: Level.info.value);
115 | } else if (sessionController.value.agoraRtmClient != null &&
116 | !toChannel &&
117 | peerRtmId != null) {
118 | await sessionController.value.agoraRtmClient
119 | ?.sendMessageToPeer2(peerRtmId, msg);
120 | log('User data sent to peer', level: Level.info.value);
121 | } else {
122 | log("No user in the channel", level: Level.warning.value);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/lib/controllers/rtm_controller_helper.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:agora_uikit/controllers/session_controller.dart';
4 | import 'package:agora_uikit/models/agora_rtm_mute_request.dart';
5 | import 'package:agora_uikit/src/enums.dart';
6 |
7 | void messageReceived({
8 | required String messageType,
9 | required Map message,
10 | required SessionController sessionController,
11 | }) {
12 | switch (messageType) {
13 | case "UserData":
14 | message.forEach((key, val) {
15 | if (key == "text") {
16 | var userData = UserData.fromJson(jsonDecode(val.toString()));
17 | String rtmId = userData.rtmId;
18 | int rtcId = userData.rtcId;
19 | _addToUidUserMap(
20 | rtcId: rtcId,
21 | rtmId: rtmId,
22 | sessionController: sessionController,
23 | );
24 | _addToUserRtmMap(
25 | rtmId: rtmId,
26 | message: message,
27 | sessionController: sessionController,
28 | );
29 | }
30 | });
31 | break;
32 | case "MuteRequest":
33 | bool? muted;
34 | int? deviceId;
35 | message.forEach((key, val) {
36 | if (key == "text") {
37 | var muteRequest = MuteRequest.fromJson(jsonDecode(val.toString()));
38 | muted = muteRequest.mute;
39 | deviceId = muteRequest.device;
40 | }
41 | });
42 | if (deviceId == 0) {
43 | sessionController.value = sessionController.value.copyWith(
44 | displaySnackbar: true,
45 | cameraRequest: muted! ? CameraState.enabled : CameraState.disabled,
46 | showMicMessage: false,
47 | showCameraMessage: true,
48 | );
49 | } else if (deviceId == 1) {
50 | sessionController.value = sessionController.value.copyWith(
51 | displaySnackbar: true,
52 | muteRequest: muted! ? MicState.unmuted : MicState.muted,
53 | showCameraMessage: false,
54 | showMicMessage: true,
55 | );
56 | }
57 |
58 | Future.delayed(Duration(seconds: 10), () {
59 | sessionController.value = sessionController.value.copyWith(
60 | displaySnackbar: false,
61 | showCameraMessage: false,
62 | );
63 | });
64 | break;
65 | default:
66 | }
67 | }
68 |
69 | void _addToUidUserMap(
70 | {required int rtcId,
71 | required String rtmId,
72 | required SessionController sessionController}) {
73 | Map tempMap = {};
74 | tempMap.addAll(sessionController.value.uidToUserIdMap ?? {});
75 | if (rtcId != 0) {
76 | tempMap.putIfAbsent(rtcId, () => rtmId);
77 | }
78 | sessionController.value =
79 | sessionController.value.copyWith(uidToUserIdMap: tempMap);
80 | }
81 |
82 | void _addToUserRtmMap({
83 | required String rtmId,
84 | required Map message,
85 | required SessionController sessionController,
86 | }) {
87 | Map> tempMap = {};
88 | tempMap.addAll(sessionController.value.userRtmMap ?? {});
89 | tempMap.putIfAbsent(rtmId, () => message);
90 | sessionController.value =
91 | sessionController.value.copyWith(userRtmMap: tempMap);
92 | }
93 |
94 | void removeFromUidToUserMap({
95 | required int rtcId,
96 | required SessionController sessionController,
97 | }) {
98 | Map tempMap = {};
99 | tempMap = sessionController.value.uidToUserIdMap!;
100 | for (int i = 0; i < tempMap.length; i++) {
101 | if (tempMap.keys.elementAt(i) == rtcId) {
102 | tempMap.remove(rtcId);
103 | }
104 | }
105 | sessionController.value =
106 | sessionController.value.copyWith(uidToUserIdMap: tempMap);
107 | }
108 |
109 | void removeFromUserRtmMap({
110 | required String rtmId,
111 | required SessionController sessionController,
112 | }) {
113 | Map> tempMap = {};
114 | tempMap = sessionController.value.userRtmMap!;
115 | for (int i = 0; i < tempMap.length; i++) {
116 | if (tempMap.keys.elementAt(i) == rtmId) {
117 | tempMap.remove(rtmId);
118 | }
119 | }
120 | sessionController.value =
121 | sessionController.value.copyWith(userRtmMap: tempMap);
122 | }
123 |
--------------------------------------------------------------------------------
/lib/controllers/rtm_mute_request.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:developer';
3 |
4 | import 'package:agora_rtm/agora_rtm.dart';
5 | import 'package:agora_uikit/controllers/session_controller.dart';
6 | import 'package:agora_uikit/models/agora_rtm_mute_request.dart';
7 | import 'package:agora_uikit/models/rtm_message.dart';
8 | import 'package:agora_uikit/src/enums.dart';
9 |
10 | void hostControl({
11 | required int index,
12 | required bool mute,
13 | required SessionController sessionController,
14 | required Device device,
15 | }) {
16 | String? peerId;
17 |
18 | var muteRequest = MuteRequest(
19 | rtcId: sessionController.value.users[index].uid,
20 | mute: mute,
21 | device: device.index,
22 | isForceful: false,
23 | );
24 |
25 | var json = jsonEncode(muteRequest);
26 | Message message = Message(text: json);
27 | RtmMessage msg = RtmMessage.fromText(message.text);
28 | sessionController.value.uidToUserIdMap!.forEach((key, val) {
29 | if (key == sessionController.value.users[index].uid) {
30 | peerId = val;
31 | if (sessionController.value.isLoggedIn) {
32 | sessionController.value.agoraRtmClient
33 | ?.sendMessageToPeer2(peerId!, msg);
34 | } else {
35 | log("User not logged in", level: Level.warning.value);
36 | }
37 | } else {
38 | log("Peer RTM ID not found", level: Level.warning.value);
39 | }
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/lib/controllers/rtm_token_handler.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:developer';
3 | import 'dart:io';
4 |
5 | import 'package:agora_uikit/controllers/session_controller.dart';
6 | import 'package:agora_uikit/src/enums.dart';
7 | import 'package:http/http.dart' as http;
8 |
9 | Future getRtmToken({
10 | String? tokenUrl,
11 | required SessionController sessionController,
12 | }) async {
13 | final String url = "$tokenUrl/rtm/${sessionController.value.generatedRtmId}";
14 | final rtmResponse = await http.get(Uri.parse(url));
15 | if (rtmResponse.statusCode == HttpStatus.ok) {
16 | sessionController.value = sessionController.value.copyWith(
17 | generatedRtmToken: jsonDecode(rtmResponse.body)['rtmToken'],
18 | );
19 | } else {
20 | log("${rtmResponse.reasonPhrase}", level: Level.error.value);
21 | log('Failed to generate the rtm token : ${rtmResponse.statusCode}',
22 | level: Level.error.value);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/models/agora_connection_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_rtc_engine/agora_rtc_engine.dart';
2 |
3 | class AgoraConnectionData {
4 | /// The App ID issued to you by Agora. See [How to get the App ID](). Only users in apps with the same App ID can join the same channel and communicate with each other.
5 | final String appId;
6 |
7 | /// Specifies the channel to join
8 | final String channelName;
9 |
10 | final String? rtmChannelName;
11 |
12 | /// (Optional) The RTC user ID. A 32-bit unsigned integer with a value ranging from 1 to (232-1). This parameter must be unique. If uid is not assigned (or set as 0), the SDK assigns a uid and reports it in the onJoinChannelSuccess callback.
13 | final int? uid;
14 |
15 | /// (Optional) The RTM user ID. A String value. If you don't provide a rtmUid, the UIKit automatically assigns a rtmUid based on timestamp.
16 | final String? rtmUid;
17 |
18 | /// (Optional) If you want to enable RTM to your application (for features like host control) make sure that you pass in a username to AgoraConnectionData.
19 | final String? username;
20 |
21 | /// (Optional) Link to the deployed token server. The UIKit automatically generates the token after a fixed interval. Have a look at this guide to learn how to set up your [token server](https://github.com/AgoraIO-Community/agora-token-service)
22 | final String? tokenUrl;
23 |
24 | /// (Optional) Link to the deployed cloud recording server. Have a look at this guide to learn how to set up your [cloud recording server](https://github.com/AgoraIO-Community/Cloud-Recording-Golang)
25 | final String? cloudRecordingUrl;
26 |
27 | /// (Optional) RTC Token value generated from the Agora dashboard.
28 | final String? tempToken;
29 |
30 | /// (Optional) RTM Token value generated from the Agora dashboard.
31 | final String? tempRtmToken;
32 |
33 | /// (Optional) The region for connection. This advanced feature applies to scenarios that have regional restrictions.
34 | final List areaCode;
35 |
36 | /// Whether you want to enable RTM or not. Enabling RTM adds the host controls which helps you to request a remote user to mute/unmute their video/mic. Host Controls are enabled by default, set this to `false` to disable it.
37 | final bool rtmEnabled;
38 |
39 | final int? screenSharingUid;
40 |
41 | final bool screenSharingEnabled;
42 |
43 | final Function? cloudRecordingCallback;
44 |
45 | AgoraConnectionData({
46 | required this.appId,
47 | required this.channelName,
48 | this.rtmChannelName,
49 | this.uid = 0,
50 | this.rtmUid,
51 | this.username,
52 | this.tokenUrl,
53 | this.cloudRecordingUrl,
54 | this.tempToken,
55 | this.tempRtmToken,
56 | this.areaCode = const [AreaCode.areaCodeGlob],
57 | this.rtmEnabled = true,
58 | this.screenSharingUid = 1000,
59 | this.screenSharingEnabled = true,
60 | this.cloudRecordingCallback,
61 | });
62 |
63 | AgoraConnectionData copyWith({
64 | String? appId,
65 | String? channelName,
66 | String? rtmChannelName,
67 | int? uid,
68 | String? rtmUid,
69 | String? username,
70 | String? tempToken,
71 | String? tempRtmToken,
72 | String? tokenUrl,
73 | String? cloudRecordingUrl,
74 | List? areaCode,
75 | bool? rtmEnabled,
76 | int? screenSharingUid,
77 | bool? screenSharingEnabled,
78 | Function? cloudRecordingCallback,
79 | }) {
80 | return AgoraConnectionData(
81 | appId: appId ?? this.appId,
82 | channelName: channelName ?? this.channelName,
83 | rtmChannelName: rtmChannelName ?? this.rtmChannelName,
84 | uid: uid ?? this.uid,
85 | rtmUid: rtmUid ?? this.rtmUid,
86 | username: username ?? this.username,
87 | tempToken: tempToken ?? this.tempToken,
88 | tempRtmToken: tempRtmToken ?? this.tempRtmToken,
89 | tokenUrl: tokenUrl ?? this.tokenUrl,
90 | cloudRecordingUrl: cloudRecordingUrl ?? this.cloudRecordingUrl,
91 | areaCode: areaCode ?? this.areaCode,
92 | rtmEnabled: rtmEnabled ?? this.rtmEnabled,
93 | screenSharingUid: screenSharingUid ?? this.screenSharingUid,
94 | screenSharingEnabled: screenSharingEnabled ?? this.screenSharingEnabled,
95 | cloudRecordingCallback:
96 | cloudRecordingCallback ?? this.cloudRecordingCallback,
97 | );
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/lib/models/agora_rtm_channel_event_handler.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_rtm/agora_rtm.dart';
2 |
3 | class AgoraRtmChannelEventHandler {
4 | /// Occurs when you receive error events.
5 | final Function(dynamic error)? onError;
6 |
7 | /// Occurs when receiving a channel message.
8 | final Function(RtmMessage message, RtmChannelMember fromMember)?
9 | onMessageReceived;
10 |
11 | /// Occurs when a user joins the channel.
12 | final Function(RtmChannelMember member)? onMemberJoined;
13 |
14 | /// Occurs when a channel member leaves the channel.
15 | final Function(RtmChannelMember member)? onMemberLeft;
16 |
17 | /// Occurs when channel attribute updated.
18 | final Function(List attributes)? onAttributesUpdated;
19 |
20 | /// Occurs when channel member count updated.
21 | final Function(int count)? onMemberCountUpdated;
22 |
23 | const AgoraRtmChannelEventHandler({
24 | this.onError,
25 | this.onMessageReceived,
26 | this.onMemberJoined,
27 | this.onMemberLeft,
28 | this.onAttributesUpdated,
29 | this.onMemberCountUpdated,
30 | });
31 | }
32 |
--------------------------------------------------------------------------------
/lib/models/agora_rtm_client_event_handler.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_rtm/agora_rtm.dart';
2 |
3 | /// Handles all the callbacks or event handler for the Agora RTM client class
4 | class AgoraRtmClientEventHandler {
5 | /// Occurs when you receive error events.
6 | final void Function(dynamic error)? onError;
7 |
8 | /// Occurs when the connection state between the SDK and the Agora RTM system changes.
9 | final void Function(
10 | RtmConnectionState state, RtmConnectionChangeReason reason)?
11 | onConnectionStateChanged2;
12 |
13 | /// Occurs when the local user receives a peer-to-peer message.
14 | final void Function(RtmMessage message, String peerId)? onMessageReceived;
15 |
16 | /// Occurs when your token expires.
17 | final void Function()? onTokenExpired;
18 |
19 | final void Function()? onTokenPrivilegeWillExpire;
20 |
21 | final void Function(Map peersStatus)?
22 | onPeersOnlineStatusChanged;
23 |
24 | const AgoraRtmClientEventHandler({
25 | this.onError,
26 | this.onMessageReceived,
27 | this.onTokenExpired,
28 | this.onTokenPrivilegeWillExpire,
29 | this.onConnectionStateChanged2,
30 | this.onPeersOnlineStatusChanged,
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/lib/models/agora_rtm_mute_request.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | /// Class to send the mute request for camera and microphone.
4 | class MuteRequest {
5 | MuteRequest(
6 | {required this.rtcId,
7 | required this.mute,
8 | required this.device,
9 | required this.isForceful});
10 | String messageType = "MuteRequest";
11 | final int rtcId;
12 | final bool mute;
13 | final int device;
14 | final bool isForceful;
15 |
16 | MuteRequest.fromJson(Map json)
17 | : messageType = json['messageType'],
18 | rtcId = json['rtcId'],
19 | mute = json['mute'],
20 | device = json['device'],
21 | isForceful = json['isForceful'];
22 |
23 | Map toJson() => {
24 | 'messageType': messageType,
25 | 'rtcId': rtcId,
26 | 'mute': mute,
27 | 'device': device,
28 | 'isForceful': isForceful
29 | };
30 | }
31 |
32 | /// Class to store and share the user data for RTC and RTM operations
33 | class UserData {
34 | UserData({
35 | required this.rtmId,
36 | required this.rtcId,
37 | required this.username,
38 | required this.role,
39 | });
40 | String messageType = "UserData";
41 |
42 | /// ID used in the RTM connection
43 | late String rtmId;
44 |
45 | /// ID used in the RTC (Video/Audio) connection
46 | late int rtcId;
47 |
48 | /// Username to be displayed for remote users
49 | String? username;
50 |
51 | /// Role of the user (broadcaster or audience)
52 | late int role; // Int, broadcaster = 0, audience = 1
53 |
54 | /// Agora VideoUIKit platform, framework, and version
55 | var uikit = AgoraUIKit().toJson();
56 |
57 | /// Properties about the Agora SDK versions this user is using
58 | var agora = AgoraVersions().toJson();
59 |
60 | UserData.fromJson(Map json) {
61 | messageType = json['messageType'];
62 | rtmId = json['rtmId'];
63 | rtcId = json['rtcId'];
64 | username = json['username'];
65 | role = json['role'];
66 | agora = json['agora'];
67 | uikit = json['uikit'];
68 | }
69 |
70 | Map toJson() {
71 | final data = {};
72 | data['messageType'] = messageType;
73 | data['rtmId'] = rtmId;
74 | data['rtcId'] = rtcId;
75 | data['username'] = username;
76 | data['role'] = role;
77 | data['agora'] = agora;
78 | data['uikit'] = uikit;
79 | return data;
80 | }
81 | }
82 |
83 | class AgoraUIKit {
84 | AgoraUIKit();
85 |
86 | static String platformStr() {
87 | if (Platform.isAndroid) {
88 | return "android";
89 | } else if (Platform.isIOS) {
90 | return "ios";
91 | } else if (Platform.isMacOS) {
92 | return "macos";
93 | } else if (Platform.isWindows) {
94 | return "windows";
95 | }
96 | return "web";
97 | }
98 |
99 | String platform = platformStr();
100 |
101 | String framework = "flutter";
102 | String version = "1.3.10";
103 |
104 | AgoraUIKit.fromJson(Map json)
105 | : platform = json['platform'],
106 | framework = json['framework'],
107 | version = json['version'];
108 |
109 | Map toJson() => {
110 | 'platform': platform,
111 | 'framework': framework,
112 | 'version': version,
113 | };
114 | }
115 |
116 | /// Class to store and share the Agora version for RTC and RTM
117 | class AgoraVersions {
118 | AgoraVersions();
119 | static String staticRTM = '1.5.0';
120 | static String staticRTC = '6.1.0';
121 | String rtm = AgoraVersions.staticRTM;
122 | String rtc = AgoraVersions.staticRTC;
123 |
124 | AgoraVersions.fromJson(Map json)
125 | : rtm = json['rtm'],
126 | rtc = json['rtc'];
127 |
128 | Map toJson() => {'rtm': rtm, 'rtc': rtc};
129 | }
130 |
--------------------------------------------------------------------------------
/lib/models/agora_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_rtc_engine/agora_rtc_engine.dart';
2 | import 'package:agora_rtm/agora_rtm.dart';
3 | import 'package:agora_uikit/models/agora_connection_data.dart';
4 | import 'package:agora_uikit/src/enums.dart';
5 |
6 | import 'agora_user.dart';
7 |
8 | class AgoraSettings {
9 | final RtcEngine? engine;
10 | final AgoraRtmChannel? agoraRtmChannel;
11 | final AgoraRtmClient? agoraRtmClient;
12 | final AgoraConnectionData? connectionData;
13 | final List users;
14 | final AgoraUser mainAgoraUser;
15 | final bool isLocalUserMuted;
16 | final bool isLocalVideoDisabled;
17 | final bool visible;
18 | final ClientRoleType clientRoleType;
19 | final int localUid;
20 | String? generatedToken;
21 | String? generatedRtmToken;
22 | String? generatedRtmId;
23 | final bool isLoggedIn;
24 | final bool isInChannel;
25 | final bool isActiveSpeakerDisabled;
26 | final Layout layoutType;
27 | final bool displaySnackbar;
28 | final MicState muteRequest;
29 | final CameraState cameraRequest;
30 | final bool showMicMessage;
31 | final bool showCameraMessage;
32 | final Map? userdata;
33 | final Map>? userRtmMap;
34 | final Map? uidToUserIdMap;
35 | final bool isScreenShared;
36 | final bool turnOnScreenSharing;
37 | final RecordingState isCloudRecording;
38 | final String? sid;
39 | final String? resourceId;
40 |
41 | AgoraSettings({
42 | this.engine,
43 | this.agoraRtmChannel,
44 | this.agoraRtmClient,
45 | this.connectionData,
46 | required this.users,
47 | required this.mainAgoraUser,
48 | required this.isLocalUserMuted,
49 | required this.isLocalVideoDisabled,
50 | required this.visible,
51 | required this.clientRoleType,
52 | required this.localUid,
53 | this.generatedToken,
54 | this.generatedRtmToken,
55 | this.generatedRtmId,
56 | this.isLoggedIn = false,
57 | this.isInChannel = false,
58 | this.isActiveSpeakerDisabled = false,
59 | this.layoutType = Layout.grid,
60 | this.displaySnackbar = false,
61 | this.muteRequest = MicState.unmuted,
62 | this.cameraRequest = CameraState.enabled,
63 | this.showMicMessage = false,
64 | this.showCameraMessage = false,
65 | this.userdata,
66 | this.userRtmMap,
67 | this.uidToUserIdMap,
68 | this.isScreenShared = false,
69 | this.turnOnScreenSharing = false,
70 | this.isCloudRecording = RecordingState.off,
71 | this.sid,
72 | this.resourceId,
73 | });
74 |
75 | AgoraSettings copyWith({
76 | RtcEngine? engine,
77 | AgoraRtmChannel? agoraRtmChannel,
78 | AgoraRtmClient? agoraRtmClient,
79 | AgoraConnectionData? connectionData,
80 | List? users,
81 | AgoraUser? mainAgoraUser,
82 | bool? isLocalUserMuted,
83 | bool? isLocalVideoDisabled,
84 | bool? visible,
85 | ClientRoleType? clientRoleType,
86 | int? localUid,
87 | String? generatedToken,
88 | String? generatedRtmToken,
89 | String? generatedRtmId,
90 | bool? isLoggedIn,
91 | bool? isInChannel,
92 | bool? isActiveSpeakerDisabled,
93 | Layout? layoutType,
94 | bool? displaySnackbar,
95 | MicState? muteRequest,
96 | CameraState? cameraRequest,
97 | bool? showMicMessage,
98 | bool? showCameraMessage,
99 | Map? userdata,
100 | Map>? userRtmMap,
101 | Map? uidToUserIdMap,
102 | bool? isScreenShared,
103 | bool? turnOnScreenSharing,
104 | RecordingState? isCloudRecording,
105 | String? sid,
106 | String? resourceId,
107 | }) {
108 | return AgoraSettings(
109 | engine: engine ?? this.engine,
110 | agoraRtmChannel: agoraRtmChannel ?? this.agoraRtmChannel,
111 | agoraRtmClient: agoraRtmClient ?? this.agoraRtmClient,
112 | connectionData: connectionData ?? this.connectionData,
113 | users: users ?? this.users,
114 | mainAgoraUser: mainAgoraUser ?? this.mainAgoraUser,
115 | isLocalUserMuted: isLocalUserMuted ?? this.isLocalUserMuted,
116 | isLocalVideoDisabled: isLocalVideoDisabled ?? this.isLocalVideoDisabled,
117 | visible: visible ?? this.visible,
118 | clientRoleType: clientRoleType ?? this.clientRoleType,
119 | localUid: localUid ?? this.localUid,
120 | generatedToken: generatedToken ?? this.generatedToken,
121 | generatedRtmToken: generatedRtmToken ?? this.generatedRtmToken,
122 | generatedRtmId: generatedRtmId ?? this.generatedRtmId,
123 | isLoggedIn: isLoggedIn ?? this.isLoggedIn,
124 | isInChannel: isInChannel ?? this.isInChannel,
125 | isActiveSpeakerDisabled:
126 | isActiveSpeakerDisabled ?? this.isActiveSpeakerDisabled,
127 | layoutType: layoutType ?? this.layoutType,
128 | displaySnackbar: displaySnackbar ?? this.displaySnackbar,
129 | muteRequest: muteRequest ?? this.muteRequest,
130 | cameraRequest: cameraRequest ?? this.cameraRequest,
131 | showMicMessage: showMicMessage ?? this.showMicMessage,
132 | showCameraMessage: showCameraMessage ?? this.showCameraMessage,
133 | userdata: userdata ?? this.userdata,
134 | userRtmMap: userRtmMap ?? this.userRtmMap,
135 | uidToUserIdMap: uidToUserIdMap ?? this.uidToUserIdMap,
136 | isScreenShared: isScreenShared ?? this.isScreenShared,
137 | turnOnScreenSharing: turnOnScreenSharing ?? this.turnOnScreenSharing,
138 | isCloudRecording: isCloudRecording ?? this.isCloudRecording,
139 | sid: sid ?? this.sid,
140 | resourceId: resourceId ?? this.resourceId,
141 | );
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/lib/models/agora_user.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_rtc_engine/agora_rtc_engine.dart';
2 |
3 | /// Data class of a user present in a channel.
4 | class AgoraUser {
5 | int uid;
6 | final bool remote;
7 | final bool muted;
8 | final bool videoDisabled;
9 | final ClientRoleType clientRoleType;
10 |
11 | AgoraUser({
12 | required this.uid,
13 | this.remote = true,
14 | this.muted = false,
15 | this.videoDisabled = false,
16 | this.clientRoleType = ClientRoleType.clientRoleBroadcaster,
17 | });
18 |
19 | AgoraUser copyWith({
20 | int? uid,
21 | bool? remote,
22 | bool? muted,
23 | bool? videoDisabled,
24 | ClientRoleType? clientRoleType,
25 | }) {
26 | return AgoraUser(
27 | uid: uid ?? this.uid,
28 | remote: remote ?? this.remote,
29 | muted: muted ?? this.muted,
30 | videoDisabled: videoDisabled ?? this.videoDisabled,
31 | clientRoleType: clientRoleType ?? this.clientRoleType,
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/models/rtm_message.dart:
--------------------------------------------------------------------------------
1 | /// Class to decode the AgoraRtmMessage
2 | class Message {
3 | final String text;
4 | final int? ts;
5 | final bool? offline;
6 |
7 | Message({required this.text, this.ts, this.offline});
8 |
9 | Map toJson() => {
10 | 'text': text,
11 | 'ts': ts,
12 | 'offline': offline,
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/lib/src/agora_client.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:developer';
3 |
4 | import 'package:agora_rtc_engine/agora_rtc_engine.dart';
5 | import 'package:agora_uikit/controllers/session_controller.dart';
6 | import 'package:agora_uikit/models/agora_channel_data.dart';
7 | import 'package:agora_uikit/models/agora_connection_data.dart';
8 | import 'package:agora_uikit/models/agora_rtc_event_handlers.dart';
9 | import 'package:agora_uikit/models/agora_rtm_channel_event_handler.dart';
10 | import 'package:agora_uikit/models/agora_rtm_client_event_handler.dart';
11 | import 'package:agora_uikit/src/enums.dart';
12 | import 'package:flutter/services.dart';
13 | import 'package:permission_handler/permission_handler.dart';
14 |
15 | import '../controllers/rtc_buttons.dart';
16 |
17 | /// [AgoraClient] is the main class in this VideoUIKit. It is used to initialize our [RtcEngine], add the list of user permissions, define the channel properties and use extend the [RtcEngineEventHandler] class.
18 | class AgoraClient {
19 | /// [AgoraConnectionData] is a class used to store all the connection details to authenticate your account with the Agora SDK.
20 | final AgoraConnectionData agoraConnectionData;
21 |
22 | /// [enabledPermission] is a list of permissions that the user has to grant to the app. By default the UIKit asks for the camera and microphone permissions for every broadcaster that joins the channel.
23 | final List? enabledPermission;
24 |
25 | /// [AgoraChannelData] is a class that contains all the Agora channel properties.
26 | final AgoraChannelData? agoraChannelData;
27 |
28 | /// [AgoraRtcEventHandlers] is a class that contains all the Agora RTC event handlers. Use it to add your own functions or methods.
29 | final AgoraRtcEventHandlers? agoraEventHandlers;
30 |
31 | /// [AgoraRtmClientEventHandlers] is a class that contains all the Agora RTM Client event handlers. Use it to add your own functions or methods.
32 | final AgoraRtmClientEventHandler? agoraRtmClientEventHandler;
33 |
34 | /// [AgoraRtmChannelEventHandlers] is a class that contains all the Agora RTM channel event handlers. Use it to add your own functions or methods.
35 | final AgoraRtmChannelEventHandler? agoraRtmChannelEventHandler;
36 |
37 | bool _initialized = false;
38 |
39 | AgoraClient({
40 | required this.agoraConnectionData,
41 | this.enabledPermission,
42 | this.agoraChannelData,
43 | this.agoraEventHandlers,
44 | this.agoraRtmClientEventHandler,
45 | this.agoraRtmChannelEventHandler,
46 | }) : _initialized = false;
47 |
48 | /// Useful to check if [AgoraClient] is ready for further usage
49 | bool get isInitialized => _initialized;
50 |
51 | static const MethodChannel _channel = MethodChannel('agora_uikit');
52 |
53 | static Future platformVersion() async {
54 | final String version = await _channel.invokeMethod('getPlatformVersion');
55 | return version;
56 | }
57 |
58 | List get users {
59 | final List version =
60 | _sessionController.value.users.map((e) => e.uid).toList();
61 | return version;
62 | }
63 |
64 | // This is our "state" object that the UI Kit works with
65 | final SessionController _sessionController = SessionController();
66 | SessionController get sessionController {
67 | return _sessionController;
68 | }
69 |
70 | RtcEngine get engine {
71 | return _sessionController.value.engine!;
72 | }
73 |
74 | Future initialize() async {
75 | if (_initialized) {
76 | return;
77 | }
78 |
79 | try {
80 | await _sessionController.initializeEngine(
81 | agoraConnectionData: agoraConnectionData);
82 | } catch (e) {
83 | log("Error while initializing Agora RTC SDK: $e",
84 | level: Level.error.value);
85 | }
86 |
87 | if (agoraConnectionData.rtmEnabled) {
88 | try {
89 | await _sessionController.initializeRtm(
90 | agoraRtmClientEventHandler ?? AgoraRtmClientEventHandler());
91 | } catch (e) {
92 | log("Error while initializing Agora RTM SDK. ${e.toString()}",
93 | level: Level.error.value);
94 | }
95 | }
96 |
97 | if (agoraChannelData?.clientRoleType ==
98 | ClientRoleType.clientRoleBroadcaster ||
99 | agoraChannelData?.clientRoleType == null) {
100 | await _sessionController.askForUserCameraAndMicPermission();
101 | }
102 | if (enabledPermission != null) {
103 | await enabledPermission!.request();
104 | }
105 |
106 | _sessionController.createEvents(
107 | agoraRtmChannelEventHandler ?? AgoraRtmChannelEventHandler(),
108 | agoraEventHandlers ?? AgoraRtcEventHandlers(),
109 | );
110 |
111 | if (agoraChannelData != null) {
112 | _sessionController.setChannelProperties(agoraChannelData!);
113 | }
114 |
115 | await _sessionController.joinVideoChannel();
116 | _initialized = true;
117 | }
118 |
119 | Future release() async {
120 | _initialized = false;
121 | await endCall(sessionController: _sessionController);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/lib/src/buttons/cloud_recording_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_uikit/agora_uikit.dart';
2 | import 'package:agora_uikit/controllers/rtc_buttons.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class CloudRecordingButton extends StatelessWidget {
6 | final Widget? child;
7 | final AgoraClient client;
8 | const CloudRecordingButton({
9 | super.key,
10 | required this.client,
11 | this.child,
12 | });
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return child != null
17 | ? RawMaterialButton(
18 | onPressed: () => toggleCloudRecording(client: client),
19 | child: child,
20 | )
21 | : RecordingStateButton(
22 | client: client,
23 | );
24 | }
25 | }
26 |
27 | class RecordingStateButton extends StatelessWidget {
28 | final AgoraClient client;
29 | const RecordingStateButton({super.key, required this.client});
30 |
31 | @override
32 | Widget build(BuildContext context) {
33 | switch (client.sessionController.value.isCloudRecording) {
34 | case RecordingState.off:
35 | return RawMaterialButton(
36 | onPressed: () => toggleCloudRecording(client: client),
37 | child: Icon(
38 | Icons.circle_outlined,
39 | color: Colors.blueAccent,
40 | size: 20.0,
41 | ),
42 | shape: CircleBorder(),
43 | elevation: 2.0,
44 | fillColor: Colors.white,
45 | padding: const EdgeInsets.all(12.0),
46 | );
47 | case RecordingState.loading:
48 | return RawMaterialButton(
49 | onPressed: () {},
50 | child: Container(
51 | height: 20,
52 | width: 20,
53 | padding: const EdgeInsets.all(2),
54 | child: CircularProgressIndicator(
55 | strokeWidth: 2,
56 | ),
57 | ),
58 | shape: CircleBorder(),
59 | elevation: 2.0,
60 | fillColor: Colors.white,
61 | padding: const EdgeInsets.all(12.0),
62 | );
63 | case RecordingState.recording:
64 | return RawMaterialButton(
65 | onPressed: () => toggleCloudRecording(client: client),
66 | child: Icon(
67 | Icons.stop,
68 | color: Colors.white,
69 | size: 20.0,
70 | ),
71 | shape: CircleBorder(),
72 | elevation: 2.0,
73 | fillColor: Colors.blueAccent,
74 | padding: const EdgeInsets.all(12.0),
75 | );
76 | default:
77 | return RawMaterialButton(
78 | onPressed: () {},
79 | child: CircularProgressIndicator(),
80 | );
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/src/enums.dart:
--------------------------------------------------------------------------------
1 | enum Layout { grid, floating, oneToOne }
2 |
3 | enum BuiltInButtons {
4 | callEnd,
5 | switchCamera,
6 | toggleCamera,
7 | toggleMic,
8 | screenSharing,
9 | cloudRecording
10 | }
11 |
12 | enum MicState { muted, unmuted }
13 |
14 | enum CameraState { enabled, disabled }
15 |
16 | enum Level { info, warning, error }
17 |
18 | extension LevelExtension on Level {
19 | int get value {
20 | switch (this) {
21 | case Level.info:
22 | return 800;
23 | case Level.warning:
24 | return 900;
25 | case Level.error:
26 | return 1000;
27 | default:
28 | return 800;
29 | }
30 | }
31 | }
32 |
33 | enum Device { camera, mic }
34 |
35 | enum RecordingState { off, loading, recording }
36 |
--------------------------------------------------------------------------------
/lib/src/layout/widgets/disabled_video_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Widget that is displayed when local/remote video is disabled.
4 | class DisabledVideoWidget extends StatefulWidget {
5 | const DisabledVideoWidget({super.key});
6 |
7 | @override
8 | State createState() => _DisabledVideoWidgetState();
9 | }
10 |
11 | class _DisabledVideoWidgetState extends State {
12 | @override
13 | Widget build(BuildContext context) {
14 | return Container(
15 | color: Colors.black,
16 | padding: const EdgeInsets.all(8.0),
17 | child: Center(
18 | child: Icon(
19 | Icons.person_rounded,
20 | color: Colors.white,
21 | )),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/layout/widgets/host_controls.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_uikit/agora_uikit.dart';
2 | import 'package:agora_uikit/controllers/rtm_mute_request.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class HostControls extends StatefulWidget {
6 | final AgoraClient client;
7 | final bool videoDisabled;
8 | final bool muted;
9 | final int index;
10 |
11 | const HostControls({
12 | super.key,
13 | required this.videoDisabled,
14 | required this.muted,
15 | required this.client,
16 | required this.index,
17 | });
18 |
19 | @override
20 | State createState() => _HostControlsState();
21 | }
22 |
23 | class _HostControlsState extends State {
24 | @override
25 | Widget build(BuildContext context) {
26 | return PopupMenuButton(
27 | icon: Icon(Icons.more_vert, color: Colors.white),
28 | enableFeedback: true,
29 | tooltip: 'Host Controls',
30 | itemBuilder: (BuildContext context) => [
31 | PopupMenuItem(
32 | child: GestureDetector(
33 | onTap: () {
34 | hostControl(
35 | index: widget.index,
36 | mute: !widget.muted,
37 | sessionController: widget.client.sessionController,
38 | device: Device.mic,
39 | );
40 | Navigator.pop(context);
41 | },
42 | child: SizedBox(
43 | child: Row(
44 | mainAxisAlignment: MainAxisAlignment.start,
45 | children: [
46 | Icon(
47 | widget.muted ? Icons.mic_off : Icons.mic,
48 | size: 15,
49 | ),
50 | SizedBox(width: 5),
51 | widget.muted
52 | ? Text(
53 | 'Ask user to unmute the mic',
54 | style: TextStyle(fontSize: 15),
55 | )
56 | : Text(
57 | 'Ask user to mute the mic',
58 | style: TextStyle(fontSize: 15),
59 | )
60 | ],
61 | ),
62 | ),
63 | ),
64 | ),
65 | PopupMenuItem(
66 | child: GestureDetector(
67 | onTap: () {
68 | hostControl(
69 | index: widget.index,
70 | mute: !widget.videoDisabled,
71 | sessionController: widget.client.sessionController,
72 | device: Device.camera,
73 | );
74 | Navigator.pop(context);
75 | },
76 | child: SizedBox(
77 | child: Row(
78 | mainAxisAlignment: MainAxisAlignment.start,
79 | children: [
80 | Icon(
81 | widget.videoDisabled ? Icons.videocam_off : Icons.videocam,
82 | size: 15,
83 | ),
84 | SizedBox(width: 5),
85 | widget.videoDisabled
86 | ? Text(
87 | 'Ask user to turn on the camera',
88 | style: TextStyle(fontSize: 15),
89 | )
90 | : Text(
91 | 'Ask user to turn off the camera',
92 | style: TextStyle(fontSize: 15),
93 | )
94 | ],
95 | ),
96 | ),
97 | ),
98 | ),
99 | ],
100 | );
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib/src/layout/widgets/number_of_users.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Displays the number of users present inside the channel.
4 | class NumberOfUsers extends StatefulWidget {
5 | final int userCount;
6 |
7 | const NumberOfUsers({
8 | super.key,
9 | this.userCount = 0,
10 | });
11 |
12 | @override
13 | State createState() => _NumberOfUsersState();
14 | }
15 |
16 | class _NumberOfUsersState extends State {
17 | @override
18 | Widget build(BuildContext context) {
19 | return Padding(
20 | padding: const EdgeInsets.all(8.0),
21 | child: Container(
22 | color: Colors.black54,
23 | padding: const EdgeInsets.all(3),
24 | child: Row(
25 | mainAxisSize: MainAxisSize.min,
26 | children: [
27 | Icon(
28 | Icons.remove_red_eye_outlined,
29 | color: Colors.white,
30 | ),
31 | Text(
32 | ' ${widget.userCount + 1}',
33 | style: TextStyle(color: Colors.white),
34 | ),
35 | ],
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/src/layout/widgets/user_av_state_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:agora_uikit/src/enums.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | /// Displays the camera and microphone state of local and remote user. Currently, this mode is only available in the [Layout.floating].
5 | class UserAVStateWidget extends StatefulWidget {
6 | final bool videoDisabled;
7 | final bool muted;
8 |
9 | const UserAVStateWidget({
10 | super.key,
11 | required this.videoDisabled,
12 | required this.muted,
13 | });
14 |
15 | @override
16 | State createState() => _UserAVStateWidgetState();
17 | }
18 |
19 | class _UserAVStateWidgetState extends State {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Positioned.fill(
23 | child: Align(
24 | alignment: Alignment.bottomRight,
25 | child: Padding(
26 | padding: const EdgeInsets.all(8.0),
27 | child: Row(
28 | mainAxisAlignment: MainAxisAlignment.end,
29 | children: [
30 | Container(
31 | decoration: BoxDecoration(
32 | color: widget.videoDisabled ? Colors.blue : Colors.white,
33 | shape: BoxShape.circle,
34 | ),
35 | child: Padding(
36 | padding: const EdgeInsets.all(3.0),
37 | child: widget.videoDisabled
38 | ? Icon(
39 | Icons.videocam_off,
40 | color: Colors.white,
41 | size: 15,
42 | )
43 | : Icon(
44 | Icons.videocam,
45 | color: Colors.blue,
46 | size: 15,
47 | ),
48 | ),
49 | ),
50 | SizedBox(
51 | width: 5,
52 | ),
53 | Container(
54 | decoration: BoxDecoration(
55 | color: widget.muted ? Colors.blue : Colors.white,
56 | shape: BoxShape.circle,
57 | ),
58 | child: Padding(
59 | padding: const EdgeInsets.all(3.0),
60 | child: widget.muted
61 | ? Icon(
62 | Icons.mic_off,
63 | color: Colors.white,
64 | size: 15,
65 | )
66 | : Icon(
67 | Icons.mic,
68 | color: Colors.blue,
69 | size: 15,
70 | ),
71 | ),
72 | ),
73 | ],
74 | ),
75 | ),
76 | ),
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: agora_uikit
2 | description: Flutter plugin to simply integrate Agora Video Calling or Live
3 | Video Streaming to your app with just a few lines of code.
4 | version: 1.3.10
5 | homepage: https://www.agora.io/en/
6 | repository: https://github.com/AgoraIO-Community/VideoUIKit-Flutter
7 |
8 | environment:
9 | sdk: ">=2.19.0 <4.0.0"
10 | flutter: ">=2.2.0"
11 |
12 | dependencies:
13 | agora_rtc_engine: ^6.3.0
14 | agora_rtm: ^1.5.5
15 | flutter:
16 | sdk: flutter
17 | http: ">=0.13.1 <2.0.0"
18 | lints: ">=1.0.1 <3.0.1"
19 | permission_handler: ^11.0.0
20 |
21 | dev_dependencies:
22 | flutter_test:
23 | sdk: flutter
24 |
25 | flutter:
26 | plugin:
27 | platforms:
28 | android:
29 | package: io.agora.agora_uikit
30 | pluginClass: AgoraUikitPlugin
31 | ios:
32 | pluginClass: AgoraUikitPlugin
33 |
34 | analyzer:
35 | strong-mode: true
36 | errors:
37 | mixin_inherits_from_not_object: ignore
38 |
--------------------------------------------------------------------------------
/test/agora_flutter_uikit_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:agora_uikit/agora_uikit.dart';
4 |
5 | void main() {
6 | const MethodChannel channel = MethodChannel('agora_uikit');
7 |
8 | TestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | setUp(() {
11 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
12 | .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
13 | return '42';
14 | });
15 | });
16 |
17 | tearDown(() {
18 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
19 | .setMockMethodCallHandler(
20 | channel,
21 | null,
22 | );
23 | });
24 |
25 | test('getPlatformVersion', () async {
26 | expect(await AgoraClient.platformVersion(), '42');
27 | });
28 | }
29 |
--------------------------------------------------------------------------------