随着每一个工程的MVC
模块逐渐增多,模块与模块之间的通讯也变得愈来愈多,代码耦合必然增长。设计模式
Event Bus 为解耦而生,热别是设计模式为MVC
或MVP
的项目。bash
import 'dart:async'; class EventBus { StreamController _streamController; /// Controller for the event bus stream. StreamController get streamController => _streamController; /// Creates an [EventBus]. EventBus({bool sync = false}) : _streamController = StreamController.broadcast(sync: sync); /// Instead of using the default [StreamController] you can use this constructor EventBus.customController(StreamController controller) : _streamController = controller; /// Listens for events of Type [T] and its subtypes. Stream<T> on<T>() { if (T == dynamic) { return streamController.stream; } else { return streamController.stream.where((event) => event is T).cast<T>(); } } /// Fires a new event on the event bus with the specified [event]. void fire(event) { streamController.add(event); } /// Destroy this [EventBus]. This is generally only in a testing context. void destroy() { _streamController.close(); } } 复制代码
是的你没有看错,源码就只有这些。EventBus
之因此这样简洁得益于Dart
中优秀的Stream
。Stream
的用法仍是不少的,Event Bus
中主要使用Stream
的listen()
方法。markdown
从源码很容易看出Event bus
的用法:app
EventBus({bool sync = false}) : _streamController = StreamController.broadcast(sync: sync); 复制代码
streamController
做为Dart
官方内置类,实际上就是stream
数据的控制器。async
sync
参数表明事件流是否马上传递到监听者,默认为false
。ide
broadcast
这种广播的初始化方式可让stream
被多我的订阅,若是你只想被一我的订阅请使用StreamController
对应single
的初始化方式,这里不过多讨论。oop
EventBus
也支持你使用EventBus.customController(StreamController controller)
的方式自定义StreamController
。布局
监听方式为eventBus.on<T>().listen()
,listen()
方法是内置类Stream
的API
。ui
Stream<T> on<T>() { if (T == dynamic) { return streamController.stream; } else { return streamController.stream.where((event) => event is T).cast<T>(); } } 复制代码
StreamSubscription<T> listen(void onData(T event),
{Function onError, void onDone(), bool cancelOnError});
复制代码
Event Bus
经过泛型过滤,你能够只处理本身定义的事件类型。每当streamController
添加你监听的事件时,监听回调将会执行。listen()
方法会返回一个StreamSubscription
,若是你想取消监听能够调用对应的cancel()
方法this
void fire(event) {
streamController.add(event);
}
复制代码
触发事件比较简单,streamController
调用add()
方法给Stream
添加事件,添加完成后广播给全部对应监听者。
这里咱们使用Event Bus
实现一个变换主题颜色的功能,先看下效果:
dependencies:
event_bus: ^1.1.1
复制代码
添加依赖以后别忘记flutter pub get
先定义一个主题颜色的事件:
import 'package:flutter/material.dart'; class ThemeColor { final Color color; ThemeColor(this.color); } 复制代码
而后项目首页初始化一个eventBus
,并在initState
时监听ThemeColor
事件。
import 'package:flutter/material.dart'; import 'package:event_bus/event_bus.dart'; import 'package:flutterdemo/events/theme_color.dart'; void main() => runApp(MyApp()); EventBus eventBus = EventBus(); // 初始化 EventBus class MyApp extends StatefulWidget { MyApp({Key key}) : super(key: key); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { Color _themeColor; @override void initState() { super.initState(); // 监听 ThemeColor 事件 eventBus.on<ThemeColor>().listen((event) { setState(() { _themeColor = event.color; }); }); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primaryColor: _themeColor, // 事件回调的颜色赋值给 ThemeData ), ); } } 复制代码
定义一套主题颜色,在更改颜色时发送ThemeColor
事件。发送后监听的回调方法里从新设置主题颜色。
import 'package:flutter/material.dart'; import 'package:flutterdemo/components/common_app_bar.dart'; import 'package:flutterdemo/events/theme_color.dart'; import 'package:flutterdemo/main.dart'; class PersonSetting extends StatefulWidget { PersonSetting({Key key}) : super(key: key); @override _PersonSettingState createState() => _PersonSettingState(); } class _PersonSettingState extends State<PersonSetting> { Color _themeColor; @override Widget build(BuildContext context) { return Scaffold( appBar: CommonAppBar(title: '设置',), body: Center( child: DropdownButton( value: _themeColor, items: <DropdownMenuItem>[ DropdownMenuItem(value: Color(0xFF2196F3), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF2196F3),),),), DropdownMenuItem(value: Color(0xFFE3F2FD), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFFE3F2FD),),),), DropdownMenuItem(value: Color(0xFFBBDEFB), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFFBBDEFB),),),), DropdownMenuItem(value: Color(0xFF90CAF9), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF90CAF9),),),), DropdownMenuItem(value: Color(0xFF64B5F6), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF64B5F6),),),), DropdownMenuItem(value: Color(0xFF42A5F5), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF42A5F5),),),), DropdownMenuItem(value: Color(0xFF1E88E5), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF1E88E5),),),), DropdownMenuItem(value: Color(0xFF1976D2), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF1976D2),),),), DropdownMenuItem(value: Color(0xFF1565C0), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF1565C0),),),), DropdownMenuItem(value: Color(0xFF0D47A1), child: SizedBox(width: 80, height: 30, child: Container(color: Color(0xFF0D47A1),),),), ], onChanged: (color) { eventBus.fire(ThemeColor(color)); // 发送事件 setState(() { _themeColor = color; }); }, ), ), ); } } 复制代码
其实Event Bus
部分的代码不多,耦合性也很低,这里大部分的代码都是UI
布局。
Event Bus
使用起来很简单,常规的用法就是定义事件,监听事件,发送事件,如何取消监听上面也已经提到了。固然你也能够自定义streamController
作更多的事情,这里只是抛砖引玉简单的替换了主题颜色。
固然更改主题颜色使用其余方案也能够实现,如何使用Event Bus
你们也能够参考官方的 example,归根结底都是为了减小代码之间的依赖从而下降代码的耦合度。