原文链接:# Flutter BLoC Tutorial: State Management using BLoC Pattern - 原文作者 Archita Nayak & Mukul Singh
本文采用意译的方式
BLoc 是什么?
BLoC
代表 Business Logic Components;它的目的是从用户界面分离程序的业务逻辑。使得应用程序代码更加优雅,可扩展和可测试。
✅ 开发者: Felix Angelov
✅ 赞助者: Very Good Ventures, Stream, Miquido
✅ 版本: flutter_bloc: ^8.0.1(编写该文的时候)
BLoC 模式的优缺点
在我们进入 flutter bloc 教程 之前,我们说说 bloc
设计模式的优缺点。
使用 BLoC 的优点
✅ 针对不同场景都有很出色的文档
✅ 从 UI
中分离业务逻辑,因此使得代码更容易理解
✅ 使得产品更易测试
✅ 容易跟踪应用程序经历的状态
使用 BLoC 的缺点
✅ 其学习曲线有点陡峭
✅ 简单应用不推荐使用
✅ 有更多标准化代码,但可以通过扩展解决
Flutter BLoC 教程目标
我们将构建相关简单的应用,来演示 BLoC
如何使用流来管理状态,并为 bloc
编写一些测试。
我们将构建一个更改文本的应用程序;按压文本将会更改,并显示出来。参考下面的 GIF
图。
初始化设置
- 确保你在编辑器中添加了
bloc
扩展;它将帮助你创建项目所需所有标准化代码和文件(操作步骤:右击lib
文件夹,然后它会为我们的项目提供生成bloc
选项)。 - 确保与我的
pubspec.yaml
文件匹配,避免出现任何问题。
弄清 BLoC 概念:Events 和 States
我们进入正题。
为了搞清楚 bloc
是怎么工作的,我们需要知道 events
和 state
是什么。
✅ Events:事件就是应用程序的输入(比如点击鼠标加载文件,文本输入,或者应用程序希望接受到的其他用户输入值)
✅ States:状态就是应用程序的状态,会根据事件收到的响应而更改
BLoC 管理着事件 events
和状态 state
,比如,它接受一系列事件流,并将它们转化为状态流作为输出。
创建一个事件
dart
@immutable
abstract class AppBlocEvent {
const AppBlocEvent();
}
@immutable
class ChangeTextEvent extends AppBlocEvent {
const ChangeTextEvent();}
上面我们创建了一个 ChangeTextEvent
,当按钮被点击后触发。
我们有一个 AppBlocEvent
的抽象类,因为 BLoC
希望是单个事件被添加到流中。因为一个应用程序中会有很多事件,我们创建一个抽象类并继承它,并在需要处理和传递多个事件给 BLoC
时进行扩展。
创建一个状态
dart
@immutable
class AppState extends Equatable {
final int index;
final String text;
const AppState.empty()
: index = 0,
text = 'Initial Text';
const AppState({
required this.index,
required this.text,
});
@override
List<Object> get props => [index, text];
}
相似的,在这个应用程序中,我们可以创建不同的状态。我们并没有很多状态。因此,我们需要创建一个单独的 state
来管理应用程序;然而,我们可以创建多个状态,就像事件那样,通过创建一个 appstate
的抽象方法,并在我们自定义的状态中继承。
✅ AppState.empty
就是当应用程序初始加载时的初始状态
✅ Equatable
(获取属性)用于比较状态。如果它们相等,将用于测试 bloc
使用 BLoC 模式进行 Event
和 State
管理
dart
class AppBlocBloc extends Bloc {
final List textList = [
'Initial Text',
'Changed Text',
'Changed Again',
];
AppBlocBloc() : super(const AppState.empty()) {
on((event, emit) {
try {
int newIndex = state.index + 1;
if(newIndex >= textList.length) {
newIndex = 0;
}
emit(
AppState(
index: newIndex,
text: textList[newIndex],
),
);
} on Exception catch (e) {
// ignore: avoid_print
print(e);
}
});
}
}
解析
该部分包含我们应用程序的业务逻辑。
✅ 当 ChangeTextEvent
通过按钮点击添加,BLoC
则运行,并接收到该事件。比如,任何你想通过切换事件的信息,我们都可以通过 this
获取(像 event.any_info
- 我们必须相应更改事件),发射 emit
该特定事件对应的状态。
✅ state.index
让我们获取应用程序当前的状态
✅ emit(AppState(...))
:emit(...)
用于输出一个新的状态,这会导致 build()
函数重新构建
将这些碎片拼接起来。
到目前为止,events
,states
,bloc
和我们应用程序的 UI
并没有联系起来。下面让我们将这些碎片拼接起来。
提供我们的 BLoC
dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:text_change/text_controller.dart';
import 'bloc/app_bloc_bloc.dart';
import 'bloc/app_bloc_state.dart';
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider(
create: (context) => AppBlocBloc(),
child: Scaffold(
appBar: AppBar(
title: const Text('Text Change'),
),
body: BlocConsumer(
listener: (context, state) {},
builder: (context, state) {
return TextController(
text: state.text,
);
},
),
),
),
);
}
}
解析:App.dart
BlocProvider(...) :我们使用它来提供我们 bloc
一个实例,通过在应用程序的根替换它,这样我们在应用程序中都能获取它。
✅ create :创建我们 AppBlocBloc
一个实例
BlocConsumer(...):所有事情发生的地方。
✅ 它有一个 listener
的属性,用来监听状态的更改,并且能以特定方式对特定状态及其变化作出反应。
✅ builder :职责是构建 UI
,并且当状态更改时会重建。blocConsumer
还包含了 listenWhen
和 buildWhen
,正如其名称那样,它们可以根据指定的状态进行定制化响应
触发 Event 和 State
dart
class TextChangeController extends StatelessWidget {
final String text;
const TextChangeController({Key? key, required this.text}) : super(key: key);
@override
Widget build (BuildContext context) {
return Column
children: [
TextChange(
text: text,
), // TextChange
ElevatedButton(
onPressed: () =>
context.read().add(const ChangeTextEvent()),
child: const Text('Change Text'),
), // ElevatedButton
), // [ ]
); // Column
)
)
这里我们添加 ChangeTextEvent
到事件流中,因此切换状态更改会造成 BlocConsumer
中的 builder()
重构,然后更改屏幕上显示的文本。
就是这样!!通过分离 UI
和业务逻辑,我们可以更改 UI
代码并直接插入 BLoC
。同样生效。
测试 BLoC 设计模式
为了测试 bloc
,我们需要安装以下两个包:
✅ bloc_test
✅ flutter_test
在测试 test
文件夹中,创建名为 app_bloc_test.dart
的文件,并开始编写测试。
我们将测试两个条件:
✅ 应用程序的初始状态,比如 AppSate.empty()
✅ 当点击按钮,更改状态
如下 😊😊
dart
void main() {
blocTest(
'Initial State',
build: () => AppBlocBloc(),
verify: (appState) =>
expect(appState.state, const AppState.empty(), reason: 'Initial State'),
);
blocTest(
'emits [MyState] when MyEvent is added.',
build: () => AppBlocBloc(),
act: (bloc) => bloc.add(const ChangeTextEvent()),
expect: () => const [
AppState(
index: 1,
text: 'Changed Text',
),
],
);
}
解析
✅ blocTest
来自 bloc_test
包
✅ build()
:它会返回 AppBlocBloc()
一个实例
✅ verify
和 expect
正如名字那样,它们匹配状态 expect(actual, matcher, reason)
✅ act
:添加事件到时间流中
Github Repository: Flutter BLoC Simple Example
相关 github
- flutter-bloc-demo。
总结
希望该教程对你开始学习使用 BLoC
模式管理状态有所帮助。