LONGI
LONGI

Reputation: 11443

How to add several listener to Flutter.EventChannel?

I'm building a Flutter-Plugin which offers several UI-Widgets to interact with a native (Android/iOS) library. Updates and/or state changes of the library to the Flutter side are delegated with Events, which are delegated to Flutter over the Flutter EventChannel.

This works fine, however I want to add several listeners to the same channel. But every time I want to register a second listener, the first one gets canceled (StreamHandler.onCancel() called).

private var channel01:EventChannel.EventSink ?= null
private var channel02:EventChannel.EventSink ?= null
//...
private var channel06:EventChannel.EventSink ?= null

//One EventChannel for every UI-Widget?!?!
EventChannel(registrar.messenger(), EVENT_CHANNEL_MAIN_SCREEN, JSONMethodCodec.INSTANCE).setStreamHandler(object : EventChannel.StreamHandler {
   override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
       L.d("Listener attached for channel 01")
       postEvent(MyEvent("Send some data on attach"))
       channel01 = events
    }

    override fun onCancel(p0: Any?) {
       L.d("Listener detached for channel 01")
       channel01 = null
     }
})

//Trigger some data via flutter stream from Android/iOS to Flutter
fun postEvent(event: MyEvent) {
    activity.runOnUiThread {
        channel01?.success(event)
        channel02?.success(event) 
    }
}

So currently I'm using one EventChannel for every UI-Widget, but I guess there are better ways?

By the way, I used an Inherited Widget as a DataProvider in Flutter, which was the only listener to the EventChannel and was delegating the changes down the tree, however this is not possible due to "architecture guidelines"

Question

Upvotes: 1

Views: 1374

Answers (2)

Dominik Roszkowski
Dominik Roszkowski

Reputation: 2543

The approach that I'm following in such cases is to expose the event channel on the Flutter side using stream controller (in this case a BehaviorSubject as it will report the latest value to any new listeners).

A pseudo code might look like that:

import 'package:rxdart/rxdart.dart';

StreamSubscription? _heartRateSub;
final _streamController = BehaviorSubject<MyModel>();

@override
Stream<MyModel> myStream() {
  _streamSub ??= heartRateChannel.receiveBroadcastStream().listen((dynamic event) {
    if (event is String) {
      // process the event
      _streamController.add(event);
    }
  })
  ..onError((Object error) {
    // handle the error
  });

  _streamController.onCancel = () async {
    await _streamSub?.cancel();
    _streamSub = null;
  };

  return _streamController.stream;
}

This way the subscription to the event channel is maintained once in my Dart plugin code. Once all the listeners cancel subscriptions to the stream, the event channel subscription is also cancelled.

Upvotes: 0

LONGI
LONGI

Reputation: 11443

It is not possible to attach several listeners to one channel.

Suitable solution: Attach the EventChannel in one (Inherited-)Widget, lift this widget up in the tree and use flutter setState logic to update your widgets.

Upvotes: 3

Related Questions