Son Tieu
Son Tieu

Reputation: 527

FlutterFire background isolate communication

i'm developing a VoIP app and having problem with background handler. What i want is: sending push with type "call" then the app will show Call UI, later if receive push type "hangup" , the app will close that call UI.

In background handler i'm using a singleton global class to inform hang up event with a StreamController and CallScreen widget will listen to that stream to close itself.

Then i found out that flutterfire will start another isolate for background handler (turn screen off) , so it will create another singleton class -> i can't close my CallScreen UI with this new singleton class.

Is it possible to do something like this with flutterfire's background isolate ?

Example pseudo code:

class SingletonGlobal {
  /// singleton class

  final hangUpStreamController = StreamController<HangUpEvent>.broadcast();

  void addHangupEvent(HangUpEvent event) {
    hangUpStreamController.add(event);
  }
}

class CallScreen extends StatefulWidget {
  //// ...
    @override
    void initState() {
      SingletonGlobal().hangUpStreamController.stream.listen((event) => closeCallUI(event));
    }
}

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  await appInit();

  if (message.data.type = 'hangup') {
    SingletonGlobal().addHangupEvent(hangUpEvent);
}

Update

I tried Nitrodon's solution. but as I mentioned, _firebaseMessagingBackgroundHandler will create another instance of my SingleGlobal class. i don't know why.

"SingletonGlobal#internal" printed again after receive firebase background handler. that's mean it recreated SingletonGlobal.

class SingletonGlobal {
  /// singleton class
  static SingletonGlobal? _instance;
  final _receivePort = ReceivePort();

  factory SingletonGlobal() => _instance ?? SingletonGlobal._internal();


  SingletonGlobal._internal() {
    print('SingletonGlobal#internal');
    IsolateNameServer.registerPortWithName(_receivePort.sendPort, 'global_singleton');
    _receivePort.listen((message) => hangUpStreamController.add);

    _instance = this;
  }

}

Upvotes: 1

Views: 2939

Answers (2)

Nitrodon
Nitrodon

Reputation: 3445

There are ways to communicate between isolates. In this case, since you only need to listen to events on the main isolate, you can register a port to receive hangup events from the background isolate:

class SingletonGlobal {
  // Whatever private constructor you're using for this singleton
  SingletonGlobal._() {
    IsolateNameServer.registerPortWithName(_receivePort.sendPort, 'some port name');
    _receivePort.listen(hangUpStreamController.add);
  }

  final hangUpStreamController = StreamController<HangUpEvent>.broadcast();

  final _receivePort = ReceivePort();

  void addHangupEvent(HangUpEvent event) {
    hangUpStreamController.add(event);
  }
}

void _firebaseMessagingBackgroundHandler(RemoteMessage message) {
  // Do not try to access SingletonGlobal here.

  if (message.data.type = 'hangup') {
    // Since the background isolate is still in the same process, you
    // can send objects (which are copied) instead of just basic messages.
    IsolateNameServer.lookupPortByName('some port name')?.send(hangUpEvent);
  }
}

Upvotes: 5

Hichem Benali
Hichem Benali

Reputation: 411

You have to use onMessageOpenedApp event. avoid using any other notifications plugins as it will ignore this event. By the way this event only works when the app is closed and it gets opened by the notification from FCM plugin. it will give you RemoteMessage object which from you can retrieve the message data.

Upvotes: 0

Related Questions