Flutter 87: 初识状态管理 Bloc (二)

简介: 0 基础学习 Flutter,第八十七步:简单了解 FlutterBloc 状态管理 (二)!

      小菜前两天刚学习了基本的 Bloc 状态管理,其中 UI 通过 setState() 方式更新数据,今天进一步了解进阶版的 FlutterBloc 状态管理;

FlutterBloc

      FlutterBloc 可以更便利的实现 Bloc,主要是为了与 Bloc 共同使用而构建的;同样需要提前了解几个概念;小菜继续以上一节中的 Demo 进行扩展,添加了 Number 的递增和递减;

BlocBuilder

      BlocBuilder 小菜理解为 Bloc 构造器,主要用于构建 Widget 以响应新的状态,相较于 StreamBuilder 更便捷;可替代小菜上一节使用的 setState()

const BlocBuilder({
    Key key,
    @required this.builder,
    B bloc,
    BlocBuilderCondition<S> condition,
})

      分析源码可知,builder 用于相应状态的 Widgetbloc 为当前提供的范围仅限于单个 Widget 且无法通过父级 BlocProvider 和当前级访问的 Bloc 时才使用;而 condition 为可选的过度细粒度,包括两个参数,之前的状态和当前的状态,返回值为 Boolean 类型,true 为更新状态重建 Widgetfalse 时不重新构建;

@override
Widget build(BuildContext context) {
  return BlocBuilder<NumberBloc, int>(
      bloc: _numBloc,
      condition: (previousState, state) {
        print('BlocPage.condition->$previousState==$state');
        return state <= 30 ? true : false;
      },
      builder: (context, count) {
        return Scaffold(
            appBar: AppBar(title: Text('Bloc Page')),
            body: Center(
                child: Column( mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                  Text('当 Number > 30 时,不进行变更', style: TextStyle(fontSize: 20.0, color: Colors.blue)),
                  SizedBox(height: 20.0),
                  Text('当前 Number = ${_numBloc.state}', style: TextStyle(fontSize: 20.0, color: Colors.blue))
                ])),
            floatingActionButton: Column(
                crossAxisAlignment: CrossAxisAlignment.end,
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  FloatingActionButton(
                      heroTag: 'addTag', child: Icon(Icons.add),
                      onPressed: () => _numBloc.add(NumberEvent.addEvent)),
                  SizedBox(height: 20.0),
                  FloatingActionButton(
                      heroTag: 'removeTag', child: Icon(Icons.remove),
                      onPressed: () => _numBloc.add(NumberEvent.removeEvent))
                ]));
      });
}

BlocProvider

      BlocProviderBloc 的供应者,创建 Bloc 并供应给其子控件树;

BlocProvider({
    Key key,
    @required Create<T> create,
    Widget child,
    bool lazy,
})

      简单了解源码可知,BlocProvider 通过 create 创建一个 Bloc;通过 child 设置用来响应状态的变更的 Widgetlazy 为是否懒创建(延迟创建),小菜理解的为是否在使用时再进行创建,默认为 true

class _BlocPageState extends State<BlocPage> {

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
        create: (BuildContext context) => NumberBloc(),
        child: BlocBuilder<NumberBloc, int>(
            condition: (previousState, state) {
          print('BlocPage.condition->$previousState==$state');
          return state <= 30 ? true : false;
        }, builder: (context, count) {
          return Scaffold(
              appBar: AppBar(title: Text('Bloc Page')),
              body: Center(
                  child: Column( mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                    Text('当 Number > 30 时,不进行变更', style: TextStyle(fontSize: 20.0, color: Colors.blue)),
                    SizedBox(height: 20.0),
                    Text('当前 Number = ${BlocProvider.of<NumberBloc>(context).state}', style: TextStyle(fontSize: 20.0, color: Colors.blue))
                  ])),
              floatingActionButton: Column(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: <Widget>[
                    FloatingActionButton(
                        heroTag: 'addTag', child: Icon(Icons.add),
                        onPressed: () => BlocProvider.of<NumberBloc>(context).add(NumberEvent.addEvent)),
                    SizedBox(height: 20.0),
                    FloatingActionButton(
                        heroTag: 'removeTag', child: Icon(Icons.remove),
                        onPressed: () => BlocProvider.of<NumberBloc>(context).add(NumberEvent.removeEvent))
                  ]));
        }));
  }
}

BlocListener

      BlocListenerBlocBuilder 应用有相似之处;其中 listener 用于监听状态变更,可在此做出相应的业务处理;

class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S> with BlocListenerSingleChildWidget {
  final Widget child;
  const BlocListener({
    Key key,
    @required BlocWidgetListener<S> listener,
    B bloc,
    BlocListenerCondition<S> condition,
    this.child,
  })
}

      简单分析源码可得:

  1. childBlocListener 提供的 Widget 用来响应状态的变更;
  2. blocBlocBuilder 对应的 bloc 用法相同,如果省略了 bloc 参数,BlocListener 将使用 BlocProvider 和当前函数自动执行查找 BuildContext
  3. condition 为可选的过度细粒度,包括两个参数,之前的状态和当前的状态,返回值为 Boolean 类型,true 为进行 listener 的监听,false 时过滤掉 listener 的监听;此时的过滤与 BlocBuilder 中的 condition 过滤无关;
  4. listener 在每次状态变更时调用,其中包括上下文环境和当前状态两个参数;
@override
Widget build(BuildContext context) {
  return BlocListener<NumberBloc, int>(
      bloc: _numBloc,
      listener: (context, count) {
        print('BlocPage.listene->$count');
      },
      condition: (previousState, state) {
        print('BlocPage.BlocListener.condition->$previousState==$state');
        return state <= 20 ? true : false;
      },
      child: BlocBuilder<NumberBloc, int>(
          bloc: _numBloc,
          condition: (previousState, state) {
            print('BlocPage.condition->$previousState==$state');
            return state <= 30 ? true : false;
          },
          builder: (context, count) {
            return Scaffold(
                appBar: AppBar(title: Text('Bloc Page')),
                body: Center(
                    child: Column( mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                      Text('当 Number > 20 时,BlocListener 过滤 listener 监听,与 BlocBuilder 中过滤的状态无关', style: TextStyle(fontSize: 20.0, color: Colors.red)),
                      SizedBox(height: 20.0),
                      Text('当 Number > 30 时,Number 不进行变更', style: TextStyle(fontSize: 20.0, color: Colors.green)),
                      SizedBox(height: 20.0),
                      Text('当前 Number = ${_numBloc.state}', style: TextStyle(fontSize: 20.0, color: Colors.blue))
                    ])),
                floatingActionButton: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
                    children: <Widget>[
                      FloatingActionButton(
                          heroTag: 'addTag', child: Icon(Icons.add),
                          onPressed: () => _numBloc.add(NumberEvent.addEvent)),
                      SizedBox(height: 20.0),
                      FloatingActionButton(
                          heroTag: 'removeTag', child: Icon(Icons.remove),
                          onPressed: () => _numBloc.add(NumberEvent.removeEvent))
                    ]));
          }));
}


TestCode

      小菜在测试过程中遇到一些小问题,仅简单记录一下,以防忘记;

Q1: There are multiple heroes that share the same tag within a subtree.

      小菜在扩展上一节的 Demo 时,点击进入页面时会黑屏,提示如下错误;

A1: 在 FloatingActionButton 中添加 heroTag 区分

      以前在学习 Hero Animation 时,在同一个 Page 页面不能用两个相同的 heroTag,小菜这次忽略了 FloatingActionButton 中也应用了 Hero 动画,需要区分一下即可;

floatingActionButton: Column(
    crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
    children: <Widget>[
      FloatingActionButton(
          heroTag: 'addTag', child: Icon(Icons.add),
          onPressed: () => _numBloc.add(NumberEvent.addEvent)),
      FloatingActionButton(
          heroTag: 'removeTag', child: Icon(Icons.remove),
          onPressed: () => _numBloc.add(NumberEvent.removeEvent))
    ]));

Q2: BlocProvider.of() called with a context that does not contain a Bloc of type ...

      小菜在刚开始尝试 BlocProvider.of(context) 方式获取 Bloc 时报如下错误;

A2: 在 build() 外创建或通过如下方式创建,并建议与 BlocBuilder 成对设置

// build() 方法外创建
NumberBloc _numBloc;
@override
void initState() {
  super.initState();
  _numBloc = NumberBloc();
}

// BlocProvider create() 创建
BlocProvider(
  create: (BuildContext context) => NumberBloc(),
  child: ....
);

      小菜刚接触 FlutterBloc 很多高级用法还没涉及到,下一节会尝试多种 Bloc 共同使用的场景,对各方面理解还不到位,如有错误请多多指导!

来源: 阿策小和尚

目录
相关文章
|
13天前
|
存储 Shell 开发工具
Flutter&鸿蒙next 中使用 MobX 进行状态管理
本文介绍了如何在 Flutter 中使用 MobX 进行状态管理。MobX 是一个基于观察者模式的响应式编程库,通过 `@observable` 和 `@action` 注解管理状态,并使用 `Observer` 小部件自动更新 UI。文章详细讲解了 MobX 的核心概念、如何集成到 Flutter 项目中以及具体的代码示例。适合希望在 Flutter 应用中实现高效状态管理的开发者阅读。
86 9
|
13天前
|
存储 开发者
Flutter&鸿蒙next 使用 BLoC 模式进行状态管理详解
本文详细介绍了如何在 Flutter 中使用 BLoC 模式进行状态管理。BLoC 模式通过将业务逻辑与 UI 层分离,利用 Streams 和 Sinks 实现状态管理和 UI 更新,提高代码的可维护性和可测试性。文章涵盖了 BLoC 的基本概念、实现步骤及代码示例,包括定义 Event 和 State 类、创建 Bloc 类、提供 Bloc 实例以及通过 BlocBuilder 更新 UI。通过一个简单的计数器应用示例,展示了 BLoC 模式的具体应用和代码实现。
67 1
|
15天前
|
开发工具 开发者
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
本文深入探讨了 Flutter 中 Provider 的高级用法,涵盖多 Provider 组合、Selector 优化性能、ChangeNotifierProxyProvider 管理依赖关系以及自定义 Provider。通过这些技巧,开发者可以构建高效、可维护的响应式应用。
62 2
|
28天前
|
开发工具 开发者
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
|
13天前
|
缓存 JavaScript API
Flutter&鸿蒙next 状态管理框架对比分析
在 Flutter 开发中,状态管理至关重要,直接影响应用的性能和可维护性。本文对比分析了常见的状态管理框架,包括 setState()、InheritedWidget、Provider、Riverpod、Bloc 和 GetX,详细介绍了它们的优缺点及适用场景,并提供了 Provider 的示例代码。选择合适的状态管理框架需考虑应用复杂度、团队熟悉程度和性能要求。
81 0
|
28天前
【Flutter】状态管理:Provider状态管理
【Flutter】状态管理:Provider状态管理
14 0
|
1月前
|
UED
flutter:动画&状态管理 (十三)
本文档介绍了Flutter中`animatedList`的使用方法和状态管理的示例。`animatedList`用于创建带有动画效果的列表,示例代码展示了如何添加、删除列表项,并执行相应的动画效果。状态管理部分通过一个简单的点击切换颜色的示例,演示了如何在Flutter中管理组件的状态。
|
3月前
Flutter 状态管理新境界:多Provider并行驱动UI
Flutter 状态管理新境界:多Provider并行驱动UI
62 0
|
3月前
|
Dart API
状态管理的艺术:探索Flutter的Provider库
状态管理的艺术:探索Flutter的Provider库
48 0
|
4月前
|
容器
flutter 布局管理【详解】
flutter 布局管理【详解】
38 3