EventBus is introduced

The EventBus library is one of the most extensive of the Android EventBus libraries. Check out the Android EventBus library description website

  • Publisher/subscriber pattern
  • Centralized communication
  • Low coupling, simplified component communication

Custom EventBus

We’re going to implement a Flutter version of EventBus! The functions are as follows:

  • Register Subscriber to EventBus
  • Publisher Post Event is distributed to Subscriber
  • EventBus can register many types of events

Demo address entry main_eventbus.dart

The interface definition

// Publisher interface
abstract class IPublisher {
  void post<T>(T event);
}
// Subscriber: function object
typedef ISubscriber<T> = void Function(T event);

// Centralized communication,
IEventBus inherits IPublisher and distributes data
IEventBus register and unregister ISubscriber
abstract class IEventBus extends IPublisher {
  
  void register<T>(ISubscriber<T> subscriber);

  void unregister<T>(ISubscriber<T> subscriber);
}
Copy the code

Interface Implementation 1

Type typeOf<T>() => T;

class MyEventBus1 implements IEventBus {
  // We use map to store our subscribers. Different subscribers may subscribe to different types of events
  Map<Type.List<Function>> map = new Map(a);@override
  void register<T>(ISubscriber<T> subscriber) {
    Type type = typeOf<T>();
    if(! map.containsKey(type)) { map[type] =new List(a); } map[type].add(subscriber); }@override
  void unregister<T>(ISubscriber<T> subscriber) {
    Type type = typeOf<T>();
    if(map.containsKey(type)) { map[type].remove(subscriber); }}/ / release
  @override
  void post<T>(T event) {
    Type type = typeOf<T>();
    if (map.containsKey(type)) {
      var subscribers = map[type];
      subscribers?.forEach((subscriber) => subscriber?.call(event));
    }
  }
}
Copy the code

Interface Implementation 2

class MyEventBus2 implements IEventBus {
  List<Function> subscribers = new List(a);@override
  register<T>(ISubscriber<T> subscriber) {
    if (!subscribers.contains(subscriber)) {
      subscribers.add(subscriber);
    }
  }

  @override
  unregister<T>(ISubscriber<T> subscriber) {
    if(subscribers.contains(subscriber)) { subscribers.remove(subscriber); }}@override
  post<T>(T event) {
    varints = subscribers.whereType<ISubscriber<T>>(); ints? .forEach((subscriber) => subscriber? .call(event)); }}Copy the code

A functional test

class EventX {}

class EventY {}

main() {
  testEventBus(new MyEventBus1());
  print("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
  testEventBus(new MyEventBus2());
}

void testEventBus(IEventBus eventBus) {
  ISubscriber<EventX> subscriber1 = (event) => print(event.toString());
  ISubscriber<EventX> subscriber2 = (event) => print(event.toString());
  eventBus.register(subscriber1);
  eventBus.register(subscriber2);
  eventBus.unregister(subscriber1);

  ISubscriber<EventY> subscriber3 = (event) => print(event.toString());
  ISubscriber<EventY> subscriber4 = (event) => print(event.toString());
  eventBus.register(subscriber3);
  eventBus.register(subscriber4);

  eventBus.post(new EventX());
  eventBus.post(new EventY());
}
Copy the code

The result output is as follows, and the test passes

Instance of 'EventX'
Instance of 'EventY'
Instance of 'EventY'
--------------------
Instance of 'EventX'
Instance of 'EventY'
Instance of 'EventY'
Copy the code

Use custom EventBus

Demand effect drawing

Click the button on each row, and the two numbers on each row increase by one

Code implementation

MyEventBus1 _eventBus = new MyEventBus1();
// First we define event. The first row uses EventA, the second row uses EventB
class BaseEvent { int count = 0; }class EventA extends BaseEvent {}
class EventB extends BaseEvent {}

// Click the button
class ButtonWidget<T extends BaseEvent> extends StatelessWidget {
  final T event;

  const ButtonWidget({Key key, this.event}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("increment"),
      onPressed: _increment,
    );
  }

  // Click handle
  void _increment() {
    if(event ! =null) { event.count++; _eventBus.post(event); }}}// Display numbers
class TextWidget<T> extends StatefulWidget {
  @override
  _TextWidgetState<T> createState() {
    return_TextWidgetState<T>(); }}class _TextWidgetState<T> extends State<TextWidget<T>> {
  int _count = 0;
  ISubscriber<T> _subscriber;

  @override
  void initState() {
    super.initState();
    // Refresh the UI with setState.
    
    _subscriber =
        (event) => setState(() => _count = (event as BaseEvent).count);
   / / register
    _eventBus.register<T>(_subscriber);
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      " $_count ",
      style: TextStyle(fontSize: 18)); }@override
  void dispose() {
    super.dispose();
    // Cancel registration_eventBus.unregister<T>(_subscriber); }}// The page body
class EventBusDemoWidget1 extends StatefulWidget {
  EventBusDemoWidget1({Key key}) : super(key: key);

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

class _EventBusDemoWidget1State extends State<EventBusDemoWidget1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(eventBusDemo1Title),
      ),
      body: Container(
        child: Column(
          children: <Widget>[
            Row(
              children: <Widget>[
                new ButtonWidget(event: new EventA()),
                new TextWidget<EventA>(),
                new TextWidget<EventA>()
              ],
            ),
            Divider(
              color: Colors.grey,
            ),
            Row(
              children: <Widget>[
                new ButtonWidget(event: new EventB()),
                new TextWidget<EventB>(),
                newTextWidget<EventB>() ], ) ], ), ), ); }}Copy the code

Customizing EventBus is complete! Take a look at someone else’s EventBus.

EventBus pub

We found an EventBus library on pub.dev, github address

An Event Bus using Dart Streams for decoupling applications

Use the EventBus

The actual differences from custom EventBus are in ButtonWidget and TextWidget.

class ButtonWidget<T extends BaseEvent> extends StatelessWidget {
  final T event;

  const ButtonWidget({Key key, this.event}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("increment"),
      onPressed: _increment,
    );
  }

  void _increment() {
    if(event ! =null) {
      event.count++;
      print(event.count);
      _eventBus.fire(event);
// _eventBus.post(event);}}}class TextWidget<T> extends StatefulWidget {
  @override
  _TextWidgetState<T> createState() {
    return_TextWidgetState<T>(); }}class _TextWidgetState<T> extends State<TextWidget<T>> {
  int _count = 0;
// ISubscriber
      
        _subscriber;
      
  StreamSubscription<T> _subscriber;

  @override
  void initState() {
    super.initState();
// _subscriber =
// (event) => setState(() => _count = (event as BaseEvent).count);
// _eventBus.register
      
       (_subscriber);
      
    _subscriber = _eventBus
        .on<T>()
        .listen((event) => setState(() => _count = (event as BaseEvent).count));
  }

  @override
  Widget build(BuildContext context) {
    print(typeOf<T>());
    return Text(
      " $_count ",
      style: TextStyle(fontSize: 18)); }@override
  void dispose() {
    super.dispose();
    // _eventBus.unregister
      
       (_subscriber);
      _subscriber.cancel(); }}Copy the code

EventBus library source code

class EventBus {
  StreamController _streamController;

  StreamController get streamController => _streamController;

  EventBus({bool sync = false})
      : _streamController = StreamController.broadcast(sync: sync);

  EventBus.customController(StreamController controller)
      : _streamController = controller;

  Stream<T> on<T>() {
    if (T == dynamic) {
      return streamController.stream;
    } else {
      return streamController.stream.where((event) => event isT).cast<T>(); }}void fire(event) {
    streamController.add(event);
  }

  voiddestroy() { _streamController.close(); }}Copy the code

The Stream of EventBus library

Dart comes with a Stream that handles asynchronous events. Stream is the core API of the Dart: Async library and provides excellent support for asynchrony. Here is a simple texture to feel the Stream.


return streamController.stream.where((event) => event is T).cast<T>();
Copy the code

CastStream(_WhereStream(_BroadcastStream)) returns a Stream that has two layers of CastStream(_WhereStream(_BroadcastStream)) to listen for generic events.

_WhereStream handle first


  class _WhereStream<T> extends _ForwardingStream<T.T> {
  final _Predicate<T> _test;

  _WhereStream(Stream<T> source, bool test(T value))
      : _test = test,
        super(source);

    //_test => event is T
  void _handleData(T inputEvent, _EventSink<T> sink) {
    bool  satisfies = _test(inputEvent);
    if(satisfies) { sink._add(inputEvent); }}}Copy the code

CastStreamSubscription actually does type conversions

class CastStreamSubscription<S.T> implements StreamSubscription<T> {
  final StreamSubscription<S> _source;

  /// User's data handler. May be null.
  void Function(T) _handleData;

  CastStreamSubscription(this._source) {
    _source.onData(_onData);
  }
  void _onData(S data) {
    if (_handleData == null) return;
    T targetData = data as T;
    _zone.runUnaryGuarded(_handleData, targetData);
  }
Copy the code

Afterword.

So far, we’ve encountered streams in the Provider library StreamProvider, and we’ve encountered Streams in the EventBus library with the excellent asynchronous programming models Stream and Future that Dart comes with. Stream has a good asynchronous model that nourishes a large component library Provider/BLoC/ Flutter_redux /Rxdart/fish-redux. It is then necessary to analyze the Stream.