Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled Exception: LateInitializationError: Field 'requestPort' has not been initialized. Agora flutter #860

Closed
FaizanAhmad127 opened this issue Jan 2, 2023 · 15 comments

Comments

@FaizanAhmad127
Copy link

I am working on a large-scale project and integrating video calls into it. So I made a separate project just for practice and I achieved good results. The group calling worked perfectly on both android and IOS. But then I integrated the same code in my large-scale project which uses firebase as a backend and when I navigate to the video screen it gives me an error saying "Unhandled Exception: LateInitializationError: Field 'requestPort' has not been initialized". The Agora console is on Testing for now and the channel wasn't expired just in case you guys are wondering. As I said it works perfectly in a separate project.

The app uses agora_rtc_engine: ^6.1.0 and defined in pubspec.yaml file

import 'package:flutter/material.dart';
import 'package:logger/logger.dart';

class VideoCallPage extends StatefulWidget {
  const VideoCallPage({Key? key}) : super(key: key);

  @override
  VideoCallPageState createState() => VideoCallPageState();
}

class VideoCallPageState extends State<VideoCallPage> {
  static final _users = <int>[];
  Logger logger = Logger();
  bool muted = false;
  late RtcEngine _engine;
  bool isRigning = true;
  bool isSpeakerOn = true;
  final String channelName = 'video';
  final String appID = 'xxx';
  final String tokenAudio ='xxx';

  @override
  void dispose() {
    _dispose();
    super.dispose();
  }

  Future<void> _dispose() async {
    _users.clear();
    await _engine.leaveChannel();
    await _engine.stopPreview();
    await _engine.release();
  }

  @override
  void initState() {
    super.initState();
    // initialize agora sdk
    initialize();
  }

  Future<void> initialize() async {
    logger.i('Initialize');
    if (appID.isEmpty) {
      setState(() {
        logger.e(
          'APP_ID missing, please provide your APP_ID in settings.dart',
        );
        logger.e('Agora Engine is not starting');
      });
      return;
    }
    await _initAgoraRtcEngine();
    _addAgoraEventHandlers();
    onOffSpeaker();
    await _engine.joinChannel(
        token: tokenAudio,
        channelId: channelName,
        uid: 0,
        options: const ChannelMediaOptions(
            channelProfile: ChannelProfileType.channelProfileCommunication,
            clientRoleType: ClientRoleType.clientRoleBroadcaster));
     
  }

  Future<void> _initAgoraRtcEngine() async {
    logger.i('_initAgoraRtcEngine');
    //create the engine
    _engine = createAgoraRtcEngine();
    logger.i('RtcEngineContext'); //this is printed 
    await _engine.initialize(RtcEngineContext(
      appId: appID,
    ));
    logger.i('enablbing video'); //this isn't printed
    await _engine.enableVideo();
    // await _engine.setVideoEncoderConfiguration(
    //   const VideoEncoderConfiguration(
    //     dimensions: VideoDimensions(width: 640, height: 360),
    //     frameRate: 15,
    //     bitrate: 0,
    //   ),
    // );
    await _engine.startPreview();
  }

  void _addAgoraEventHandlers() {
    _engine.registerEventHandler(RtcEngineEventHandler(
      onError: (ErrorCodeType errorCodeType, String value) {
        if (mounted) {
          setState(() {
            final info = 'onError: ${errorCodeType.name}';
            logger.e(info);
          });
        }
      },
      onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
        setState(() {
          final info =
              'onJoinChannel: ${connection.channelId}, uid: ${connection.localUid}';
          logger.i(info);
        });
      },
      onLeaveChannel: (RtcConnection rtcConnection, RtcStats rtcStats) {
        setState(() {
          logger.i('onLeaveChannel');
          _users.clear();
        });
      },
      onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
        setState(() {
          isRigning = false;
          final info = 'remoteUserJoined: $remoteUid';
          logger.i(info);
          _users.add(remoteUid);
        });
      },
      onUserOffline: (RtcConnection connection, int remoteUid,
          UserOfflineReasonType reason) {
        setState(() {
          final info =
              'remoteUserOffline: $remoteUid , reason: ${reason.index}';
          logger.i(info);
          _users.remove(remoteUid);
        });
      },
      onFirstRemoteVideoFrame: (connection, uid, width, height, elapsed) {
        setState(() {
          final info = 'firstRemoteVideoFrame: $uid';
          logger.i(info);
        });
      },
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Agora Group Video Calling'),
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Stack(
          children: <Widget>[
            Opacity(
              opacity: isRigning ? 0.2 : 1,
              child: _viewRows(),
            ),
            _toolbar(),
            if (isRigning)
              Positioned(
                  top: 100,
                  left: MediaQuery.of(context).size.width * 0.3,
                  right: MediaQuery.of(context).size.width * 0.3,
                  child: Center(
                    child: Text(
                      'Ringing...',
                      style: TextStyle(color: Colors.white, fontSize: 30),
                    ),
                  ))
          ],
        ),
      ),
    );
  }

  /// Helper function to get list of native views
  List<Widget> _getRenderViews() {
    final List<StatefulWidget> list = [];
    list.add(AgoraVideoView(
        controller: VideoViewController(
      rtcEngine: _engine,
      canvas: const VideoCanvas(uid: 0),
    )));
    _users.forEach((int uid) => list.add(AgoraVideoView(
          controller: VideoViewController.remote(
            rtcEngine: _engine,
            canvas: VideoCanvas(uid: uid),
            connection: RtcConnection(channelId: channelName),
          ),
        )));
    return list;
  }

  /// Video view wrapper
  Widget _videoView(view) {
    return Expanded(child: Container(child: view));
  }

  /// Video view row wrapper
  Widget _expandedVideoRow(List<Widget> views) {
    final wrappedViews = views.map<Widget>(_videoView).toList();
    return Expanded(
      child: Row(
        children: wrappedViews,
      ),
    );
  }

  Widget _viewRows() {
    final views = _getRenderViews();
    switch (views.length) {
      case 1:
        return Container(
            child: Column(
          children: <Widget>[_videoView(views[0])],
        ));
      case 2:
        return Container(
            child: Column(
          children: <Widget>[
            _expandedVideoRow([views[0]]),
            _expandedVideoRow([views[1]])
          ],
        ));
      case 3:
        return Container(
            child: Column(
          children: <Widget>[
            _expandedVideoRow(views.sublist(0, 2)),
            _expandedVideoRow(views.sublist(2, 3))
          ],
        ));
      case 4:
        return Container(
            child: Column(
          children: <Widget>[
            _expandedVideoRow(views.sublist(0, 2)),
            _expandedVideoRow(views.sublist(2, 4))
          ],
        ));
      default:
    }
    return Container();
  }

  Widget _toolbar() {
    return Container(
      alignment: Alignment.bottomCenter,
      padding: const EdgeInsets.symmetric(vertical: 48),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RawMaterialButton(
            onPressed: onOffSpeaker,
            child: Icon(
              isSpeakerOn ? Icons.volume_up_sharp : Icons.volume_off,
              color: isSpeakerOn ? Colors.white : Colors.blueAccent,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: isSpeakerOn ? Colors.blueAccent : Colors.white,
            padding: const EdgeInsets.all(12.0),
          ),
          RawMaterialButton(
            onPressed: _onToggleMute,
            child: Icon(
              muted ? Icons.mic_off : Icons.mic,
              color: muted ? Colors.white : Colors.blueAccent,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: muted ? Colors.blueAccent : Colors.white,
            padding: const EdgeInsets.all(12.0),
          ),
          RawMaterialButton(
            onPressed: () => _onCallEnd(context),
            child: Icon(
              Icons.call_end,
              color: Colors.white,
              size: 35.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: Colors.redAccent,
            padding: const EdgeInsets.all(15.0),
          ),
          RawMaterialButton(
            onPressed: _onSwitchCamera,
            child: Icon(
              Icons.switch_camera,
              color: Colors.blueAccent,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: Colors.white,
            padding: const EdgeInsets.all(12.0),
          )
        ],
      ),
    );
  }

  void _onToggleMute() {
    setState(() {
      muted = !muted;
    });
    _engine.muteLocalAudioStream(muted);
  }

  void _onCallEnd(BuildContext context) {
    Navigator.pop(context);
  }

  void _onSwitchCamera() {
    _engine.switchCamera();
  }

  Future onOffSpeaker() async {
    setState(() {
      isSpeakerOn = !isSpeakerOn;
    });
    await _engine.setEnableSpeakerphone(isSpeakerOn);
  }
}

@JimmyGoGoGo
Copy link

same here

@littleGnAl
Copy link
Contributor

@FaizanAhmad127
Can you share the more stack trace of the error log?

@littleGnAl littleGnAl added the waiting for customer response waiting for customer response, or closed by no-reponse bot label Jan 3, 2023
@tselmeggkh
Copy link

tselmeggkh commented Jan 3, 2023

Having the same issue here. It's working fine after one call, this exception is throwing only on the first initialization for me.

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: LateInitializationError: Field 'requestPort' has not been initialized.
#0      _ApiCallExecutor.requestPort (package:agora_rtc_engine/src/impl/api_caller.dart)
#1      _ApiCallExecutor.callIrisApiWithUin8ListAsync (package:agora_rtc_engine/src/impl/api_caller.dart:273:5)
#2      ApiCaller.callIrisApiWithUin8ListAsync (package:agora_rtc_engine/src/impl/api_caller.dart:110:10)
#3      ApiCaller.callIrisApi (package:agora_rtc_engine/src/impl/api_caller.dart:118:12)
#4      RtcEngineImpl.enableAudio (package:agora_rtc_engine/src/binding/agora_rtc_engine_impl.dart:723:25)
#9      AgoraRtmChannel._eventListener (package:agora_rtm/src/agora_rtm_channel.dart:60:33)
#10     _RootZone.runUnaryGuarded (dart:async/zone.dart:1586:10)
#11     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
#12     _DelayedData.perform (dart:async/stream_impl.dart:515:14)
#13     _PendingEvents.handleNext (dart:async/stream_impl.dart:620:11)
#14     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:591:7)
#15     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#16     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)

@FaizanAhmad127
Copy link
Author

@FaizanAhmad127 Can you share the more stack trace of the error log?

E/flutter (10089): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: LateInitializationError: Field 'requestPort' has not been initialized.
E/flutter (10089): #0      _ApiCallExecutor.requestPort (package:agora_rtc_engine/src/impl/api_caller.dart)
package:agora_rtc_engine/…/impl/api_caller.dart:1
E/flutter (10089): #1      _ApiCallExecutor.callIrisApiWithUin8ListAsync
package:agora_rtc_engine/…/impl/api_caller.dart:273
E/flutter (10089): #2      ApiCaller.callIrisApiWithUin8ListAsync
package:agora_rtc_engine/…/impl/api_caller.dart:110
E/flutter (10089): #3      ApiCaller.callIrisApi
package:agora_rtc_engine/…/impl/api_caller.dart:118
E/flutter (10089): #4      RtcEngineImpl.setupLocalVideo
package:agora_rtc_engine/…/binding/agora_rtc_engine_impl.dart:706
E/flutter (10089): #5      VideoViewControllerBaseMixin.setupView
package:agora_rtc_engine/…/impl/video_view_controller_impl.dart:125
E/flutter (10089): #6      _AgoraRtcRenderPlatformViewState._setupVideo
package:agora_rtc_engine/…/impl/agora_video_view_impl.dart:111
E/flutter (10089): <asynchronous suspension>

@github-actions github-actions bot removed the waiting for customer response waiting for customer response, or closed by no-reponse bot label Jan 3, 2023
@FaizanAhmad127
Copy link
Author

I think the method createAgoraRtcEngine() should be async just like in previous versions we had await RtcEngine.create(appID). When the app gets big then it only gives the error else it's fine.

@FaizanAhmad127
Copy link
Author

Solved.
I created a service class in which I put this piece of code

class Foo
{
    late RtcEngine engine;

    Foo()
    {
        engine = createAgoraRtcEngine();
        engine.initialize(RtcEngineContext(
        appId: 'your_app_id', 
        logConfig: LogConfig(level: LogLevel.logLevelError))); 
      }

}

and used get it package to achieve a singleton pattern which is also suggested by Agora while creating RTCEngine so we can have only one instance of it. And then register the service class at the very beginning of main() like this:

GetIt locator = GetIt.instance;
locator.registerLazySingleton(() => Foo()); //Foo should be replace by your service class

Then I used the engine variable inside my video call screen,like this

final foo=locator.get<Foo>();
RtcEngine engine=foo.engine;

and the rest of the code remains the same.
I don't really get the depth of the issue. A proper solution must be provided.

@littleGnAl
Copy link
Contributor

You should call the RtcEngine APIs after initialize, cause the initialize is async function, so you need check the initialized state:

class VideoCallPageState extends State<VideoCallPage> {

  late RtcEngine _engine;
  bool _isInitialized = false;

  @override
  void initState() {
    super.initState();
    // initialize agora sdk
    initialize();
  }

  Future<void> initialize() async {
    _engine = createAgoraRtcEngine();
    await _engine.initialize(RtcEngineContext(
    appId: 'your_app_id', 
    logConfig: LogConfig(level: LogLevel.logLevelError))); 
    setState((){
      _isInitialized = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (!_isInitialized) {
      return Container();
    }

    // Only show AgoraVideoView after initialized

    ...
  }
}

@littleGnAl littleGnAl added the waiting for customer response waiting for customer response, or closed by no-reponse bot label Jan 4, 2023
@FaizanAhmad127
Copy link
Author

Thanks, yes I know that and I was doing the exact same thing but it led me to a response port error in my large-scale project. I'm doing the same now but in a service class that is run before other service classes. If I run your code after other services it gives me an error, which means I have to create and initialize the engine before everything else in my app.

@github-actions github-actions bot removed the waiting for customer response waiting for customer response, or closed by no-reponse bot label Jan 4, 2023
@littleGnAl
Copy link
Contributor

This can be enhanced, but you should control the initializing order on your own at this time.

@FaizanAhmad127
Copy link
Author

Yes, I have done that. But any luck on finding why we get responsePort error?

@littleGnAl
Copy link
Contributor

Since the responsePort is initiated in the RtcEngine.initialize function, if any APIs are called before RtcEngine.initialize will be caused this error.

@qaTomosia
Copy link

i'm facing the same problem. if you can fix, let me know. thanks man

@adiShinwari
Copy link

@littleGnAl iam getting the same error and the same steps to reproduce it. If initialization is the case, then it should give the error in the first call also.

@johnhcolani
Copy link

Please same here, after upgrading my Flutter today,
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: LateInitializationError: Field 'deviceName' has not been initialized.
#0 _BluetoothPageState.deviceName (package:phillips_smart_charger/presentation/bluetoothpage/bluetoothPage.dart)
#1 _BluetoothPageState._startDiscovery (package:phillips_smart_charger/presentation/bluetoothpage/bluetoothPage.dart:57:19)
#2 _BluetoothPageState.initState (package:phillips_smart_charger/presentation/bluetoothpage/bluetoothPage.dart:41:5)
#3 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5101:55)
#4 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4944:5)
#5 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3953:16)
#6 Element.updateChild (package:flutter/src/widgets/framework.dart:3682:18)
#7 SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6377:14)

@github-actions
Copy link
Contributor

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please raise a new issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants