FlutterFire background isolate communication

Issue

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;
  }

}

Solution

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);
  }
}

Answered By – Nitrodon

Answer Checked By – Robin (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *