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,归根结底都是为了减小代码之间的依赖从而下降代码的耦合度。

相关文章
相关标签/搜索