写点什么

【Flutter 专题】99 初识 EventBus

发布于: 2 小时前
【Flutter 专题】99 初识 EventBus

      小菜在 Android 开发过程中经常会用到 EventBus 事件分发机制,EventBus 遵从 publish/subscribe 模式,即发布/订阅模式;简化了模块之间通信,对于项目的解耦很实用;而 Flutter 也提供了相应的 event_bus 插件,今天小菜学习尝试一下;

EventBus

源码分析

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(); }}
复制代码


      简单分析源码可得,EventBus 核心主要是通过 Stream 来进行事件分发的,其中初始化时会创建一个 StreamController.broadcast(sync: sync) 广播流;fire() 广播发送方法主要是向 StreamController 中添加事件,on() 为广播监听,都是对 Stream 流操作;

案例尝试

      小菜尝试做一个主题切换的小尝试,同时尝试了 EventBusProvider 两种方式;

1. EventBus 初始化

      小菜首先创建一个全局的 EventBus,通常每个应用只有一个事件总线,但如果需要多个事件总线的话可以在初始化时设置 sync = false


EventBus eventBus = EventBus();
复制代码
2. EventBus 定义事件

      小菜创建两个自定义事件,分别为语言切换和主题色切换;使用方法和 Android 一致;


class LanguageEvent {  String languageType;  LanguageEvent(this.languageType);}
class ThemeColorEvent { Color color; ThemeColorEvent(this.color);}
复制代码
3. EventBus 发布事件

      EventBus 通过 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);    });
复制代码
4. EventBus 接收事件

      对于 EventBus 的接收可以通过 on(event).listen() 来监听;其中若 on() 可以监听所有事件也可以监听固定的事件,区别是是否限制当前广播;


// 监听所有广播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}');});
复制代码



5. EventBus 销毁

      为了防止内存泄漏,一般在应用销毁时都需要对 EventBus 进行销毁;


eventBus.cancel();eventBus.destroy();
复制代码


      小菜预想的是在 main.dartrunApp(MyApp()) 中直接更改 ThemeData,但是 MyApp()StatelessWidget 无状态类型的,虽然可以通过 EventBus 监听切换主题,但是直接更新 UI 相对复杂一些;此时小菜尝试用 Provider 来进行主题切换,Provider 核心是 InheritedWidget 可以直接更新主题色;

1. Provider 定义事件
class ThemeColorNotifier with ChangeNotifier {  Color themeColor;
Color get getThemeColor => themeColor;
setThemeColor(getThemeColor) { themeColor = getThemeColor; notifyListeners(); }}
复制代码
2. Provider 发送通知
_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);        });  });}
复制代码
3. Provider 接收通知
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());}
复制代码



      小菜尝试了 EventBusProvider 两种方式进行主题色切换,对于不同的场景可以自由选择;给小菜最直接的感觉是 EventBus 主要是事件分发,只发送/接收数据,更偏向于数据层,而 Provider 实际是对 InheritedWidget 的优化和封装,可以在发送/接收数据同时更新 UI 层;

小扩展

      小菜在测试过程中设置 ListView 对话框时出现如下错误:


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, whichI/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 ableI/flutter (28408): to achieve that effect by just giving the viewport loose constraints, without needing to measure itsI/flutter (28408): intrinsic dimensions.
复制代码



      小菜测试可以设置 ListViewContainer 宽或高即可,也可以将 ListView 包裹在容器中并为其设置宽度为 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);                    })));      });}
复制代码





      小菜仅是在应用中尝试了 EventBus 并未对源码进行系统的研究,涉及还很浅显;如有错误请多多指导!


来源: 阿策小和尚

发布于: 2 小时前阅读数: 2
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
【Flutter 专题】99 初识 EventBus