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 模式管理状态有所帮助。

相关推荐
涔溪36 分钟前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun1 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇1 小时前
ES6进阶知识一
前端·ecmascript·es6
杨哥带你写代码2 小时前
网上商城系统:Spring Boot框架的实现
java·spring boot·后端
camellias_2 小时前
SpringBoot(二十一)SpringBoot自定义CURL请求类
java·spring boot·后端
渗透测试老鸟-九青2 小时前
通过投毒Bingbot索引挖掘必应中的存储型XSS
服务器·前端·javascript·安全·web安全·缓存·xss
龙猫蓝图2 小时前
vue el-date-picker 日期选择器禁用失效问题
前端·javascript·vue.js