Flutter-BLoC-第三讲

本文为 《Flutter Bloc Package》 的译文,原文地址主要讲 BLoC 的第三方框架 bloc ,若转载本译文请注明出处。本译文主要内如下:

  • Glossary(词汇表)
  • Bloc API
  • BlocBuilder
  • BlocProvider

在使用Flutter工作一段时间之后,我决定创建一个软件包以帮助我经常使用的东西:BLoC模式。
对于那些不熟悉BLoC模式的人来说,它是一种设计模式,有助于将表示层与业务逻辑分开。你在这里了解更多。

使用BLoC模式可能具有挑战性,因为需要建立对Streams和Reactive Programming的理解。但它的核心是BLoC非常简单:

BLoC 将event流作为输入,并将它们转换为state流作为输出。

image.png

我们现在可以在bloc的dart包的帮助下使用这种强大的设计模式。

该软件包抽象了模式的反应方面,允许开发人员专注于将事件(event)转换为状态(state)。

让我们从定义这些术语开始……

词汇表

Events

Events 是Bloc的输入。它们通常是UI事件,例如按钮按下。Events被分发(dispatched)并且被转换为States。

States

States 是Bloc的输出。表示组件可以监听状态流 并根据给定状态重绘其自身的部分(BlocBuilder有关详细信息,请参阅参考资料)。

Transitions

Transitions 发生在 调用mapEventToState之后 但在更新了bloc的state之前 调度了一个Event

现在我们了解事件和状态,我们可以看一下Bloc API。

BLOC API

mapEventToState

当一个类继承Bloc时,必须实现 mapEventToState 方法, 该函数将传入事件作为参数。
只要UI层触发一个事件,就会调用 mapEventToState。
mapEventToState 必须将该event转换为新state,并以UI层消耗的Stream形式返回新状态。

dispatch

dispatch 是一个 接受 event 并触发 mapEventToState 的方法。
可以从表示层调用dispatch 或 从Bloc内部(见例子)并通知Bloc一个新 event。

initialState

initialState是处理任何事件之前的状态(在mapEventToState被调用之前)。
如果未实现,则为initialState null。

transform

transform是一个 在调用mapEventToState之前 可以重写以转换 Stream .
这允许使用distinct() 和 debounce() 的操作。

onTransition

onTransition 是一个 每次 transform 发生时都可以重写以进行处理 的方法。
调度新event 并调用mapEventToState时发生transition。
onTransition 在更新 bloc 状态之前 被调用。
这是添加特定于块的日志记录/分析的好地方

让我们创建一个counter bloc!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;

@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}

创建一个BLoC 需要作如下操作:

  • 定义所有 event 和 state
  • 继承Bloc
  • 重写 initialState 和 mapEventToState

在这种情况下,我们的 events 是CounterEvents ,states 是 integers

CounterBloc 转换 CounterEvents 为 integers。

我们可以通过 dispatch 来 通知CounterBloc 事件

1
2
3
4
5
6
void main() {
final counterBloc = CounterBloc();

counterBloc.dispatch(CounterEvent.increment);
counterBloc.dispatch(CounterEvent.decrement);
}

为了观察状state 的 转换(Transitions),我们可以重写onTransition。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;

@override
void onTransition(Transition<CounterEvent, int> transition) {
print(transition);
}

@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}

现在,每当发出(dispatch)一个CounterEvent我们的Bloc将响应一个新的integer状态,我们将看到一个transition被输出到控制台。

现在让我们使用Flutter构建一个UI,并使用flutter_bloc 包将UI连接到我们的CounterBloc。

BlocBuilder

BlocBuilder是一个Flutter小部件,它需要一个Bloc和一个构建器函数。
BlocBuilder处理构建窗口小部件以响应新state。
BlocBuilder与StreamBuilder非常相似,但它有一个更简单的API来减少所需的样板代码量。

BlocProvider

BlocProvider是一个Flutter小部件,它通过 BlocProvider.of(context)为其子女提供了一个bloc。
它用作依赖注入(DI)小部件, 这样一个bloc实例 可以被提供给子树中的多个小部件。

现在让我们构建 counter App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class App extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AppState();
}

class _AppState extends State<App> {
final CounterBloc _counterBloc = CounterBloc();

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider<CounterBloc>(
bloc: _counterBloc,
child: CounterPage(),
),
);
}

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

class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc _counterBloc = BlocProvider.of<CounterBloc>(context);

return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterEvent, int>(
bloc: _counterBloc,
builder: (BuildContext context, int count) {
return Center(
child: Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_counterBloc.dispatch(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
_counterBloc.dispatch(CounterEvent.decrement);
},
),
),
],
),
);
}
}

该App小部件是StatefulWidget,负责创建和销毁 CounterBloc。
它让 CounterBloc 使用 BlocProvider 小部件可用于 CounterPage 小部件。

CounterPage小部件是StatelessWidget, 它使用BlocBuilder重建UI以响应CounterBloc的状态变化。

此时,我们已经成功地将我们的表示层与业务逻辑层分开。请注意,CounterPage窗口小部件对用户点击按钮时发生的情况一无所知。小部件只是告诉CounterBloc用户按下了递增或递减按钮。

有关更多示例和详细文档,请查看官方集团文档

相关链接:

bloc dart包
flutter_bloc包
flutter_bloc使用官方文档

------------- End Thank For Your Reading -------------