Flutter 99: 初识 EventBus

简介: 0 基础学习 Flutter,第九十九步:初识 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, 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.

      小菜测试可以设置 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 并未对源码进行系统的研究,涉及还很浅显;如有错误请多多指导!

来源: 阿策小和尚

目录
相关文章
|
4天前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
4天前
|
开发框架 Dart 前端开发
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
【4月更文挑战第30天】对比 Flutter(Dart,强类型,Google支持,快速热重载,高性能渲染)与 React Native(JavaScript,庞大生态,热重载,依赖原生渲染),文章讨论了开发语言、生态系统、性能、开发体验、学习曲线、社区支持及项目选择因素。两者各有优势,选择取决于项目需求、团队技能和长期维护考虑。参考文献包括官方文档和性能比较文章。
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
|
2天前
|
前端开发 C++ 容器
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
|
2天前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
2天前
|
XML Dart Java
Flutter插件开发之APK自动安装,字节跳动Android岗面试题
Flutter插件开发之APK自动安装,字节跳动Android岗面试题
|
3天前
|
Java Android开发 设计模式
flutter音视频开发,Android开发需要学什么
flutter音视频开发,Android开发需要学什么
|
4天前
|
Dart 前端开发 测试技术
【Flutter前端技术开发专栏】Flutter开发中的代码质量与重构实践
【4月更文挑战第30天】随着Flutter在跨平台开发的普及,保证代码质量成为开发者关注的重点。优质代码能确保应用性能与稳定性,提高开发效率。关键策略包括遵循最佳实践,编写可读性强的代码,实施代码审查和自动化测试。重构实践在项目扩展时尤为重要,适时重构能优化结构,降低维护成本。开发者应重视代码质量和重构,以促进项目成功。
【Flutter前端技术开发专栏】Flutter开发中的代码质量与重构实践
|
4天前
|
存储 缓存 监控
【Flutter前端技术开发专栏】Flutter中的列表滚动性能优化
【4月更文挑战第30天】本文探讨了Flutter中优化列表滚动性能的策略。建议使用`ListView.builder`以节省内存,避免一次性渲染所有列表项。为防止列表项重建,可使用`UniqueKey`或`ObjectKey`。缓存已渲染项、减少不必要的重绘和异步加载大数据集也是关键。此外,选择轻量级组件,如`StatelessWidget`,并利用Flutter DevTools监控性能以识别和解决瓶颈。持续测试和调整以提升用户体验。
【Flutter前端技术开发专栏】Flutter中的列表滚动性能优化
|
4天前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
|
4天前
|
Dart 前端开发 开发者
【Flutter前端技术开发专栏】Flutter中的性能分析工具Profiler
【4月更文挑战第30天】Flutter Profiler是用于性能优化的关键工具,提供CPU、GPU、内存和网络分析。它帮助开发者识别性能瓶颈,如CPU过度使用、渲染延迟、内存泄漏和网络效率低。通过实时监控和分析,开发者能优化代码、减少内存占用、改善渲染速度和网络请求,从而提升应用性能和用户体验。定期使用并结合实际场景与其它工具进行综合分析,是实现最佳实践的关键。
【Flutter前端技术开发专栏】Flutter中的性能分析工具Profiler