在刚开始学习Flutter时如果读到有关状态管理的文章大部分都会是与「BLoC」相关的内容,虽然真的是有点复杂,但感觉还是得尝试看看「BLoC」到底有什麽优点可以是Flutter中最多人推崇的架构(但也许现在的风向是GetX比较受欢迎)
所谓 BLoC 就是 Business Logic Components的缩写,BLoC其实是一种 design pattern,而不只是套件。他的目标是在分离「画面」以及「商业逻辑」,其实就跟我们之前在讲状态管理时的目标一样。
BLoC的核心概念是将所有「事件」都视作stream:
我们透过送出「事件」然後会送到「BLoC」里进行处理最後送到接收Steam的widget。
如同前面讲过 BLoC 是一种design pattern ,所以他是可以在不使用「bloc」、「flutter_bloc」这两个套件下被实作的出来的。
首先我们先来创立一个档案来放 BLoC相关的东西。
import 'dart:async';
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
class CounterBLoC {
CounterBLoC() {
_counterEventController.stream.listen(_count);
}
int _counter = 0;
_count(CounterEvent event) {
if (event is IncrementEvent) {
counterSink.add(++_counter);
} else if (event is DecrementEvent) {
counterSink.add(--_counter);
}
}
final _counterStreamController = StreamController<int>();
StreamSink<int> get counterSink => _counterStreamController.sink;
Stream<int> get streamCounter => _counterStreamController.stream;
final _counterEventController = StreamController<CounterEvent>();
Sink<CounterEvent> get counterEventSink {
return _counterEventController.sink;
}
dispose() {
_counterStreamController.close();
_counterEventController.close();
}
}
首先会先看到
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
这边就是所谓的「事件」也就是这个BLoC能够处理的事件就只有这些。
接下来我们接着来宣告这个BLoC处理Stream的相关实作
final _counterStreamController = StreamController<int>();
StreamSink<int> get counterSink => _counterStreamController.sink;
Stream<int> get streamCounter => _counterStreamController.stream;
final _counterEventController = StreamController<CounterEvent>();
Sink<CounterEvent> get counterEventSink {
return _counterEventController.sink;
}
我们利用 _counterStreamController
来作为更新状态及画面更新的控制器,_counterEventController
则是来接收外部事件用。
然後实作我们的计数逻辑:
int _counter = 0;
_count(CounterEvent event) {
if (event is IncrementEvent) {
counterSink.add(++_counter);
} else if (event is DecrementEvent) {
counterSink.add(--_counter);
}
}
当我们的事件是 IncrementEvent
时向我们的_counterStreamController
的 sink传入加一後的值,如果是 DecrementEvent
则传入减一後的值。
最後则是让我们这个BLoC 实例化後,去监听 _count
CounterBLoC() {
_counterEventController.stream.listen(_count);
}
//省略
final bloc = CounterBLoC();
//省略
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
OutlinedButton(
onPressed: () {
bloc.counterEventSink.add(DecrementEvent());
},
child: Text('-1')),
OutlinedButton(
onPressed: () {
bloc.counterEventSink.add(IncrementEvent());
},
child: Text('+1')),
],
),
StreamBuilder(
stream: bloc.streamCounter,
initialData: 0,
builder: (context, snapshot) {
return Center(child: Text(snapshot.data.toString()));
})
],
),
这边我们就先实例化 CounterBLoC
然後将实作两个按钮然後将他们的 onPressed
时会触发
bloc.counterEventSink.add(事件)
。
然後在用 StreamBuilder
来监听 bloc.streamCounter
的变化来进行画面重新渲染。
整个流程就会是:
按按钮 → 送出事件到 counterEventSink
→ counterEventSink
收到事件後会送出值到 counterSink
→ streamCounter
响应变化。
今天的程序码:
https://github.com/zxc469469/flutter_rest_api_playground/tree/Day27
明天就会开始使用套件来实作BLoC
参考资料:
<<: Day 26:Container != Docker Container
>>: 【Day 25】Go 与 Python gRPC 小练习
客户需求如下 不要用datepicker点,说是手机太小不好点+老人不会点 资料库格式为西元年,但一...
今天会介绍Material UI 的排版系统,可以进入官网从侧边栏可以看到以下画面: Contain...
#odoo #开源系统 #数位赋能 #E化自主 昨天我们讨论了销售模组的运作,我们接下来进入到采购模...
本文将於赛後同步刊登於笔者部落格 有兴趣学习更多 Kubernetes/DevOps/Linux 相...
观赏鱼辨识系统说明-Day 01 在接下来的30天会制作一个完整的系统包含前端-手机/网页,後端-N...