隨着每一個工程的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,歸根結底都是爲了減小代碼之間的依賴從而下降代碼的耦合度。