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 和业务逻辑分离的,会有真正的网络请求和页面的刷新。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值