EventBus is introduced
The EventBus library is one of the most extensive of the Android EventBus libraries. Check out the Android EventBus library description website
- Publisher/subscriber pattern
- Centralized communication
- Low coupling, simplified component communication
Custom EventBus
We’re going to implement a Flutter version of EventBus! The functions are as follows:
- Register Subscriber to EventBus
- Publisher Post Event is distributed to Subscriber
- EventBus can register many types of events
Demo address entry main_eventbus.dart
The interface definition
// Publisher interface
abstract class IPublisher {
void post<T>(T event);
}
// Subscriber: function object
typedef ISubscriber<T> = void Function(T event);
// Centralized communication,
IEventBus inherits IPublisher and distributes data
IEventBus register and unregister ISubscriber
abstract class IEventBus extends IPublisher {
void register<T>(ISubscriber<T> subscriber);
void unregister<T>(ISubscriber<T> subscriber);
}
Copy the code
Interface Implementation 1
Type typeOf<T>() => T;
class MyEventBus1 implements IEventBus {
// We use map to store our subscribers. Different subscribers may subscribe to different types of events
Map<Type.List<Function>> map = new Map(a);@override
void register<T>(ISubscriber<T> subscriber) {
Type type = typeOf<T>();
if(! map.containsKey(type)) { map[type] =new List(a); } map[type].add(subscriber); }@override
void unregister<T>(ISubscriber<T> subscriber) {
Type type = typeOf<T>();
if(map.containsKey(type)) { map[type].remove(subscriber); }}/ / release
@override
void post<T>(T event) {
Type type = typeOf<T>();
if (map.containsKey(type)) {
var subscribers = map[type];
subscribers?.forEach((subscriber) => subscriber?.call(event));
}
}
}
Copy the code
Interface Implementation 2
class MyEventBus2 implements IEventBus {
List<Function> subscribers = new List(a);@override
register<T>(ISubscriber<T> subscriber) {
if (!subscribers.contains(subscriber)) {
subscribers.add(subscriber);
}
}
@override
unregister<T>(ISubscriber<T> subscriber) {
if(subscribers.contains(subscriber)) { subscribers.remove(subscriber); }}@override
post<T>(T event) {
varints = subscribers.whereType<ISubscriber<T>>(); ints? .forEach((subscriber) => subscriber? .call(event)); }}Copy the code
A functional test
class EventX {}
class EventY {}
main() {
testEventBus(new MyEventBus1());
print("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
testEventBus(new MyEventBus2());
}
void testEventBus(IEventBus eventBus) {
ISubscriber<EventX> subscriber1 = (event) => print(event.toString());
ISubscriber<EventX> subscriber2 = (event) => print(event.toString());
eventBus.register(subscriber1);
eventBus.register(subscriber2);
eventBus.unregister(subscriber1);
ISubscriber<EventY> subscriber3 = (event) => print(event.toString());
ISubscriber<EventY> subscriber4 = (event) => print(event.toString());
eventBus.register(subscriber3);
eventBus.register(subscriber4);
eventBus.post(new EventX());
eventBus.post(new EventY());
}
Copy the code
The result output is as follows, and the test passes
Instance of 'EventX'
Instance of 'EventY'
Instance of 'EventY'
--------------------
Instance of 'EventX'
Instance of 'EventY'
Instance of 'EventY'
Copy the code
Use custom EventBus
Demand effect drawing
Click the button on each row, and the two numbers on each row increase by one
Code implementation
MyEventBus1 _eventBus = new MyEventBus1();
// First we define event. The first row uses EventA, the second row uses EventB
class BaseEvent { int count = 0; }class EventA extends BaseEvent {}
class EventB extends BaseEvent {}
// Click the button
class ButtonWidget<T extends BaseEvent> extends StatelessWidget {
final T event;
const ButtonWidget({Key key, this.event}) : super(key: key);
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("increment"),
onPressed: _increment,
);
}
// Click handle
void _increment() {
if(event ! =null) { event.count++; _eventBus.post(event); }}}// Display numbers
class TextWidget<T> extends StatefulWidget {
@override
_TextWidgetState<T> createState() {
return_TextWidgetState<T>(); }}class _TextWidgetState<T> extends State<TextWidget<T>> {
int _count = 0;
ISubscriber<T> _subscriber;
@override
void initState() {
super.initState();
// Refresh the UI with setState.
_subscriber =
(event) => setState(() => _count = (event as BaseEvent).count);
/ / register
_eventBus.register<T>(_subscriber);
}
@override
Widget build(BuildContext context) {
return Text(
" $_count ",
style: TextStyle(fontSize: 18)); }@override
void dispose() {
super.dispose();
// Cancel registration_eventBus.unregister<T>(_subscriber); }}// The page body
class EventBusDemoWidget1 extends StatefulWidget {
EventBusDemoWidget1({Key key}) : super(key: key);
@override
_EventBusDemoWidget1State createState() => _EventBusDemoWidget1State();
}
class _EventBusDemoWidget1State extends State<EventBusDemoWidget1> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(eventBusDemo1Title),
),
body: Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
new ButtonWidget(event: new EventA()),
new TextWidget<EventA>(),
new TextWidget<EventA>()
],
),
Divider(
color: Colors.grey,
),
Row(
children: <Widget>[
new ButtonWidget(event: new EventB()),
new TextWidget<EventB>(),
newTextWidget<EventB>() ], ) ], ), ), ); }}Copy the code
Customizing EventBus is complete! Take a look at someone else’s EventBus.
EventBus pub
We found an EventBus library on pub.dev, github address
An Event Bus using Dart Streams for decoupling applications
Use the EventBus
The actual differences from custom EventBus are in ButtonWidget and TextWidget.
class ButtonWidget<T extends BaseEvent> extends StatelessWidget {
final T event;
const ButtonWidget({Key key, this.event}) : super(key: key);
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("increment"),
onPressed: _increment,
);
}
void _increment() {
if(event ! =null) {
event.count++;
print(event.count);
_eventBus.fire(event);
// _eventBus.post(event);}}}class TextWidget<T> extends StatefulWidget {
@override
_TextWidgetState<T> createState() {
return_TextWidgetState<T>(); }}class _TextWidgetState<T> extends State<TextWidget<T>> {
int _count = 0;
// ISubscriber
_subscriber;
StreamSubscription<T> _subscriber;
@override
void initState() {
super.initState();
// _subscriber =
// (event) => setState(() => _count = (event as BaseEvent).count);
// _eventBus.register
(_subscriber);
_subscriber = _eventBus
.on<T>()
.listen((event) => setState(() => _count = (event as BaseEvent).count));
}
@override
Widget build(BuildContext context) {
print(typeOf<T>());
return Text(
" $_count ",
style: TextStyle(fontSize: 18)); }@override
void dispose() {
super.dispose();
// _eventBus.unregister
(_subscriber);
_subscriber.cancel(); }}Copy the code
EventBus library source code
class EventBus {
StreamController _streamController;
StreamController get streamController => _streamController;
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
EventBus.customController(StreamController controller)
: _streamController = controller;
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream;
} else {
return streamController.stream.where((event) => event isT).cast<T>(); }}void fire(event) {
streamController.add(event);
}
voiddestroy() { _streamController.close(); }}Copy the code
The Stream of EventBus library
Dart comes with a Stream that handles asynchronous events. Stream is the core API of the Dart: Async library and provides excellent support for asynchrony. Here is a simple texture to feel the Stream.
return streamController.stream.where((event) => event is T).cast<T>();
Copy the code
CastStream(_WhereStream(_BroadcastStream)) returns a Stream that has two layers of CastStream(_WhereStream(_BroadcastStream)) to listen for generic events.
_WhereStream handle first
class _WhereStream<T> extends _ForwardingStream<T.T> {
final _Predicate<T> _test;
_WhereStream(Stream<T> source, bool test(T value))
: _test = test,
super(source);
//_test => event is T
void _handleData(T inputEvent, _EventSink<T> sink) {
bool satisfies = _test(inputEvent);
if(satisfies) { sink._add(inputEvent); }}}Copy the code
CastStreamSubscription actually does type conversions
class CastStreamSubscription<S.T> implements StreamSubscription<T> {
final StreamSubscription<S> _source;
/// User's data handler. May be null.
void Function(T) _handleData;
CastStreamSubscription(this._source) {
_source.onData(_onData);
}
void _onData(S data) {
if (_handleData == null) return;
T targetData = data as T;
_zone.runUnaryGuarded(_handleData, targetData);
}
Copy the code
Afterword.
So far, we’ve encountered streams in the Provider library StreamProvider, and we’ve encountered Streams in the EventBus library with the excellent asynchronous programming models Stream and Future that Dart comes with. Stream has a good asynchronous model that nourishes a large component library Provider/BLoC/ Flutter_redux /Rxdart/fish-redux. It is then necessary to analyze the Stream.