AgoraIO-Extensions / Agora-Flutter-SDK

Flutter plugin of Agora RTC SDK for Android/iOS/macOS/Windows
https://pub.dev/packages/agora_rtc_engine
MIT License
720 stars 372 forks source link

Issue with Screen Sharing on Flutter iOS with video call #1882

Open vaibhavbhus opened 4 days ago

vaibhavbhus commented 4 days ago

Version of the agora_rtc_engine

6.3.2

Platforms affected

Steps to reproduce

  1. Implement screen sharing following the Agora documentation.
  2. Launch the app on an iOS device.
  3. Observe the app crashing on the splash screen.

Expected results

The app should not crash and screen sharing should work as intended.

Actual results

Actual Behavior: The app crashes on the splash screen after implementing the changes.

I am facing an issue with screen sharing in Flutter specifically on iOS in video call. The feature works as expected on Android.

Initially, I encountered a MissingPluginException with the following message but the video call was working as expected: MissingPluginException(No implementation found for method show RPSystemBroadcastPickerView on channel example_screensharing_ios)

I followed the steps provided in the Agora documentation for screen sharing. (https://docs.agora.io/en/3.x/video-calling/basic-features/screensharing?platform=flutter) However, after implementing these changes, my app crashes on the splash screen.

Additional Information: Environment: Flutter version: 3.16.1 iOS version: 17.1.1 Agora SDK version: 6.3.2

Please provide guidance on resolving this issue. Thank you.

Code sample

Code sample ```dart import 'dart:ui' as ui; import 'package:Omniva/component/basic_video_configuration_widget.dart'; import 'package:Omniva/component/example_actions_widget.dart'; import 'package:Omniva/component/remote_video_views_widget.dart'; import 'package:Omniva/component/rgba_image.dart'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:permission_handler/permission_handler.dart'; import '../../../ids.dart'; /// ScreenSharing Example class ScreenSharing extends StatefulWidget { /// Construct the [ScreenSharing] const ScreenSharing({Key? key}) : super(key: key); @override State createState() => _State(); } class _State extends State with KeepRemoteVideoViewsMixin { late final RtcEngineEx _engine; bool _isReadyPreview = false; String channelId = "test"; bool isJoined = false; late TextEditingController _controller; late final TextEditingController _localUidController; late final TextEditingController _screenShareUidController; bool _isScreenShared = false; late final RtcEngineEventHandler _rtcEngineEventHandler; @override void initState() { super.initState(); _controller = TextEditingController(text: channelId); _localUidController = TextEditingController(text: '1000'); _screenShareUidController = TextEditingController(text: '1001'); _initEngine(); } @override void dispose() { super.dispose(); _engine.unregisterEventHandler(_rtcEngineEventHandler); _engine.release(); } _initEngine() async { await [Permission.microphone, Permission.camera].request(); _rtcEngineEventHandler = RtcEngineEventHandler( onError: (ErrorCodeType err, String msg) { // logSink.log('[onError] err: $err, msg: $msg'); }, onJoinChannelSuccess: (RtcConnection connection, int elapsed) { // logSink.log( // '[onJoinChannelSuccess] connection: ${connection.toJson()} elapsed: $elapsed'); setState(() { isJoined = true; }); }, onLeaveChannel: (RtcConnection connection, RtcStats stats) { // logSink.log( // '[onLeaveChannel] connection: ${connection.toJson()} stats: ${stats.toJson()}'); setState(() { isJoined = false; }); }, onLocalVideoStateChanged: (VideoSourceType source, LocalVideoStreamState state, LocalVideoStreamReason error) { // logSink.log( // '[onLocalVideoStateChanged] source: $source, state: $state, error: $error'); if (!(source == VideoSourceType.videoSourceScreen || source == VideoSourceType.videoSourceScreenPrimary)) { return; } switch (state) { case LocalVideoStreamState.localVideoStreamStateCapturing: case LocalVideoStreamState.localVideoStreamStateEncoding: setState(() { _isScreenShared = true; }); break; case LocalVideoStreamState.localVideoStreamStateStopped: case LocalVideoStreamState.localVideoStreamStateFailed: setState(() { _isScreenShared = false; }); break; default: break; } }); _engine = createAgoraRtcEngineEx(); await _engine.initialize(RtcEngineContext( appId: appId, channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, )); await _engine.setLogLevel(LogLevel.logLevelError); _engine.registerEventHandler(_rtcEngineEventHandler); await _engine.enableVideo(); try { await _engine.startPreview(); } catch (e, s) { print(e.toString()); print(s.toString()); } await _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster); setState(() { _isReadyPreview = true; }); } void _joinChannel() async { final localUid = int.tryParse(_localUidController.text); if (localUid != null) { await _engine.joinChannelEx( token: token, connection: RtcConnection(channelId: _controller.text, localUid: localUid), options: const ChannelMediaOptions( autoSubscribeVideo: true, autoSubscribeAudio: true, publishCameraTrack: true, publishMicrophoneTrack: true, clientRoleType: ClientRoleType.clientRoleBroadcaster, )); } final shareShareUid = int.tryParse(_screenShareUidController.text); if (shareShareUid != null) { await _engine.joinChannelEx( token: token, connection: RtcConnection( channelId: _controller.text, localUid: shareShareUid), options: const ChannelMediaOptions( autoSubscribeVideo: false, autoSubscribeAudio: false, publishScreenTrack: true, publishSecondaryScreenTrack: true, publishCameraTrack: false, publishMicrophoneTrack: false, publishScreenCaptureAudio: true, publishScreenCaptureVideo: true, clientRoleType: ClientRoleType.clientRoleBroadcaster, )); } } Future _updateScreenShareChannelMediaOptions() async { try { final shareShareUid = int.tryParse(_screenShareUidController.text); if (shareShareUid == null) return; await _engine.updateChannelMediaOptionsEx( options: const ChannelMediaOptions( publishScreenTrack: true, publishSecondaryScreenTrack: true, publishCameraTrack: false, publishMicrophoneTrack: false, publishScreenCaptureAudio: true, publishScreenCaptureVideo: true, autoSubscribeVideo: true, clientRoleType: ClientRoleType.clientRoleBroadcaster, ), connection: RtcConnection(channelId: _controller.text, localUid: shareShareUid), ); print("e.toString()"); } catch (e, s) { print("e.toString()"); print(s.toString()); } } _leaveChannel() async { await _engine.stopScreenCapture(); await _engine.leaveChannel(); } @override Widget build(BuildContext context) { return ExampleActionsWidget( displayContentBuilder: (context, isLayoutHorizontal) { if (!_isReadyPreview) return Container(); final children = [ Expanded( flex: 1, child: AspectRatio( aspectRatio: 1, child: AgoraVideoView( controller: VideoViewController( rtcEngine: _engine, canvas: const VideoCanvas( uid: 0, ), )), ), ), Expanded( flex: 1, child: AspectRatio( aspectRatio: 1, child: _isScreenShared ? AgoraVideoView( controller: VideoViewController( rtcEngine: _engine, canvas: const VideoCanvas( uid: 0, sourceType: VideoSourceType.videoSourceScreen, ), )) : Container( color: Colors.grey[200], child: const Center( child: Text('Screen Sharing View'), ), ), ), ), ]; Widget localVideoView; if (isLayoutHorizontal) { localVideoView = Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: children, ); } else { localVideoView = Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: children, ); } return Stack( children: [ localVideoView, Align( alignment: Alignment.topLeft, child: RemoteVideoViewsWidget( // key: keepRemoteVideoViewsKey, rtcEngine: _engine, channelId: _controller.text, connectionUid: int.tryParse(_localUidController.text), ), ) ], ); }, actionsBuilder: (context, isLayoutHorizontal) { if (!_isReadyPreview) return Container(); return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ TextField( controller: _controller, decoration: const InputDecoration(hintText: 'Channel ID'), ), TextField( controller: _localUidController, decoration: const InputDecoration(hintText: 'Local Uid'), ), TextField( controller: _screenShareUidController, decoration: const InputDecoration(hintText: 'Screen Sharing Uid'), ), const SizedBox( height: 20, ), // BasicVideoConfigurationWidget( // rtcEngine: _engine, // title: 'Video Encoder Configuration', // setConfigButtonText: const Text( // 'setVideoEncoderConfiguration', // style: TextStyle(fontSize: 10), // ), // onConfigChanged: (width, height, frameRate, bitrate) { // _engine.setVideoEncoderConfiguration(VideoEncoderConfiguration( // dimensions: VideoDimensions(width: width, height: height), // frameRate: frameRate, // bitrate: bitrate, // )); // }, // ), const SizedBox( height: 20, ), Row( children: [ Expanded( flex: 1, child: ElevatedButton( onPressed: isJoined ? _leaveChannel : _joinChannel, child: Text('${isJoined ? 'Leave' : 'Join'} channel'), ), ) ], ), if (kIsWeb) ScreenShareWeb( rtcEngine: _engine, isScreenShared: _isScreenShared, onStartScreenShared: () { if (isJoined) { _updateScreenShareChannelMediaOptions(); } }, onStopScreenShare: () {}), if (!kIsWeb && (defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS)) ScreenShareMobile( rtcEngine: _engine, isScreenShared: _isScreenShared, onStartScreenShared: () { if (isJoined) { _updateScreenShareChannelMediaOptions(); } }, onStopScreenShare: () {}), if (!kIsWeb && (defaultTargetPlatform == TargetPlatform.windows || defaultTargetPlatform == TargetPlatform.macOS)) ScreenShareDesktop( rtcEngine: _engine, isScreenShared: _isScreenShared, onStartScreenShared: () { if (isJoined) { _updateScreenShareChannelMediaOptions(); } }, onStopScreenShare: () {}), ], ); }, ); } } class ScreenShareWeb extends StatefulWidget { const ScreenShareWeb( {Key? key, required this.rtcEngine, required this.isScreenShared, required this.onStartScreenShared, required this.onStopScreenShare}) : super(key: key); final RtcEngine rtcEngine; final bool isScreenShared; final VoidCallback onStartScreenShared; final VoidCallback onStopScreenShare; @override State createState() => _ScreenShareWebState(); } class _ScreenShareWebState extends State implements ScreenShareInterface { @override bool get isScreenShared => widget.isScreenShared; @override void onStartScreenShared() { widget.onStartScreenShared(); } @override void onStopScreenShare() { widget.onStopScreenShare(); } @override RtcEngine get rtcEngine => widget.rtcEngine; @override Widget build(BuildContext context) { return Row( children: [ Expanded( flex: 1, child: ElevatedButton( onPressed: !isScreenShared ? startScreenShare : stopScreenShare, child: Text('${isScreenShared ? 'Stop' : 'Start'} screen share'), ), ) ], ); } @override void startScreenShare() async { if (isScreenShared) return; await rtcEngine.startScreenCapture( const ScreenCaptureParameters2(captureAudio: true, captureVideo: true)); await rtcEngine.startPreview(sourceType: VideoSourceType.videoSourceScreen); onStartScreenShared(); } @override void stopScreenShare() async { if (!isScreenShared) return; await rtcEngine.stopScreenCapture(); onStopScreenShare(); } } class ScreenShareMobile extends StatefulWidget { const ScreenShareMobile( {Key? key, required this.rtcEngine, required this.isScreenShared, required this.onStartScreenShared, required this.onStopScreenShare}) : super(key: key); final RtcEngine rtcEngine; final bool isScreenShared; final VoidCallback onStartScreenShared; final VoidCallback onStopScreenShare; @override State createState() => _ScreenShareMobileState(); } class _ScreenShareMobileState extends State implements ScreenShareInterface { final MethodChannel _iosScreenShareChannel = const MethodChannel('example_screensharing_ios'); @override bool get isScreenShared => widget.isScreenShared; @override void onStartScreenShared() { widget.onStartScreenShared(); } @override void onStopScreenShare() { widget.onStopScreenShare(); } @override RtcEngine get rtcEngine => widget.rtcEngine; @override Widget build(BuildContext context) { return Row( children: [ Expanded( flex: 1, child: ElevatedButton( onPressed: !isScreenShared ? startScreenShare : stopScreenShare, child: Text('${isScreenShared ? 'Stop' : 'Start'} screen share'), ), ) ], ); } @override void startScreenShare() async { if (isScreenShared) return; await rtcEngine.startScreenCapture( const ScreenCaptureParameters2(captureAudio: true, captureVideo: true)); await rtcEngine.startPreview(sourceType: VideoSourceType.videoSourceScreen); _showRPSystemBroadcastPickerViewIfNeed(); onStartScreenShared(); } @override void stopScreenShare() async { if (!isScreenShared) return; await rtcEngine.stopScreenCapture(); onStopScreenShare(); } Future _showRPSystemBroadcastPickerViewIfNeed() async { if (defaultTargetPlatform != TargetPlatform.iOS) { return; } await _iosScreenShareChannel .invokeMethod('showRPSystemBroadcastPickerView'); } } class ScreenShareDesktop extends StatefulWidget { const ScreenShareDesktop( {Key? key, required this.rtcEngine, required this.isScreenShared, required this.onStartScreenShared, required this.onStopScreenShare}) : super(key: key); final RtcEngine rtcEngine; final bool isScreenShared; final VoidCallback onStartScreenShared; final VoidCallback onStopScreenShare; @override State createState() => _ScreenShareDesktopState(); } class _ScreenShareDesktopState extends State implements ScreenShareInterface { List _screenCaptureSourceInfos = []; late ScreenCaptureSourceInfo _selectedScreenCaptureSourceInfo; @override bool get isScreenShared => widget.isScreenShared; @override void onStartScreenShared() { widget.onStartScreenShared(); } @override void onStopScreenShare() { widget.onStopScreenShare(); } @override RtcEngine get rtcEngine => widget.rtcEngine; Future _initScreenCaptureSourceInfos() async { SIZE thumbSize = const SIZE(width: 50, height: 50); SIZE iconSize = const SIZE(width: 50, height: 50); _screenCaptureSourceInfos = await rtcEngine.getScreenCaptureSources( thumbSize: thumbSize, iconSize: iconSize, includeScreen: true); _selectedScreenCaptureSourceInfo = _screenCaptureSourceInfos[0]; setState(() {}); } Widget _createDropdownButton() { if (_screenCaptureSourceInfos.isEmpty) return Container(); ui.PixelFormat format = ui.PixelFormat.rgba8888; if (defaultTargetPlatform == TargetPlatform.windows) { // The native sdk return the bgra format on Windows. format = ui.PixelFormat.bgra8888; } return DropdownButton( items: _screenCaptureSourceInfos.map((info) { Widget image; if (info.iconImage!.width! != 0 && info.iconImage!.height! != 0) { image = RgbaImage( bytes: info.iconImage!.buffer!, width: info.iconImage!.width!, height: info.iconImage!.height!, format: format, ); } else if (info.thumbImage!.width! != 0 && info.thumbImage!.height! != 0) { image = RgbaImage( bytes: info.thumbImage!.buffer!, width: info.thumbImage!.width!, height: info.thumbImage!.height!, format: format, ); } else { image = const SizedBox( width: 50, height: 50, ); } return DropdownMenuItem( value: info, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ image, Text('${info.sourceName}', style: const TextStyle(fontSize: 10)) ], ), ); }).toList(), value: _selectedScreenCaptureSourceInfo, onChanged: isScreenShared ? null : (v) { setState(() { _selectedScreenCaptureSourceInfo = v!; }); }); } @override void initState() { super.initState(); _initScreenCaptureSourceInfos(); } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ _createDropdownButton(), if (_screenCaptureSourceInfos.isNotEmpty) Row( children: [ Expanded( flex: 1, child: ElevatedButton( onPressed: !isScreenShared ? startScreenShare : stopScreenShare, child: Text('${isScreenShared ? 'Stop' : 'Start'} screen share'), ), ) ], ), ], ); } @override void startScreenShare() async { if (isScreenShared) return; final sourceId = _selectedScreenCaptureSourceInfo.sourceId; if (_selectedScreenCaptureSourceInfo.type == ScreenCaptureSourceType.screencapturesourcetypeScreen) { await rtcEngine.startScreenCaptureByDisplayId( displayId: sourceId!, regionRect: const Rectangle(x: 0, y: 0, width: 0, height: 0), captureParams: const ScreenCaptureParameters( captureMouseCursor: true, frameRate: 30, )); } else if (_selectedScreenCaptureSourceInfo.type == ScreenCaptureSourceType.screencapturesourcetypeWindow) { await rtcEngine.startScreenCaptureByWindowId( windowId: sourceId!, regionRect: const Rectangle(x: 0, y: 0, width: 0, height: 0), captureParams: const ScreenCaptureParameters( captureMouseCursor: true, frameRate: 30, ), ); } onStartScreenShared(); } @override void stopScreenShare() async { if (!isScreenShared) return; await rtcEngine.stopScreenCapture(); onStopScreenShare(); } } abstract class ScreenShareInterface { void onStartScreenShared(); void onStopScreenShare(); bool get isScreenShared; RtcEngine get rtcEngine; void startScreenShare(); void stopScreenShare(); } ```

Screenshots or Video

Screenshots / Video demonstration https://github.com/AgoraIO-Extensions/Agora-Flutter-SDK/assets/174697940/a6b878e5-4d7f-4055-a663-5646212ec0ca

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.16.9, on macOS 14.3.1 23D60 darwin-arm64, locale en-IN) • Flutter version 3.16.9 on channel stable at /Users/user/fvm/versions/3.16.1 • Upstream repository https://github.com/flutter/flutter.git • Framework revision 41456452f2 (5 months ago), 2024-01-25 10:06:23 -0800 • Engine revision f40e976bed • Dart version 3.2.6 • DevTools version 2.28.5 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/user/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.2) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 15C500b • CocoaPods version 1.14.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) [✓] Android Studio (version 4.2) • Android Studio at /Users/user/Downloads/android studio/Android Studio 4.2.2.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264) [✓] Android Studio (version 2021.2) • Android Studio at /Users/user/Downloads/android studio/Android Studio chipmunk patch 1.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.90.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.90.0 [✓] Connected device (5 available) • sdk gphone arm64 (mobile) • emulator-5554 • android-arm64 • Android 11 (API 30) (emulator) • Jenish’s iPhone (mobile) • 00008030-00110DDC0ED0802E • ios • iOS 17.5.1 21F90 • iMac’s iPhone (mobile) • 00008030-000325A91A90802E • ios • iOS 17.1.1 21B91 • macOS (desktop) • macos • darwin-arm64 • macOS 14.3.1 23D60 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 126.0.6478.127 ! Error: Browsing on the local area network for HARDIK’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) ! Error: Browsing on the local area network for Fenil’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) ! Error: Browsing on the local area network for Tele’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) ! Error: Browsing on the local area network for Pankit’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac. The device must be opted into Developer Mode to connect wirelessly. (code -27) [✓] Network resources • All expected network resources are available. ```
vaibhavbhus commented 4 days ago

@yeahren @maxxfrazer @littleGnAl @LichKing-2234 @devsideal @roip890 Please provide guidance on resolving this issue. Thank you.

littleGnAl commented 3 days ago

It is most likely you referred to an outdated doc which caused the crash.

Initially, I encountered a MissingPluginException with the following message but the video call was working as expected: MissingPluginException(No implementation found for method show RPSystemBroadcastPickerView on channel example_screensharing_ios)

For this error, you also need to copy the MethodChannel implementation on the native side https://github.com/AgoraIO-Extensions/Agora-Flutter-SDK/blob/main/example/ios/Runner/AppDelegate.m#L23-L44

vaibhavbhus commented 3 days ago

You've shared Objective-C code file link. SO I asked ChatGPT to convert the Objective-C code into Swift, I received the following Swift code:

let screensharingIOSChannel = FlutterMethodChannel(name: "example_screensharing_ios", binaryMessenger: controller.binaryMessenger)
screensharingIOSChannel.setMethodCallHandler { (call, result) in
    if #available(iOS 12.0, *) {
        DispatchQueue.main.async {
            if let url = Bundle.main.url(forResource: nil, withExtension: "appex", subdirectory: "PlugIns"),
               let bundle = Bundle(url: url) {

                let picker = RPSystemBroadcastPickerView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
                picker.showsMicrophoneButton = true
                picker.preferredExtension = bundle.bundleIdentifier

                for view in picker.subviews {
                    if let button = view as? UIButton {
                        button.sendActions(for: .allTouchEvents)
                    }
                }
            }
        }
    }
}

After adding this code to my AppDelegate.swift, I encountered the error "No such module 'Flutter'" and I'm unable to build the project.

I have also tried the following code

var screensharingIOSChannel = FlutterMethodChannel(
            name: "example_screensharing_ios",
            binaryMessenger: controller as! FlutterBinaryMessenger)
screensharingIOSChannel.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            if #available(iOS 12.0, *) {
                DispatchQueue.main.async(execute: {
                    if let url = Bundle.main.url(forResource: nil, withExtension: "appex", subdirectory: "PlugIns") {
                        if let bundle = Bundle(url: url) {
                            let picker = RPSystemBroadcastPickerView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
                            picker.showsMicrophoneButton = true
                            picker.preferredExtension = bundle.bundleIdentifier
                            for view in picker.subviews() {
                                if view is UIButton {
                                    (view as? UIButton)?.sendActions(for: .allTouchEvents)
                                }
                            }
                        }
                    }
                })
            }
        })

still facing the same error of no such module found 'Flutter'.

littleGnAl commented 3 days ago

Have you imported the Flutter module like this? https://github.com/AgoraIO-Extensions/Agora-Flutter-SDK/blob/79bf9974541b638fd7105385bece446ae8ab8328/example/ios/Runner/AppDelegate.swift#L2

vaibhavbhus commented 3 days ago

yes

littleGnAl commented 3 days ago

Did you run it in the command line?

vaibhavbhus commented 8 hours ago

yes I also tried to run it from command line and and from xcode on real device

littleGnAl commented 8 hours ago

Can you provide a reproducible demo so we can see what went wrong?