Flutter 状态管理之Bloc上

Posted 小猪快跑22

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 状态管理之Bloc上相关的知识,希望对你有一定的参考价值。

前言:Flutter 的状态管理插件有很多,比如 ProviderGetX 还有本篇要讲述的 Bloc 。Bloc 目前最新的版本是 flutter_bloc: ^8.0.1

BLoC 依赖 Stream和 StreamController实现,组件通过Sinks发送更新状态的事件,然后再通过 Streams通知其他组件更新。事件处理和通知刷新的业务逻辑都是由 BLoC 完成,从而实现业务逻辑与 UI 层的分离,并且逻辑部分可以做到复用。

之前我们更新数据通常是通过 setState 的方式实现的,这种会刷新整个页面,而使用 Bloc 只会刷新想要更新的UI部分。下面会通过几个例子来说明下。

一、使用 Bloc 来实现计数器且把数据传递给跳转的页面

计算器要实现加减一的功能,所以先定义2个 Event,且都继承 CounterEvent ,如下:

// 定义 event 的基类
abstract class CounterEvent 
// 加1的 event
class IncrementEvent extends CounterEvent 
// 减1的event
class DecrementEvent extends CounterEvent 

然后定义一个 CounterBloc 如下:

/// 表示通过 Bloc 发送的事件只能是 CounterEvent, 返回值是 int
class CounterBloc extends Bloc<CounterEvent, int> 
  CounterBloc(int initialState) : super(initialState) 
    /// 减1的事件就是把当前的 state - 1
    on<DecrementEvent>((event, emit) 
      /// 把对应的状态发送出去,在页面中就可以通过 BlocBuilder 来观察数据的改变
      emit(state - 1);
    );
    /// 加1的事件就是把当前的 state + 1
    on<IncrementEvent>((event, emit) 
      emit(state + 1);
    );
  

页面具体的代码如下:

void main() 
  runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      /// 要使用 BlocProvider 来提供 Bloc 
      home: BlocProvider<CounterBloc>(
        create: (BuildContext context) 
          return CounterBloc(0);
        ,
        child: CounterPage(),
      ),
    );
  

class CounterPage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context);
    print(
        "---------------------- CounterPage build ----------------------- $counterBloc.hashCode");
    return Scaffold(
      appBar: AppBar(
        title: Text('test bloc '),
      ),
      body: Container(
        width: double.infinity,
        child: Column(
          children: [
            /// 当增加或者减少计数时,只会局部更新 BlocBuilder ,不会整个刷新 build
            BlocBuilder<CounterBloc, int>(
              builder: (BuildContext context, int count) 
                print(
                    "---------------------- BlocBuilder build -----------------------");
                return Text(
                  '当前计数: $count',
                  style: TextStyle(fontSize: 24),
                );
              ,
              buildWhen: (previous, next) 
                /// 这样写只有 increment 才有用,用来控制触发刷新的逻辑
                return previous < next;
              ,
            ),

            SizedBox(
              height: 12,
            ),
            ElevatedButton(
              onPressed: () 
                counterBloc.add(IncrementEvent());
              ,
              child: Text(
                'increment',
              ),
            ),
            ElevatedButton(
              onPressed: () 
                counterBloc.add(DecrementEvent());
              ,
              child: Text(
                'decrement',
              ),
            ),
            ElevatedButton(
              onPressed: () 
                /// 需要 BlocProvider 的 context , 且 BlocProvider 的  create 中返回当前的 counterBloc
                /// 如果你在 create 中 重新 new 一个 CounterBloc ,那么在 page2 中增加计数,不会刷新本页面的计数
                Navigator.push(context, MaterialPageRoute(builder: (context) 
                  return BlocProvider<CounterBloc>(
                    create: (BuildContext context) 
                      return counterBloc;
                    ,
                    child: BlocPage2(),
                  );
                ));
              ,
              child: Text(
                'jump page 2',
              ),
            ),
          ],
        ),
      ),
    );
  

其中的 buildWhen 是过滤触发条件的,代码中有注释了。BlocPage2 的代码如下:

class BlocPage2 extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context);
    print('xxxxxxxxxxxxxxx $counterBloc.hashCode ');

    return Scaffold(
      appBar: AppBar(
        title: Text('BlocPage2'),
      ),
      body: Container(
        width: double.infinity,
        child: Column(
          children: [
            BlocBuilder<CounterBloc, int>(
                builder: (BuildContext context, int count) 
              return Text(
                'page2 当前计数: $count',
                style: TextStyle(fontSize: 24),
              );
            ),
            ElevatedButton(
                onPressed: () 
                  counterBloc.add(IncrementEvent());
                ,
                child: Text('add')),
          ],
        ),
      ),
    );
  

BlocPage2 中改变了计数,返回到 CounterPage 页面时计数会同步更新。

结语:本篇这里就结束了,下篇会通过一个真正的例子来说明 Bloc 是怎么做到 UI 和业务逻辑分离的,会有真正的网络请求和页面的刷新。

以上是关于Flutter 状态管理之Bloc上的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 状态管理之Bloc上

Flutter Bloc 状态管理

Flutter 状态管理BLoC

Flutter - flutter_bloc状态管理

Flutter 状态管理(BloC):无状态与有状态小部件

Flutter BLoC:管理主要数据类型的状态