Flutter BLoC 教程:使用 BLoC 模式的状态管理

原文链接:# 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 图。

初始化设置

  1. 确保你在编辑器中添加了 bloc 扩展;它将帮助你创建项目所需所有标准化代码和文件(操作步骤:右击 lib 文件夹,然后它会为我们的项目提供生成 bloc 选项)。
  2. 确保与我的 pubspec.yaml 文件匹配,避免出现任何问题。

弄清 BLoC 概念:Events 和 States

我们进入正题。

为了搞清楚 bloc 是怎么工作的,我们需要知道 eventsstate 是什么。

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 模式进行 EventState 管理

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() 函数重新构建

将这些碎片拼接起来

到目前为止,eventsstatesbloc 和我们应用程序的 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 还包含了 listenWhenbuildWhen,正如其名称那样,它们可以根据指定的状态进行定制化响应

触发 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() 一个实例

verifyexpect 正如名字那样,它们匹配状态 expect(actual, matcher, reason)

act:添加事件到时间流中

Github Repository: Flutter BLoC Simple Example

相关 github - flutter-bloc-demo

总结

希望该教程对你开始学习使用 BLoC 模式管理状态有所帮助。

相关推荐
傻小胖6 分钟前
React 脚手架配置代理完整指南
前端·react.js·前端框架
重整旗鼓~8 分钟前
2.flask中使用装饰器统一验证用户登录
后端·python·flask
EterNity_TiMe_17 分钟前
【论文复现】农作物病害分类(Web端实现)
前端·人工智能·python·机器学习·分类·数据挖掘
kirk_wang24 分钟前
Flutter适配HarmonyOS实践
flutter·华为·harmonyos
it噩梦33 分钟前
springboot 工程使用proguard混淆
java·spring boot·后端
余生H37 分钟前
深入理解HTML页面加载解析和渲染过程(一)
前端·html·渲染
吴敬悦1 小时前
领导:按规范提交代码conventionalcommit
前端·程序员·前端工程化
ganlanA1 小时前
uniapp+vue 前端防多次点击表单,防误触多次请求方法。
前端·vue.js·uni-app
卓大胖_1 小时前
Next.js 新手容易犯的错误 _ 性能优化与安全实践(6)
前端·javascript·安全
m0_748246351 小时前
Spring Web MVC:功能端点(Functional Endpoints)
前端·spring·mvc