### 事件總線
在APP中,我們經常會需要一個廣播機制,用以跨頁面事件通知,比如一個需要登錄的APP中,頁面會關注用戶登錄或注銷事件,來進行一些狀態更新。這時候,一個事件總線便會非常有用,事件總線通常實現了訂閱者模式,訂閱者模式包含發布者和訂閱者兩種角色,可以通過事件總線來觸發事件和監聽事件,本節我們實現一個簡單的全局事件總線,我們使用單例模式,代碼如下:
```
//訂閱者回調簽名
typedef void EventCallback(arg);
class EventBus {
//私有構造函數
EventBus._internal();
//保存單例
static EventBus _singleton = new EventBus._internal();
//工廠構造函數
factory EventBus()=> _singleton;
//保存事件訂閱者隊列,key:事件名(id),value: 對應事件的訂閱者隊列
var _emap = new Map<Object, List<EventCallback>>();
//添加訂閱者
void on(eventName, EventCallback f) {
if (eventName == null || f == null) return;
_emap[eventName] ??= new List<EventCallback>();
_emap[eventName].add(f);
}
//移除訂閱者
void off(eventName, [EventCallback f]) {
var list = _emap[eventName];
if (eventName == null || list == null) return;
if (f == null) {
_emap[eventName] = null;
} else {
list.remove(f);
}
}
//觸發事件,事件觸發后該事件所有訂閱者會被調用
void emit(eventName, [arg]) {
var list = _emap[eventName];
if (list == null) return;
int len = list.length - 1;
//反向遍歷,防止在訂閱者在回調中移除自身帶來的下標錯位
for (var i = len; i > -1; --i) {
list[i](arg);
}
}
}
//定義一個top-level變量,頁面引入該文件后可以直接使用bus
var bus = new EventBus();
```
使用
```
//頁面A中
...
//監聽登錄事件
bus.on("login", (arg) {
// do something
});
//登錄頁B中
...
//登錄成功后觸發登錄事件,頁面A中訂閱者會被調用
bus.emit("login", userInfo);
```
> 注意:Dart中實現單例模式的標準做法就是使用static變量+工廠構造函數的方式,這樣就可以保證`new EventBus()`始終返回都是同一個實例,讀者應該理解并掌握這種方法。
事件總線通常用于Widget之間狀態共享,但關于Widget之間狀態共享也有一些專門的Package如redux,這和web框架Vue/React是一致的。通常情況下事件總線是足以滿足業務需求的,如果你決定使用redux的話,一定要想清楚業務是否真的有必要用它,防止“化簡為繁”、過度設計。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示