Search code examples
flutterdartflutter-getx

Unable to get the latest value from GetX Controller


I am developing a voice call app using Agora. I use FCM to show calls on devices. I send the image and other required data using silent notification from FCM. So, whenever I receive background notification then this is how I handle the data and show an incoming call notification using this package:

Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {

  callStateController.isIncoming.value=true;
  callStateController.callerName.value=message.data['callerName'];
  callStateController.callerID.value=message.data['callerID'];
  callStateController.callerImage.value=message.data['callerImage'];
  callStateController.callerUsername.value=message.data['callerUsername'];

  var incoming = <String, dynamic>{
    'id': message.data['callerID'],
    'nameCaller': message.data['callerName'],
    'appName': 'Callkit',
    'avatar': message.data['callerImage'],
    'handle': '',
    'type': 0,
    'duration': 30000,
    'extra': <String, dynamic>{'userId': '1a2b3c4d'},
    'headers': <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'},
    'android': <String, dynamic>{
      'isCustomNotification': true,
      'isShowLogo': false,
      'ringtonePath': 'ringtone_default',
      'backgroundColor': '#0955fa',
      //'backgroundUrl': 'https://i.pravatar.cc/500',
      'actionColor': '#4CAF50'
    }};
  await FlutterCallkitIncoming.showCallkitIncoming(incoming);

}

And I use call lisner to get the current state of a call, eg: accepted or rejected and then push to the design screen with the data I received from FCM:

Future<void> listenerEvent() async {
    String img = callStateController.callerImage.value;
    String name = callStateController.callerName.value;
    String id = callStateController.callerID.value;
    String usrnm = callStateController.callerUsername.value;
    print(callStateController.callerID.value);

    try {
      FlutterCallkitIncoming.onEvent.listen((event) {

        switch (event!.name) {
          case CallEvent.ACTION_CALL_INCOMING:
            print('INCOMING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
            break;
          case CallEvent.ACTION_CALL_START:
          // TODO: started an outgoing call
          // TODO: show screen calling in Flutter
            break;
          case CallEvent.ACTION_CALL_ACCEPT:
            print('accepted');
            print(callStateController.callerImage.value);
            Get.offAll(()=>Incoming(
                userName: name,
                userImage: img,
                userID: id,
                userUsername: usrnm));
            break;
          case CallEvent.ACTION_CALL_DECLINE:
            print('rejected');
            break;
          case CallEvent.ACTION_CALL_ENDED:
          // TODO: ended an incoming/outgoing call
            break;
          case CallEvent.ACTION_CALL_TIMEOUT:
          // TODO: missed an incoming call
            break;
          case CallEvent.ACTION_CALL_CALLBACK:
          // TODO: only Android - click action `Call back` from missed call notification
            break;
          case CallEvent.ACTION_CALL_TOGGLE_HOLD:
          // TODO: only iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_MUTE:
          // TODO: only iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_DMTF:
          // TODO: only iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_GROUP:
          // TODO: only iOS
            break;
          case CallEvent.ACTION_CALL_TOGGLE_AUDIO_SESSION:
          // TODO: only iOS
            break;
        }
        print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
      });
    } on Exception {}
  }

The listener method runs on the initState of my HomePage. The problem here is that the listener starts with the app so it always gets the initial values(which is null) of the controller and I get the data later. So in this case, how do I fetch the latest values from getx after a call is accepted or rejected then push to the accepted/rejected call screen with the required data?

Then I store this value in a GetX Controller:

class CallState extends GetxController{

  RxBool isIncoming = false.obs;
  RxString callerName = ''.obs;
  RxString callerID = ''.obs;
  RxString callerImage = ''.obs;
  RxString callerUsername = ''.obs;

}

Solution

  • Found the culprit in the documentation of Firebase background messages::

    Since the handler runs in its own isolate outside your applications context, it is not possible to update application state or execute any UI impacting logic. You can however perform logic such as HTTP requests, IO operations (updating local storage), communicate with other plugins etc.