Flutter事件总线插件event_bus_basics的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter事件总线插件event_bus_basics的使用

Event Bus Basics

GitHub main workflow codecov pub package GitHub Stars Buy me a coffee

一个用于在一次记录多个服务的 Dart 包。

Overview

大多数应用程序在其生命周期中会收集一些事件数据。这包括用户点击按钮、登录失败、网络请求完成、异常等。

当收集这些重要事件时,它们通常会被发送到诸如 Firebase Analytics、Pendo 或 New Relic 的指标服务,然后发送到控制台,甚至广告平台等。

不建议为单个事件调用多个服务。这会使添加和更新事件变得困难。

onPressed: () {
    FirebaseAnalytics().logEvent('tapped_sign_in'); // 不好的做法
    MyLogger.log('tapped sign in'); // 不好的做法
    print('tapped sign in'); // 不好的做法
}

另外,更改指标提供商或在整个应用中添加一个新的提供商也需要大量工作。

通过 EventBus,你可以在代码中记录一个事件,然后创建多个消费者来将事件写入不同的服务。

这是一个类似于但不同于 EventBus 包的事件总线。

Getting Started

EventBus 支持特定类型的事件,这些事件有助于组织事件,并使消费者更容易地将它们写入服务。

  • app: 当应用程序事件发生时,例如冷启动或从后台恢复。
  • error: 当错误发生时,例如异常。
  • event: 当特定的可识别事件发生时,例如用户完成登录,或者用户完成购买。
  • tap: 当用户点击按钮、链接或其他可点击的小部件时。
  • network: 当 HTTP 请求被发出或完成时。
  • trace: 任何要记录的事件,不属于上述类别,例如常规调试日志。

首先,你需要实例化 EventBus 并保持对它的引用。

final eventBus = EventBus();

现在,你可以记录你的事件。例如,要记录点击按钮,可以使用 EventBus 上的 tap 方法。

eventBus.tap('signin_button', screen: 'SignInScreen');

你也可以使用 EventBus 上的 event 方法记录任何特定事件。

eventBus.event('signin_completed');

你可以轻松地一致地记录应用级别的事件,如冷启动、恢复和版本。

eventBus.app(EventBusAppEvent.coldStart);

所有方法都可以接受额外的参数以增强日志记录。这些参数是无限的,并且采用 Map 的形式。

eventBus.event('signin_completed', params: {'username': username});

Consumers

可能需要多个消费者,每个消费者支持一个服务。这些消费者会监听来自 EventBus 的事件流,并决定将哪些事件发送到其服务。

以下是一个简单的消费者,它只是将事件打印到控制台:

eventBus.events.listen((event) {
  print(event);
});

Consumer: EventBusLogger

这个包中包含一个消费者 (EventBusLogger),它可以将事件记录到 [Logger] 中。使用非常简单。

final eventBus = EventBus();

final logger = Logger(
  level: Level.debug,
  output: ConsoleOutput(),
  filter: ProductionFilter(),
  printer: PrefixPrinter(SimplePrinter(printTime: true, colors: false)),
);

EventBusLogger(eventBus, logger);

设置好 EventBusLogger 后,它将以如下格式将每个事件记录到日志中:

INFO [I] TIME: 2023-07-02T18:29:34.613347 @ app.coldstart
INFO [I] TIME: 2023-07-02T18:29:34.621539 @ tap=>signin_button - {screen: SignInScreen}
DEBUG [D] TIME: 2023-07-02T18:29:34.621638 @ ⚡︎ SignInBloc: start signing
DEBUG [D] TIME: 2023-07-02T18:29:34.621705 @ SignInScreen.build: started - {name: foobar}
INFO [I] TIME: 2023-07-02T18:29:34.621751 @ sign_in - {username: foobar}
ERROR [E] TIME: 2023-07-02T18:29:34.621784 @ SignInScreen._signIn: failed
DEBUG [D] TIME: 2023-07-02T18:29:34.621828 @ SignInScreen._signIn: completed

如果你喜欢使用 EventBusLogger,但希望有不同的输出格式,只需复制 EventBusLogger 中的代码到新类中,根据需要进行自定义并使用该类。

Consumer: EventBusEventCollector

此包还包含一个消费者(EventBusEventCollector),它收集事件并在内存中保留它们,以便像在调试屏幕上显示它们。

Consumer: EventBusFirebaseAnayltics

以下是一个方便的示例,编写了一个针对 Firebase Analytics 的消费者。它将所有事件记录到 Firebase,但更改了错误和点击事件的名称。

class EventBusFirebaseAnayltics {
  EventBusFirebaseAnayltics(this.eventBus) {
    eventBus.events.listen((event) {
      _handleEvent(event);
    });
  }

  final EventBus eventBus;

  Future<void> _handleEvent(EventBusEvent event) async {
    if (event.group == EventBusGroup.error) {
      final name = event.isValidName ? 'error_${event.name}' : 'error_detected';
      FirebaseAnalytics.instance.logEvent(name: name, parameters: event.params);
    } else if (event.group == EventBusGroup.tap) {
      final name = 'tap_${event.name}';
      FirebaseAnalytics.instance.logEvent(name: name, parameters: event.params);
    } else {
      FirebaseAnalytics.instance
          .logEvent(name: event.name, parameters: event.params);
    }
  }
}

注意:此类未包含在此包中,以避免将 FirebaseAnalytics 作为依赖项。您可以将其复制到您的应用中供自己使用。

Contributing

所有评论和拉取请求都欢迎。

Donations / Sponsor

请在 Flattr 或 Patreon 上赞助或捐赠给 event_bus_basics 的创建者。


完整示例 Demo

import 'package:event_bus_basics/event_bus_basics.dart';

Future<void> main() async {
  final eventBus = EventBus();

  eventBus.events.listen((event) {
    print('event: $event');
  });

  eventBus.app(EventBusAppEvent.coldStart);
  eventBus.tap('signin_button', screen: 'SignInScreen');
  eventBus.network('SignInBloc: start signing');
  eventBus.trace('SignInScreen.build: started', params: {'name': 'foobar'});
  eventBus.event('sign_in', params: {'username': 'foobar'});
  eventBus.error('SignInScreen._signIn: failed');
  eventBus.send(EventBusGroup.trace, name: 'SignInScreen._signIn: completed');
}

更多关于Flutter事件总线插件event_bus_basics的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter事件总线插件event_bus_basics的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用event_bus_basics插件的示例代码。这个插件可以帮助你在不同的组件之间传递事件,非常适合实现观察者模式。

首先,你需要在你的pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  event_bus_basics: ^2.0.0  # 请检查最新版本号

然后运行flutter pub get来安装依赖。

接下来,我们创建一个简单的事件类。例如,我们创建一个MessageEvent类,它包含一条消息字符串:

// event.dart
class MessageEvent {
  final String message;

  MessageEvent(this.message);
}

然后,我们需要创建一个EventBus实例来管理事件:

// main.dart
import 'package:flutter/material.dart';
import 'package:event_bus_basics/event_bus.dart';
import 'event.dart';

void main() {
  // 创建一个全局的EventBus实例
  final eventBus = EventBus();

  runApp(MyApp(eventBus));
}

class MyApp extends StatelessWidget {
  final EventBus eventBus;

  MyApp(this.eventBus);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('EventBus Basics Example'),
        ),
        body: Column(
          children: [
            EventBusProvider(
              eventBus: eventBus,
              child: SenderWidget(),
            ),
            EventBusProvider(
              eventBus: eventBus,
              child: ReceiverWidget(),
            ),
          ],
        ),
      ),
    );
  }
}

在上面的代码中,我们创建了一个全局的EventBus实例,并将其传递给MyApp组件。EventBusProvider是一个自定义的Widget,用于将EventBus实例传递给其子组件:

// event_bus_provider.dart
import 'package:flutter/material.dart';
import 'package:event_bus_basics/event_bus.dart';

class EventBusProvider extends StatelessWidget {
  final EventBus eventBus;
  final Widget child;

  EventBusProvider({required this.eventBus, required this.child});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<EventBus>(
      valueListenable: eventBus.asValueListenable(),
      builder: (_, __, ___) {
        return child;
      },
      child: child,
    );
  }
}

接下来,我们创建一个发送事件的Widget:

// sender_widget.dart
import 'package:flutter/material.dart';
import 'package:event_bus_basics/event_bus.dart';
import 'event.dart';

class SenderWidget extends StatelessWidget {
  final EventBus eventBus;

  SenderWidget({required this.eventBus});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        // 发送事件
        eventBus.fire(MessageEvent("Hello, EventBus!"));
      },
      child: Text('Send Message'),
    );
  }
}

最后,我们创建一个接收事件的Widget:

// receiver_widget.dart
import 'package:flutter/material.dart';
import 'package:event_bus_basics/event_bus.dart';
import 'event.dart';

class ReceiverWidget extends StatefulWidget {
  final EventBus eventBus;

  ReceiverWidget({required this.eventBus});

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

class _ReceiverWidgetState extends State<ReceiverWidget> {
  String? _message;

  @override
  void initState() {
    super.initState();
    // 订阅事件
    widget.eventBus.on<MessageEvent>().listen((event) {
      setState(() {
        _message = event.message;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Text(_message ?? "Waiting for a message..."),
    );
  }
}

在这个例子中,当你点击Send Message按钮时,SenderWidget会发送一个MessageEvent事件。ReceiverWidget会订阅这个事件并更新其显示的文本。

请确保在实际项目中检查event_bus_basics插件的最新版本和文档,因为API可能会发生变化。

回到顶部