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

来源: 阿策小和尚

目录
相关文章
|
2月前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
19天前
|
开发框架 前端开发 测试技术
Flutter开发常见问题解答
Flutter开发常见问题解答
|
2月前
|
前端开发 C++ 容器
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
|
8天前
|
开发框架 移动开发 Android开发
构建高效移动应用:探索Flutter开发框架
【6月更文挑战第28天】随着移动设备的普及,用户对移动应用的需求日益增长。开发者面临着在众多平台间提供无缝体验的挑战。本文深入探讨了Flutter框架如何通过其跨平台特性、热重载功能以及丰富的组件库简化移动应用的开发流程,同时确保高性能和优雅的用户界面设计。
16 2
|
2月前
|
Dart 安全
简化代码、提高效率:Dart和Flutter开发小技巧
在日常开发中,我们常常会使用一些常用的技巧或语法糖,以简化代码、提高开发效率。本文将分享一些在Dart和Flutter中常用的小贴士,帮助你更轻松地编写优雅高效的代码。
简化代码、提高效率:Dart和Flutter开发小技巧
|
26天前
|
Dart 监控 测试技术
在Flutter开发中,注重代码质量与重构实践显得尤为重要
【6月更文挑战第11天】随着Flutter在跨平台开发的普及,保持高质量代码成为开发者关注的重点。良好的代码质量关乎应用性能、稳定性和开发效率。为提升Flutter代码质量,开发者应遵循最佳实践,编写可读性高的代码,实施代码审查和自动化测试。重构实践在应对代码复杂性时也至关重要,包括识别重构时机、制定计划、逐步操作及利用重构工具。注重代码质量和重构是Flutter开发成功的关键。
36 3
|
4天前
|
Dart Android开发 iOS开发
flutter插件开发
flutter插件开发
|
18天前
|
移动开发 小程序 安全
基础入门-APP架构&小程序&H5+Vue语言&Web封装&原生开发&Flutter
基础入门-APP架构&小程序&H5+Vue语言&Web封装&原生开发&Flutter
|
2月前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
|
24天前
|
Dart 前端开发 JavaScript
Flutter for Web:跨平台移动与Web开发的新篇章
Flutter for Web是Google的开源UI工具包Flutter的延伸,用于构建高性能、高保真的跨平台应用,包括Web。它基于Dart语言和Flutter的核心框架,利用Skia渲染引擎通过WebAssembly在Web上运行。开发流程包括安装SDK、创建项目、编写Dart代码和部署。性能优化涉及减少渲染开销、代码压缩等。与传统Web框架相比,Flutter for Web在开发效率和性能上有优势,但兼容性和生态系统尚待完善。
21 0