├── .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 | 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 | --------------------------------------------------------------------------------