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

来源: 阿策小和尚

目录
相关文章
|
1月前
|
Android开发 iOS开发 容器
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
|
5月前
|
开发框架 前端开发 测试技术
Flutter开发常见问题解答
Flutter开发常见问题解答
|
28天前
|
开发者
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter混合开发支持两种模式:1) 基于har包,便于主项目开发者无需关心Flutter细节,但不支持热重载;2) 基于源码依赖,利于代码维护与热重载,需配置Flutter环境。项目结构包括AppScope、flutter_module等目录,适用于不同开发需求。
69 3
|
13天前
|
传感器 开发框架 物联网
鸿蒙next选择 Flutter 开发跨平台应用的原因
鸿蒙(HarmonyOS)是华为推出的一款旨在实现多设备无缝连接的操作系统。为了实现这一目标,鸿蒙选择了 Flutter 作为主要的跨平台应用开发框架。Flutter 的跨平台能力、高性能、丰富的生态支持和与鸿蒙系统的良好兼容性,使其成为理想的选择。通过 Flutter,开发者可以高效地构建和部署多平台应用,推动鸿蒙生态的快速发展。
114 0
|
15天前
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
55 0
|
1月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
74 7
|
28天前
|
编解码 Dart API
鸿蒙Flutter实战:06-使用ArkTs开发Flutter鸿蒙插件
本文介绍了如何开发一个 Flutter 鸿蒙插件,实现 Flutter 与鸿蒙的混合开发及双端消息通信。通过定义 `MethodChannel` 实现 Flutter 侧的 token 存取方法,并在鸿蒙侧编写 `EntryAbility` 和 `ForestPlugin`,使用鸿蒙的首选项 API 完成数据的读写操作。文章还提供了注意事项和参考资料,帮助开发者更好地理解和实现这一过程。
56 0
|
28天前
|
Dart Android开发
鸿蒙Flutter实战:03-鸿蒙Flutter开发中集成Webview
本文介绍了在OpenHarmony平台上集成WebView的两种方法:一是使用第三方库`flutter_inappwebview`,通过配置pubspec.lock文件实现;二是编写原生ArkTS代码,自定义PlatformView,涉及创建入口能力、注册视图工厂、处理方法调用及页面构建等步骤。
47 0
|
2月前
|
JSON Dart Java
flutter开发多端平台应用的探索
flutter开发多端平台应用的探索
50 6
|
2月前
|
JSON Dart Java
flutter开发多端平台应用的探索 下 (跨模块、跨语言通信之平台通道)
flutter开发多端平台应用的探索 下 (跨模块、跨语言通信之平台通道)