Flutter 之使用 Event Bus 更改主題

介紹

隨着每一個工程的MVC模塊逐漸增多,模塊與模塊之間的通訊也變得愈來愈多,代碼耦合必然增長。設計模式

Event Bus 爲解耦而生,熱別是設計模式爲MVCMVP的項目。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中優秀的StreamStream的用法仍是不少的,Event Bus中主要使用Streamlisten()方法。markdown

從源碼很容易看出Event bus的用法:app

初始化

EventBus({bool sync = false})
      : _streamController = StreamController.broadcast(sync: sync);
複製代碼

streamController做爲Dart官方內置類,實際上就是stream數據的控制器。async

sync參數表明事件流是否馬上傳遞到監聽者,默認爲falseide

broadcast這種廣播的初始化方式可讓stream被多我的訂閱,若是你只想被一我的訂閱請使用StreamController對應single的初始化方式,這裏不過多討論。oop

EventBus也支持你使用EventBus.customController(StreamController controller)的方式自定義StreamController佈局

監聽

監聽方式爲eventBus.on<T>().listen()listen()方法是內置類StreamAPIui

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

Coding

先定義一個主題顏色的事件:

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

相關文章
相關標籤/搜索