EventBus is often used in Android development process. EventBus obeys publish/ Subscribe mode. It simplifies the communication between modules and is very practical for project decoupling. Flutter also provides a plugin for event_bus.

EventBus

Source code analysis

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 is T).cast<T>(); } } void fire(event) { streamController.add(event); } void destroy() { _streamController.close(); }}Copy the code

Simple analysis of the source code available, EventBus core is primarily event distribution through the Stream, the initialization will create a StreamController. Broadcast (sync: sync) radio flow; The fire() broadcast method adds events to the StreamController, and on() is the broadcast listener.

Case try

Small dishes try to make a small attempt to switch the theme, trying both EventBus and Provider.

1. Initialize the EventBus

The first tip is to create a global EventBus. Usually there is only one EventBus per application, but if you need multiple event buses you can set sync = false at initialization.

EventBus eventBus = EventBus();
Copy the code
2. EventBus Defines events

Create two custom events, respectively language switch and theme color switch; Use the same method as Android;

class LanguageEvent {
  String languageType;
  LanguageEvent(this.languageType);
}

class ThemeColorEvent {
  Color color;
  ThemeColorEvent(this.color);
}
Copy the code
3. EventBus releases events

EventBus distributes events through fire(Event).

return GestureDetector(
    child: Padding(
        padding: EdgeInsets.symmetric(vertical: 14),
        child: Row(children: <Widget>[
          _itemColorWid(_colorList[index]),
          Expanded(child: dataIndex == 1 ? _itemColorWid(_colorList[index]) : Text(_languageList[index])),
          SizedBox(width: 20),
          Icon(Icons.done)
        ])),
    onTap: () {
      eventBus.fire(dataIndex == 1 ? ThemeColorEvent(_colorList[index]) : LanguageEvent(_languageList[index]));
      Navigator.pop(context);
    });
Copy the code
4. EventBus Receives events

The receiving of EventBus can be monitored by on(event).listen(). Where, if on() can listen to all events or fixed events, the difference is whether to restrict the current broadcast;

Eventbus.on ().listen((event) {if (event is LanguageEvent) {print(' eventBus --> LanguageEvent --> ${event.languageType}'); } else if (event is ThemeColorEvent) { themeColor = event.color; print('EventBus --> ThemeColorEvent --> ${event.color}'); } print('EventBus --> Type --> ${event.runtimeType}'); }); Eventbus.on <LanguageEvent>().listen((event) {print(' eventbus.on <LanguageEvent>() --> ${event.languageType}'); });Copy the code

5. EventBus destroyed

To prevent memory leaks, EventBus is usually destroyed during application destruction.

eventBus.cancel();
eventBus.destroy();
Copy the code

Dart’s runApp(MyApp()) is intended to change ThemeData directly, but MyApp() is StatelessWidget stateless. But updating the UI directly is a little more complicated; At this point, xiao CAI tries to use Provider to switch the theme. The core of Provider is that InheritedWidget can directly update the theme color;

1. Provider defines events
class ThemeColorNotifier with ChangeNotifier { Color themeColor; Color get getThemeColor => themeColor; setThemeColor(getThemeColor) { themeColor = getThemeColor; notifyListeners(); }}Copy the code
2. The Provider sends notifications
_itemClick(dataIndex, index) {
  return Consumer<ThemeColorNotifier>(builder: (context, themeColor, _) {
    return GestureDetector(
        child: Padding(
            padding: EdgeInsets.symmetric(vertical: 14),
            child: Row(children: <Widget>[
              _itemColorWid(_colorList[index]),
              Expanded(child: dataIndex == 1 ? _itemColorWid(_colorList[index]) : Text(_languageList[index])),
              SizedBox(width: 20),
              Icon(Icons.done)
            ])),
        onTap: () {
          themeColor.setThemeColor(_colorList[index]);
          Navigator.pop(context);
        });
  });
}
Copy the code
3. The Provider receives notifications
return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => ThemeColorNotifier()) ], child: Consumer<ThemeColorNotifier>(builder: (context, themeColor, _) { return _mainProviderWid(themeColor); })); _mainProviderWid(themeColor) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: themeColor ! = null ? themeColor.getThemeColor : Colors.blue), home: MyHomePage(title: 'Flutter Demo Home Page'), routes: _commonRoute()); }Copy the code

Xiao CAI tried two ways to switch the theme color in EventBus and Provider, which can be freely selected for different scenes. The most direct feeling is that EventBus is mainly event distribution, only sending/receiving data, more inclined to the data layer, while Provider is actually optimization and encapsulation of InheritedWidget, which can update the UI layer while sending/receiving data.

A small extension

The ListView dialog box was set with the following error:

I/flutter (28408): The following assertion was thrown during performLayout():
I/flutter (28408): RenderShrinkWrappingViewport does not support returning intrinsic dimensions.
I/flutter (28408): Calculating the intrinsic dimensions would require instantiating every child of the viewport, which
I/flutter (28408): defeats the point of viewports being lazy.
I/flutter (28408): If you are merely trying to shrink-wrap the viewport in the main axis direction, you should be able
I/flutter (28408): to achieve that effect by just giving the viewport loose constraints, without needing to measure its
I/flutter (28408): intrinsic dimensions.
Copy the code

Set the width of the Container in the ListView to Double. MaxFinite;

_itemDialog(context, dataIndex) {
  return showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
            title: Row(children: <Widget>[
              Icon(Icons.settings), SizedBox(width: 8),
              Text(dataIndex == 1 ? 'Theme Color' : 'Language')
            ]),
            content: Container(
                width: double.maxFinite,
                child: ListView.builder(
                    itemCount: dataIndex == 1 ? _colorList.length : _languageList.length,
                    physics: ScrollPhysics(),
                    primary: false, shrinkWrap: true,
                    itemBuilder: (BuildContext context, int index) {
                      return _itemClick(dataIndex, index);
                    })));
      });
}
Copy the code


Xiao CAI only tried EventBus in the application without systematic study of the source code, involving is still very superficial; If there are mistakes, please guide!

Source: Little Monk A Ce