「 flutter 必知必会 」最强事件发布/订阅框架方案 event_bus 全局事件总线使用解析

2021-12-30 16:32:45 浏览数 (1)

一、前言

  • EventBus是全局事件总线,底层通过Stream来实现;它可以实现不同页面的跨层访问,通过Stream的机制来实现不同widget之间的状态共享.

二、作用

  • 举个例子:

你有一个主界面,里面有一些信息可能会修改,但触发源不在该界面,是在其他的界面触发了一些事件后,首页的内容需要做修改。如果没有EventBus,也有很多的方式可以实现,譬如定义全局静态变量、或者定义个CallBack接口传出去等等。不管怎样,总是要把主页和触发源关联起来,这是相当难受的,这不但会导致代码量暴涨,同时还会导致耦合度极高,不得不写的这个引用让人如鲠在喉。

三、使用

flutter 中使用 eventbus 主要可以归纳为如下步骤:

  1. 实例化 eventbus
  2. 定义消息 event 对象
  3. 创建监听器
  4. 发送消息
  5. 接收消息
  • 下文我将以网络请求为例进行解析
3.1 实例化 eventbus
  • 由于 eventbus 是可能在不同类里调用的
  • 就比如说本文的网络请求使用 eventbus ,那么网络请求可能发生在很多歌类的代码里
  • 所以不能将其单独在某个特定页面的类里创建,而需要让其变成全局的变量
  • 这里我们新建一个文件:event_bus.dart 用它来专门存储 event_bus 对象
代码语言:javascript复制
import 'package:event_bus/event_bus.dart';

EventBus eventBus = new EventBus();
3.2 定义消息 event 对象
  • 有学过 AndroidiOS 或者其他任意技术的同学都知道
  • 在全局发送一个消息是需要携带一个对象,来存储消息的内容
  • 其实 flutterevent_bus 也是如此
  • 所以我们新建一个文件 http_event.dart 来存放这种需要传递的消息体
  • 当然本文限于篇幅,就以网络请求错误/失败为例
代码语言:javascript复制
class HttpErrorEvent {
  final int code;

  final String message;

  HttpErrorEvent(this.code, this.message);
}
3.3 创建监听器
  • 跟 3.2 类似,我们针对每种类型的事件(每个 eventbus),需要建立一个监听
  • 并把这个监听混入相应的页面(widget 中)来达到监听效果
  • 所以,我们首先新建一个文件 http_listener.dart
代码语言:javascript复制
import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:fluttertoast/fluttertoast.dart';

import 'event_bus.dart';
import 'home_page.dart';
import 'http_event.dart';

mixin HttpErrorListener on State<MyHomePage> {
  StreamSubscription stream;

  BuildContext _context;

  @override
  void initState() {
    super.initState();

    ///Stream演示event bus
    stream = eventBus.on<HttpErrorEvent>().listen((event) {
      errorHandleFunction(event.code, event.message);
    });
  }

  @override
  void dispose() {
    super.dispose();
    if (stream != null) {
      stream.cancel();
      stream = null;
    }
  }

  ///网络错误提醒
  errorHandleFunction(int code, message) {
    switch (code) {
      case 404:
        showToast("404: ${message}");
        break;
      default:
        showToast("unknown: ${message}");
        break;
    }
  }

  showToast(String message) {
    Fluttertoast.showToast(
        msg: message,
        gravity: ToastGravity.CENTER,
        toastLength: Toast.LENGTH_LONG);
  }
}
  • 这里使用 私有的 _context 是因为此时 StatecontextFlutterReduxApp 而不是 MaterialApp
  • 所以如果直接用 context 是会获取不到 MaterialAppLocalizations 哦。

showToast 是一个三方的吐司库 可以再 dependences 中添加 fluttertoast: ^7.1.1 即可

3.4 发送消息
  • 发送消息时只要调用 eventBus.fire(...) 即可
  • 参数是需要传递的消息对象
  • 比如本文的玩过请求失败就可以是 HttpErrorEvent(404, "找不到网页")
代码语言:javascript复制
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  @override
  void dispose() {
    super.dispose();
  }

  void _incrementCounter() {
    eventBus.fire(HttpErrorEvent(404, "找不到网页"));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Text(
        'flutter event bus sample',
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
3.5 接收消息
  • 接收消息其实就是把监听器混入的过程
  • 混入对象可以是一个单例(比如收到消息后上传日志,或者后台做什么动作),也可以是一个页面(比如刷新下页面,或者本文网络请求的例子,请求失败需要弹出一个 Toast),等等诸如此类
  • 这里就按网络请求的例子,我们在 State 对象后混入:with HttpErrorListener 即可
代码语言:javascript复制
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with HttpErrorListener {
  // ...
}
  • 这样一来,当 3. 4 中发送消息后,_MyHomePageState 的监听就会收到消息对象
  • 并调用 HttpErrorListener 中的 errorHandleFunction 方法去触发其中的 showToast(String message) 方法,从而在屏幕上弹出一个 Toast

为了方便大家理解,对于 Dart 中混合 mixin、on、with 语法陌生的同学可以看这篇文章 Flutter 知识梳理 (Dart) - implements, extends, mixin 的理解

四、总结

  • 对于复杂的程序 event_bus 能有效的解耦,把本该有联系的需要传引用的给简化了,由它来分发事件了。这在某些时候和Android里的broadcast有点像,都是能通知很多个观察者,只不过更简便一点。
  • 为了方便大家学习,我把代码上传到了 github:bilibili-workspace/flutter_eventbus_sample
  • 如果还有疑问,或者有更好的方法,欢迎大家在评论区提出
  • 感谢大家的三连或者关注支持,我们下期文章再见

0 人点赞