Day 27 | 状态管理 - BLoC基本介绍

在刚开始学习Flutter时如果读到有关状态管理的文章大部分都会是与「BLoC」相关的内容,虽然真的是有点复杂,但感觉还是得尝试看看「BLoC」到底有什麽优点可以是Flutter中最多人推崇的架构(但也许现在的风向是GetX比较受欢迎)

那什麽是BLoC呢?

所谓 BLoC 就是 Business Logic Components的缩写,BLoC其实是一种 design pattern,而不只是套件。他的目标是在分离「画面」以及「商业逻辑」,其实就跟我们之前在讲状态管理时的目标一样。

BLoC的核心概念是将所有「事件」都视作stream:

https://ithelp.ithome.com.tw/upload/images/20211010/201129069Mlv9OatL9.png

我们透过送出「事件」然後会送到「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 的变化来进行画面重新渲染。

整个流程就会是:
按按钮 → 送出事件到 counterEventSinkcounterEventSink 收到事件後会送出值到 counterSinkstreamCounter 响应变化。


今天的程序码:
https://github.com/zxc469469/flutter_rest_api_playground/tree/Day27

明天就会开始使用套件来实作BLoC


参考资料:

  1. https://medium.com/flutter-community/flutter-bloc-with-streams-6ed8d0a63bb8
  2. https://blog.zcychen.com/post/archived/2020/03/bloc-design-pattern/
  3. https://juejin.cn/post/6844903821336903694#heading-1

<<:  Day 26:Container != Docker Container

>>:  【Day 25】Go 与 Python gRPC 小练习

Jquery/JS 使用Input 输入生日并限制年龄

客户需求如下 不要用datepicker点,说是手机太小不好点+老人不会点 资料库格式为西元年,但一...

Material UI in React [Day 2] Layout (Container & Box)

今天会介绍Material UI 的排版系统,可以进入官网从侧边栏可以看到以下画面: Contain...

【Day7】ERP核心模组篇-Purchase

#odoo #开源系统 #数位赋能 #E化自主 昨天我们讨论了销售模组的运作,我们接下来进入到采购模...

Day 29 - Summary

本文将於赛後同步刊登於笔者部落格 有兴趣学习更多 Kubernetes/DevOps/Linux 相...

观赏鱼辨识系统说明-Day 01

观赏鱼辨识系统说明-Day 01 在接下来的30天会制作一个完整的系统包含前端-手机/网页,後端-N...