flutter bloc使用方法以及源码解析

flutter bloc, provider和getx异同点

一、bloc,环境配置

pubspec.yaml

dependencies: 
    bloc: ^8.1.0  
    flutter_bloc: ^8.1.0  
    equatable: ^2.0.5 // 重写equals和hashcode

为什么使用bloc

Bloc可以比较轻松地将展示层的代码与业务逻辑分开,从而使您的代码快速,易于测试且可重复使用。

在构建高质量的应用程序时,状态管理就变得至关重要。

我们作为开发人员总是希望:

  • 知道我们的应用程序在任何时间下的状态。
  • 轻松地测试每种情况,以确保我们的应用程序能够正确响应。
  • 记录应用程序中的每个用户交互,以便我们做出数据驱动的决策。
  • 尽可能高效地工作,并在我们的应用程序内以及跨其他应用程序重用组件。
  • 有许多开发人员遵循相同的模式和约定在一个代码库中无缝工作。
  • 开发快速反应的应用程序。

Bloc就是在满足所有这些需求以及更多其他需求的情况下被设计出来的。

状态管理的解决方案有很多,决定使用哪种解决方案可能会是一项艰巨的任务。

Bloc在设计时考虑到了以下三个核心价值:

  • 简单
    易于理解,可供技能水平不同的开发人员使用。
  • 强劲
    通过将它们组成更小的组件,帮助制作出色而复杂的应用程序。
  • 可测试
    轻松测试应用程序的各个方面,以便我们可以自信地进行迭代。

Bloc试图通过调节何时可以发生状态更改并在整个应用程序中强制采用一种更改状态的方式来使状态更改可预测。

流(Stream) 是一系列异步的数据.

使用bloc之前,需要对stream有一定了解, stream的简单用法如下:

Stream countStream(int max) async* {
    for (int i = 0; i < max; i++) {
        yield i;
    }
 }

第一种用法 cubit


Cubit 类继承自 BlocBase 的类,并且可以扩展到管理任何类型的状态。

cubit_architecture_full.png

一个 Cubit 可以公开触发状态变化的函数。

状态是从 Cubit 中输出的,代表应用程序状态的一部分。可以通知 UI 组件状态,并根据当前状态重绘其自身的某些部分。

创建一个 Cubit


class CounterCubit extends Cubit {
    CounterCubit() : super(0);
 }

创建 Cubit 时,我们需要定义 Cubit 将要管理的状态类型。对于上面的 CounterCubit,状态可以通过 int 来表示,但在更复杂的情况下,可能有必要使用 class(类)而不是原始类型。

创建 Cubit 时,我们需要做的第二件事是指定初始状态。我们可以通过使用初始状态的值调用 super 来实现。在上面的代码段中,我们在内部将初始状态设置为 0,但我们也可以通过接受外部值来使 Cubit 更加灵活:

class CounterCubit extends Cubit {
     CounterCubit(int initialState) : super(initialState);
}

我们可以实例化具有不同初始状态的 CounterCubit 实例,例如:

final cubitA = CounterCubit(0); // state starts at 0
final cubitB = CounterCubit(1); // state starts at 1

状态变化


每个 Cubit 都有能力通过 emit 输出一个新状态。

class CounterCubit extends Cubit {
    CounterCubit() : super(0);

    void increment() => emit(state + 1);
 }

在上面的代码片段中,CounterCubit 公开了一个名为 increment 的公共方法,可以从外部调用该方法,以通知 CounterCubit 增加其状态。当调用 increment 时,我们可以通过 state 获取器访问 Cubit 的当前状态,并通过向当前状态加 1 来发出 emit 新状态。

emit 函数受到保护,这意味着它只能在 Cubit 内部使用。

使用cubit


基础用例

void main() {
    final cubit = CounterCubit();
    print(cubit.state); // 0
    cubit.increment();
    print(cubit.state); // 1
    cubit.close();
 }

流的用例

Future main() async {
  final cubit = CounterCubit();
  final subscription = cubit.stream.listen(print); // 1
  cubit.increment();
  await Future.delayed(Duration.zero);
  await subscription.cancel();
  await cubit.close();
}

在上面的代码段中,我们正在订阅 CounterCubit,并在每次状态更改时调用 print 函数。然后,我们调用 increment 函数,它将发出一个新状态。最后,当我们不再希望接收更新并关闭 Cubit 时,我们在 subscription 上调用 cancel。

观察 Cubit


当 Cubit 发出新状态时,将有一个 改变 发生。我们可以通过重写 onChange 方法来观察给定 Cubit 的所有变化。

class CounterCubit extends Cubit {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);

  @override
  void onChange(Change change) {
    super.onChange(change);
    print(change);
  }
}

然后,我们可以与 Cubit 交互并观察所有输出到控制台的改变。

void main() {
  CounterCubit()
    ..increment()
    ..close();
}

上面的示例将输出的结果:

Change { currentState: 0, nextState: 1 }

注意:在 Cubit 状态更新之前发生 Change 改变。一个 改变 由 currentState 和 nextState 组成。

BlocObserver (Bloc观察者)


使用 bloc 库的另一个好处是,我们可以在一处访问所有 变化。即使在此应用程序中只有一个 Cubit,在大型应用程序中也很常见,有许多 Cubits 管理应用程序状态的不同部分。

如果我们希望能够对所有变化做出响应,我们可以简单地创建自己的 BlocObserve (Bloc观察者)来观察改变。

class SimpleBlocObserver extends BlocObserver {
    @override
     void onChange(BlocBase bloc, Change change) {
        super.onChange(bloc, change);
        print('${bloc.runtimeType} $change');
     }
}

注意:我们要做的就是继承 BlocObserver 类并重写 onChange 方法。

为了使用 SimpleBlocObserver,我们只需要调整 main 函数:

void main() {
    Bloc.observer = SimpleBlocObserver();
    CounterCubit()
        ..increment()
        ..close();  
}

上面的代码段将输出的结果:

Change { currentState: 0, nextState: 1 }
CounterCubit Change { currentState: 0, nextState: 1 }

错误处理


每个 Cubit 都有一个 addError 方法,该方法可用于指示发生了错误。

class CounterCubit extends Cubit {
    CounterCubit() : super(0);

    void increment() {
        addError(Exception('increment error!'), StackTrace.current);
        emit(state + 1);
     }

     @override
    void onChange(Change change) {
        super.onChange(change);
        print(change);
    }

    @override
    void onError(Object error, StackTrace stackTrace) {
        print('$error, $stackTrace');
        super.onError(error, stackTrace);
     }
 }

注意:onError 方法可以在 Cubit 中被重写,以处理特定 Cubit 的所有错误。

也可以在 BlocObserver 中重写 onError 方法以全局处理所有报告的错误。

class SimpleBlocObserver extends BlocObserver {
    @override
    void onChange(BlocBase bloc, Change change) {
        super.onChange(bloc, change);
        print('${bloc.runtimeType} $change');
     }

    @override
     void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
        print('${bloc.runtimeType} $error $stackTrace');
        super.onError(bloc, error, stackTrace);
     }
 }

如果我们再次运行同一程序,我们应该看到以下输出结果:

Exception: increment error!, #0      CounterCubit.increment (file:///main.dart:21:56)
#1      main (file:///main.dart:41:7)
#2      _startIsolate. (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

CounterCubit Exception: increment error! #0      CounterCubit.increment (file:///main.dart:21:56)
#1      main (file:///main.dart:41:7)
#2      _startIsolate. (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

Change { currentState: 0, nextState: 1 }
CounterCubit Change { currentState: 0, nextState: 1 }

注意:与 onChange 一样,内部 onError 重写在全局 BlocObserver 重写之前被调用。

在应用中的使用,和BlocProvider结合使用,以计数器为例。


第一步:初始化BlocProvider

void main() { 
    Bloc.observer = SimpleBlocObserver();
    runApp(MyApp());
}

class SimpleBlocObserver extends BlocObserver {
      @override
      void onChange(BlocBase bloc, Change change) {
        super.onChange(bloc, change);
        print('${bloc.runtimeType} $change');
      }

      @override
      void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
        print('${bloc.runtimeType} $error $stackTrace');
        super.onError(bloc, error, stackTrace);
      }
}

class MyApp extends StatelessWidget {
    @overrideWidget build(BuildContext context) {  
        return MaterialApp(    
            title: 'Flutter Demo',    
            theme: ThemeData(primarySwatch: Colors.blue, ),   
            home: _isCubit(false), 
        );
    }

    _isCubit(bool isCubit) {  
        //第一步:BlocProvider  
        return isCubit 
            ? BlocProvider(    
                create: (context) => CounterCubit(),    
                child: CounterCubitPage(),  
              ) 
            : BlocProvider(    
                create: (context) => CounterBloc(),    
                child: CounterBlocPage(), 
              );
    }
}

第二步:初始化BlocBuilder

第三步:使用 BlocProvider.of方法查找对应的CounterCubit对象更新数据


class CounterCubitPage extends StatelessWidget {  
    @override  Widget build(BuildContext context) {    
        return Scaffold(appBar: AppBar(title: Text("计数器"),),     
            body: Center(
                child: Column(
                        children: [
                                    BlocBuilder(
                                        builder: (context, state) => Text("cubit:${BlocProvider.of(context).state}", 
                                        style:TextStyle(color:Colors.black),)
                                    ),            
                                    Spacer(),           
                                    Text("111", style: TextStyle(color: Colors.black),),  
                                    Spacer(),           
                                    ElevatedButton(onPressed() => BlocProvider.of(context).increment(), child:Text("加法"),),            
                                    Spacer(),            
                                    ElevatedButton(onPressed: () => BlocProvider.of(context).decrement(), child:Text("减法"),),),   
                        ],  
              ),    
       );  
    }
}

使用到的CounterCubit如下:


class CounterCubit extends Cubit { 
    CounterCubit() : super(0);  
    void increment() {    
        if(state == 3) {      
            print("test:: increment: 3333");      
            addError(FormatException("format exception"));
        }    
        emit(state + 1);  
    }  
    
    void decrement() => emit(state - 1);
    
    @override  void onChange(Change change) {    
        super.onChange(change);    
        print("test:: onChange: ${change.currentState}");  
    }  
    
    @override  void onError(Object error, StackTrace stackTrace) {
        print("test:: onError: ${error.toString()}, stackTrace: ${stackTrace.toString()}");   
        super.onError(error, stackTrace); 
    }
}

第二种用法 bloc


Bloc 是一个更高级的类,它依赖事件来触发状态的改变而不是函数。 Bloc 也扩展了 BlocBase ,这意味着它有着类似于 Cubit 的API。然而, Blocs 不是在 Bloc 上调用函数然后直接发出一个新的状态,而是接收事件并且将传入的事件转换为状态传出。

bloc_architecture_full.png

创建一个 Bloc 类似于创建一个 Cubit,除了定义我们将要管理的状态外,我们还必须定义 Bloc 使其能够处理事件。

事件是将输入进 Bloc 中。通常是为了响应用户交互(例如按钮按下)或生命周期事件(例如页面加载)而添加它们。

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
    CounterBloc() : super(0);
}

就像创建 CounterCubit 一样,我们必须指定初始状态,方法是通过 super 方法将其传递给父类。

状态改变


Bloc 要求我们通过 on 上注册事件处理程序 API, 而不是在 Cubit 中的功能. 事件处理程序负责将任何传入事件转换为零或多个传出状态.

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) {
      // handle incoming `CounterIncrementPressed` event
    });
  }
}

提示: EventHandler 可以访问添加的活动以及一个 Emitter 它可以用于响应传入事件而发出零个或多个状态.

然后我们可以更新 EventHandler 来处理 CounterIncrementPressed 事件:

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) {
      emit(state + 1);
    });
  }
}

在上面的代码段中,我们已经注册了一个 EventHandler 句柄管理所有的 CounterIncrementPressed 事件. 每个 CounterIncrementPressed 事件我们可以通过 state getter 方法访问 bloc 的当前状态和通过 emit(state + 1) 改变状态.

注意:由于 Bloc 类继承了 BlocBase,因此我们可以随时通过 state getter 来访问 bloc 的当前状态,就像使用 Cubit 一样。

Bloc 和 Cubits 都会忽略重复的状态。如果我们产生或发出状态 State nextState 当 State == nextState 时,则不会发生状态变化。

使用 Bloc


创建 CounterBloc 的实例并将其使用!

基础用例

Future main() async {
  final bloc = CounterBloc();
  print(bloc.state); // 0
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  print(bloc.state); // 1
  await bloc.close();
}

在上面的代码片段中,我们首先创建一个 CounterBloc 实例。然后,我们打印 Bloc 的当前状态,该状态为初始状态(因为尚未发出新状态)。接下来,我们添加 CounterIncrementPressed 事件来触发状态改变。最后,我们再次打印从 0 到 1 的 Bloc 状态,并且在 Bloc 上调用 close 关闭内部状态流。

注意:添加了 await Future.delayed(Duration.zero) 以确保我们等待下一个事件循环迭代(允许 EventHandler 处理增量事件)

Stream 的用例


就像 Cubit 一样,Bloc 是 Stream 的一种特殊类型,这意味着我们还可以订阅 Bloc 来实时更新其状态:

Future main() async {
  final bloc = CounterBloc();
  final subscription = bloc.stream.listen(print); // 1
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  await subscription.cancel();
  await bloc.close();
}

在上面的片段中,我们订阅了 CounterBloc 并在每个状态改变时调用 print 。然后我们添加 CounterIncrementPressed 事件,该事件触发 on EventHandler 并且发出一个新的状态。最后,当我们不想再接收更新时,我们在订阅上调用 cancel ,并关闭 Bloc。

注意:在此示例中添加了 await Future.delayed(Duration.zero),以避免立即取消订阅。

观察一个 Bloc


由于所有 Bloc 都扩展了 BlocBase,因此我们可以使用 onChange 观察 Bloc 的所有状态变化。

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) => emit(state + 1));
  }

  @override
  void onChange(Change change) {
    super.onChange(change);
    print(change);
  }
}

接下来我们可以将 main.dart 更新为:

void main() {
  CounterBloc()
    ..add(CounterIncrementPressed())
    ..close();
}

现在,如果我们运行上面的代码片段,输出将是:

Change { currentState: 0, nextState: 1 }

Bloc 和 Cubit 之间的主要区别因素是,由于 Bloc 是事件驱动的,因此我们也能够捕获有关触发状态更改的信息。

我们可以通过重写 onTransition 来做到这一点。

从一种状态到另一种状态的转换称为 Transition。Transition 由当前状态,事件和下一个状态组成

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) => emit(state + 1));
  }

  @override
  void onChange(Change change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition transition) {
    super.onTransition(transition);
    print(transition);
  }
}

如果然后从前重新运行相同的 main.dart 代码段,则应看到以下输出:

Transition { currentState: 0, event: Increment, nextState: 1 }
Change { currentState: 0, nextState: 1 }

注意: onTransition 在 onChange 之前被调用,并且包含触发从 currentState 到 nextState 改变的事件。

BlocObserver (Bloc观察者)


和以前一样,我们可以在自定义 BlocObserver 中重写 onTransition,以观察从一个位置发生的所有过渡。

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }
  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print('${bloc.runtimeType} $transition');
  }
  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

我们可以像之前一样初始化 SimpleBlocObserver:

void main() {
  Bloc.observer = SimpleBlocObserver();
  CounterBloc()
    ..add(CounterIncrementPressed())
    ..close();  
}

现在,如果我们运行上面的代码片段,输出应如下所示:

Transition { currentState: 0, event: Increment, nextState: 1 }
CounterBloc Transition { currentState: 0, event: Increment, nextState: 1 }
Change { currentState: 0, nextState: 1 }
CounterBloc Change { currentState: 0, nextState: 1 }

注意: 首先调用 onTransition(在全局之前先于本地),然后调用 onChange。

Bloc 实例的另一个独特功能是,它们使我们能够重写 onEvent,无论何时将新事件添加到 Bloc 都会调用 onEvent。就像 onChange 和 onTransition 一样,onEvent 可以在本地或全局重写。

abstract class CounterEvent {}
class CounterIncrementPressed extends CounterEvent {}
class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) => emit(state + 1));
  }
  @override
  void onEvent(CounterEvent event) {
    super.onEvent(event);
    print(event);
  }
  @override
  void onChange(Change change) {
    super.onChange(change);
    print(change);
  }
  @override
  void onTransition(Transition transition) {
    super.onTransition(transition);
    print(transition);
  }
}
class SimpleBlocObserver extends BlocObserver {
  @override
  void onEvent(Bloc bloc, Object? event) {
    super.onEvent(bloc, event);
    print('${bloc.runtimeType} $event');
  }
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }
  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print('${bloc.runtimeType} $transition');
  }
}

我们可以像以前一样运行相同的 main.dart,并且应该看到以下输出:

Increment
CounterBloc Increment
Transition { currentState: 0, event: Increment, nextState: 1 }
CounterBloc Transition { currentState: 0, event: Increment, nextState: 1 }
Change { currentState: 0, nextState: 1 }
CounterBloc Change { currentState: 0, nextState: 1 }

注意:一旦添加事件,就会调用 onEvent。本地 onEvent 在 BlocObserver 中的全局 onEvent 之前被调用。

错误处理

就像 Cubit 一样,每个 Bloc 都有一个 addError 和 onError 方法。我们可以通过从 Bloc 内部的任何地方调用 addError 来表明发生了错误。然后我们可以像重写 Cubit 一样通过重写 onError 来对所有错误做出反应。

abstract class CounterEvent {}
class CounterIncrementPressed extends CounterEvent {}
class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) {
      addError(Exception('increment error!'), StackTrace.current);
      emit(state + 1);
    });
  }
  @override
  void onChange(Change change) {
    super.onChange(change);
    print(change);
  }
  @override
  void onTransition(Transition transition) {
    print(transition);
    super.onTransition(transition);
  }
  @override
  void onError(Object error, StackTrace stackTrace) {
    print('$error, $stackTrace');
    super.onError(error, stackTrace);
  }
}

如果我们重新运行与以前相同的 main.dart,我们可以看到报告错误时的样子:


Exception: increment error!, #0      new CounterBloc. (file:///main.dart:117:58)
#1      Bloc.on..handleEvent (package:bloc/src/bloc.dart:427:26)
#2      Bloc.on..handleEvent (package:bloc/src/bloc.dart:418:25)
#3      Bloc.on. (package:bloc/src/bloc.dart:435:9)
#4      _MapStream._handleData (dart:async/stream_pipe.dart:213:31)
#5      _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#6      _RootZone.runUnaryGuarded (dart:async/zone.dart:1620:10)
#7      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#8      _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#9      _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123:11)
#10     _WhereStream._handleData (dart:async/stream_pipe.dart:195:12)
#11     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#12     _RootZone.runUnaryGuarded (dart:async/zone.dart:1620:10)
#13     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#14     _DelayedData.perform (dart:async/stream_impl.dart:591:14)
#15     _StreamImplEvents.handleNext (dart:async/stream_impl.dart:706:11)
#16     _PendingEvents.schedule. (dart:async/stream_impl.dart:663:7)
#17     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#18     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
#19     _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:120:13)
#20     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:185:5)

CounterBloc Exception: increment error!, #0      new CounterBloc. (file:///main.dart:117:58)
#1      Bloc.on..handleEvent (package:bloc/src/bloc.dart:427:26)
#2      Bloc.on..handleEvent (package:bloc/src/bloc.dart:418:25)
#3      Bloc.on. (package:bloc/src/bloc.dart:435:9)
#4      _MapStream._handleData (dart:async/stream_pipe.dart:213:31)
#5      _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#6      _RootZone.runUnaryGuarded (dart:async/zone.dart:1620:10)
#7      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#8      _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#9      _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123:11)
#10     _WhereStream._handleData (dart:async/stream_pipe.dart:195:12)
#11     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#12     _RootZone.runUnaryGuarded (dart:async/zone.dart:1620:10)
#13     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#14     _DelayedData.perform (dart:async/stream_impl.dart:591:14)
#15     _StreamImplEvents.handleNext (dart:async/stream_impl.dart:706:11)
#16     _PendingEvents.schedule. (dart:async/stream_impl.dart:663:7)
#17     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#18     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
#19     _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:120:13)
#20     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:185:5)

Transition { currentState: 0, event: Increment, nextState: 1 }
CounterBloc Transition { currentState: 0, event: Increment, nextState: 1 }
Change { currentState: 0, nextState: 1 }
CounterBloc Change { currentState: 0, nextState: 1 }

注意: 首先调用本地 onError,然后调用 BlocObserver 中的全局 onError。

注意: 对于 Bloc 和 Cubit 实例,onError 和 onChange 的工作方式完全相同。

在 EventHandler 中发生的任何未处理的异常也会报告给 onError。

bloc 在项目中的应用 以计数器为例:


  • 第一步:初始化BlocProvider
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_demo/counter/bloc/counter_bloc.dart';
import 'package:flutter_demo/counter/bloc/counter_bloc_page.dart';
import 'package:flutter_demo/counter/cubit/counter_cubit.dart';
import 'package:flutter_demo/counter/cubit/counter_cubit_page.dart';
void main() {
  Bloc.observer = SimpleBlocObserver();
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: _isCubit(true),
    );
  }
  _isCubit(bool isCubit) {
    //第一步:BlocProvider
    return isCubit ? BlocProvider(
      create: (context) => CounterCubit(),
      child: CounterCubitPage(),
    ) : BlocProvider(
      create: (context) => CounterBloc(),
      child: CounterBlocPage(),
    );
  }
}
class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }
  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}
  • 第二步:初始化BlocBuilder

  • 第三步:BlocProvider.of方法添加Event

第二步和第三步代码如下:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_demo/counter/bloc/counter_bloc.dart';
import 'package:flutter_demo/counter/bloc/counter_event.dart';
import 'package:flutter_demo/counter/bloc/counter_bloc_state.dart';
class CounterBlocPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("计数器"),),
          body: Center(
            child: Column(
              children: [
                //第二步:BlocBuilder
                BlocBuilder(builder: (context, state) {
                  return Text("bloc: ${state.value}", style: TextStyle(color: Colors.black),);
                }),
                Spacer(),
                Text("111", style: TextStyle(color: Colors.black),),
                Spacer(),
                ElevatedButton(onPressed: () => _increment(context), child: Text("加法"),),
                Spacer(),
                ElevatedButton(onPressed: () => _decrement(context), child: Text("减法"),),
              ],
            ),
          ),
    );
  }
  void _increment(context) {
    //第三步:BlocProvider.of
    BlocProvider.of(context).add(CounterIncrement());
  }
  _decrement(BuildContext context) {
    BlocProvider.of(context).add(CounterSubduction());
  }
}
  • 对应的Event事件:
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
@immutable
abstract class CounterEvent extends Equatable {
  @override
  List get props => [];
}
class CounterIncrement extends  CounterEvent {
}
class CounterSubduction extends CounterEvent {
}
  • 对应的State状态
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
@immutable
abstract class CounterBlocState extends Equatable{
  final int value;
  CounterBlocState(this.value);
  @override
  List get props => [value];
}
class CounterBlocInitial extends CounterBlocState {
    CounterBlocInitial(int value) : super(value);
}
class CounterBlocChange extends CounterBlocState {
  CounterBlocChange(int value) : super(value);
}

Flutter Equatable的作用

bloc中使用的比较多,因为经常比较状态,用来重写==(类似于java中的equals方法)和hashcode方法:

  • 举个例子,不使用Equatable:
class Person {
  const Person(this.name);
  final String name;
  @override
  bool operator ==(Object other) =>
    identical(this, other) ||
    other is Person &&
    runtimeType == other.runtimeType &&
    name == other.name;
  @override
  int get hashCode => name.hashCode;
}
print(bob == Person("Bob")); // true
  • 使用Equatable后,可以简化为:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
  const Person(this.name);
  final String name;
  @override
  List get props => [name];
}
print(bob == Person("Bob")); // true

Cubit vs. Bloc


简单

使用 Cubit 的最大优点之一就是简单。当创建一个 Cubit 时,我们只需要定义状态以及我们想要公开的改变状态的函数即可。相比之下,创建 Bloc 时,我们必须定义状态、事件和 EventHandler 实现。这使得 Cubit 更容易理解,并且涉及的代码更少。现在让我们看一下两个计数器实现:

CounterCubit

class CounterCubit extends Cubit {
  CounterCubit() : super(0);
  void increment() => emit(state + 1);
}

CounterBloc

abstract class CounterEvent {}class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) => emit(state + 1));
}

Cubit 实现更简洁,而不是单独定义事件,而是像事件一样。此外,在使用Cubit时,我们可以简单地从任何地方调用 emit,以便触发状态变化。

Bloc的优势


可追溯性

使用 Bloc 的最大优势之一就是知道状态变化的顺序以及触发这些变化的确切原因。对于对于应用程序功能至关重要的状态,使用更多事件驱动的方法来捕获状态变化之外的所有事件可能会非常有益。

一个常见的用例可能是管理 AuthenticationState。为了简单起见,假设我们可以通过 enum 来表示 AuthenticationState:

enum AuthenticationState { unknown, authenticated, unauthenticated }

关于应用程序的状态可能从 authenticated 更改为 unauthenticated 的原因可能有很多原因。例如,用户可能点击了一个注销按钮,并要求退出该应用程序。另一方面,也许用户的访问令牌已被撤消,并被强制注销。当使用 Bloc 时,我们可以清楚地跟踪应用程序状态如何达到特定状态。

Transition {
  currentState: AuthenticationState.authenticated,
  event: LogoutRequested,
  nextState: AuthenticationState.unauthenticated
}

上面的 Transition 为我们提供了了解状态发生变化的所有信息。如果我们使用 Cubit 来管理 AuthenticationState,那么我们的日志将如下所示:

Change {
  currentState: AuthenticationState.authenticated,
  nextState: AuthenticationState.unauthenticated
}

这告诉我们用户已注销,但没有说明为什么这对于调试和了解应用程序状态随时间的变化可能至关重要。

高级的事件转换

Bloc 优于 Cubit 的另一个领域是我们需要利用反应性运算符,例如:buffer, debounceTime, throttle 等。

Bloc 有一个事件接收器,它使我们能够控制和转换事件的传入流。

例如,如果我们正在构建一个实时搜索,我们可能希望对避免后端的重复请求操作,以避免受到速率限制以及降低后端的成本/负载。

使用 Bloc,我们可以重写 EventTransformer,以改变 Bloc 处理传入事件的方式。

EventTransformer debounce(Duration duration) {
  return (events, mapper) => events.debounceTime(duration).flatMap(mapper);}

CounterBloc() : super(0) {
  on(
    (event, emit) => emit(state + 1),
    /// Apply the custom `EventTransformer` to the `EventHandler`.
    transformer: debounce(const Duration(milliseconds: 300)),
  );
}

使用以上代码,我们可以用很少的其他代码轻松地实现事件防抖。

二、flutter bloc

Bloc Widgets

BlocBuilder

BlocBuilder 是一个Flutter部件(Widget),它需要Bloc和builder两个方法。BlocBuilder 在接收到新的状态(State)时处理构建部件。BlocBuilder 与 StreamBuilder十分相像,但是它有一个简单的接口来减少一部分必须的模版代码。builder方法会被潜在的触发很多次并且应该是一个返回一个部件(Widget)以响应该状态(State)的纯方法

如果省略了bloc中的参数,则BlocBuilder将使用BlocProvider和当前的BuildContext自动执行查找。

BlocBuilder(
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

仅当您希望提供一个范围仅限于单个部件(widget)且无法通过父代BlocProvider和当前BuildContext访问的块时,才指定Bloc。

BlocBuilder(
  bloc: blocA, // provide the local bloc instance
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
 )

如果您希望对何时调用builder函数的时间进行十分缜密的控制,可以向BlocBuilder提供可选的条件(condition) 。条件(condition)获取先前的Bloc的状态和当前的bloc的状态并返回bool值。如果condition返回true,将使用state调用builder,并且部件(widget)将重新构建。如果buildWhen返回false,则不会用state调用builder,也不会进行重建。

BlocBuilder(
  buildWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

BlocSelector

BlocSelector 是一个和 BlocBuilder 类似的组件,但它可以允许开发者选择一个基于当前bloc状态的新值来过滤更新。如果所选值不更改,则会阻止不必要的构建。选中的值必须是不可变的,以便 BlocSelector 准确地判断是否应该再次调用 builder。

如果省略了 bloc 参数,BlocSelector 将自动使用 BlocProvider 和当前的 BuildContext 执行查找。

BlocSelector(
  selector: (state) {
    // return selected state based on the provided state.
  },
  builder: (context, state) {
    // return widget here based on the selected state.
  },
)

BlocProvider

BlocProvider 是Flutter部件(widget),可通过BlocProvider.of (context)向其子级提bloc。它被作为依赖项注入(DI)部件(widget),以便可以将一个bloc的单个实例提供给子树中的多个部件(widgets)。

在大多数情况下,应该使用BlocProvider来创建新的blocs,并将其提供给其余子树。在这种情况下,由于BlocProvider负责创建bloc,它将自动处理关闭bloc。

BlocProvider(
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

默认情况下,BlocProvider 将在需要的时候创建bloc,意味着 create 将在 BlocProvider.of(context) 查找bloc时执行。要覆盖这个行为并强制 create 立即运行,lazy 可以设置为 false。

BlocProvider(
  lazy: false,
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

在某些情况下,BlocProvider可用于向部件树(widget tree)的新部分提供现有的bloc。当需要将现有的bloc提供给新路线时,这将是最常用的。在这种情况下,BlocProvider不会自动关闭该bloc,因为它没有创建它。

然后从ChildA或ScreenA中,我们可以通过以下方式检索BlocA:

// with extensions
context.read();

// without extensions
BlocProvider.of(context)

MultiBlocProvider

MultiBlocProvider 是Flutter部件(widget),将多个BlocProvider部件合并为一个。 MultiBlocProvider提高了可读性,并且消除了嵌套多个BlocProviders的需要。 通过使用MultiBlocProvider,我们可以从:

BlocProvider(
  create: (BuildContext context) => BlocA(),
  child: BlocProvider(
    create: (BuildContext context) => BlocB(),
    child: BlocProvider(
      create: (BuildContext context) => BlocC(),
      child: ChildA(),
    )
  )
)

变为:

MultiBlocProvider(
  providers: [
    BlocProvider(
      create: (BuildContext context) => BlocA(),
    ),
    BlocProvider(
      create: (BuildContext context) => BlocB(),
    ),
    BlocProvider(
      create: (BuildContext context) => BlocC(),
    ),
  ],
  child: ChildA(),
)

BlocListener

BlocListener 是Flutter部件(widget),它接受一个BlocWidgetListener和一个可选的Bloc,并调用listener以响应该状态(state)的变化。它应用于每次状态更改都需要发生一次的功能,例如导航,显示SnackBar,显示Dialog等。

与BlocBuilder中的builder不同,每个状态(State)更改(不包括 initialState在内的) 仅被调用一次listener,并且是一个void函数。如果省略了bloc参数,则BlocListener将使用BlocProvider和当前的BuildContext自动执行查找。

BlocListener(
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  child: Container(),
)

仅当您无法通过BlocProvider和当前的BuildContext访问的bloc时,才指定bloc。

BlocListener(
  bloc: blocA,
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  child: Container()
 )

如果您希望对任何时候调用监听器的函数进行十分缜密的控制,则可以向BlocListener提供可选的条件(listenWhen) 。 条件(listenWhen) 获取先前的bloc的状态(State) 和当前的bloc的状态(State) 并返回bool值。如果条件(listenWhen) 返回true,listener将被state调用。如果条件返回false,则不会使用状态调用listener。

BlocListener(
  listenWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to call listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  child: Container(),
)

MultiBlocListener 是Flutter的部件(widget),将多个BlocListener部件合并为一个。 MultiBlocListener可以提高可读性,并且不需要嵌套多个BlocListeners。 通过使用MultiBlocListener,我们可以从:

BlocListener(
  listener: (context, state) {},
  child: BlocListener(
    listener: (context, state) {},
    child: BlocListener(
      listener: (context, state) {},
      child: ChildA(),
    ),
  ),
)

变为:

MultiBlocListener(
  listeners: [
    BlocListener(
      listener: (context, state) {},
    ),
    BlocListener(
      listener: (context, state) {},
    ),
    BlocListener(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)

BlocConsumer

BlocConsumer 公开一个builder和listener以便对新状态(State)做出反应。BlocConsumer与嵌套的BlocListener和BlocBuilder类似,但是减少了所需的样板代码的数量。仅在有必要重建UI并执行其他反应来声明bloc中的状态(State)更改时,才应使用BlocConsumer。 BlocConsumer需要一个必需的BlocWidgetBuilder和BlocWidgetListener,以及一个可选的bloc,BlocBuilderCondition和BlocListenerCondition。

如果省略bloc参数,则BlocConsumer将使用以下命令自动执行查找BlocProvider和当前的BuildContext。

BlocConsumer(
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

可以实现可选的listenWhen和buildWhen,以更精细地控制何时调用listener和builder。在每次bloc状态(State)改变时,都会调用listenWhen和buildWhen。它们各自采用先前的状态(State)和当前的状态(State),并且必须返回“bool”,该bool确定是否将调用构建器和/或监听器功能。当初始化BlocConsumer时,先前的状态(State)将被初始化为bloc的状态(State)。 listenWhen和 buildWhen是可选的,如果未实现,则默认为true。

BlocConsumer(
  listenWhen: (previous, current) {
    // return true/false to determine whether or not
    // to invoke listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA's state
  },
  buildWhen: (previous, current) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

RepositoryProvider

RepositoryProvider 是Flutter部件(widget),可通过RepositoryProvider.of (context)向其子级提供存储库。它用作依赖项注入(DI) 部件(widget),以便可以将存储库的单个实例提供给子树中的多个部件(widgets)。 BlocProvider用于提供bloc,而RepositoryProvider仅用于存储库。

RepositoryProvider(
  create: (context) => RepositoryA(),
  child: ChildA(),
);

然后,我们可以从ChildA检索以下内容的Repository实例:

// with extensions
context.read();

// without extensionsRepositoryProvider.of(context)

MultiRepositoryProvider

MultiRepositoryProvider 是Flutter部件(widget),将多个RepositoryProvider部件(widgets)合并为一个。 MultiRepositoryProvider可以提高可读性,并且不需要嵌套多个RepositoryProvider。

通过使用MultiRepositoryProvider 我们可以从原来的:

RepositoryProvider(
  create: (context) => RepositoryA(),
  child: RepositoryProvider(
    create: (context) => RepositoryB(),
    child: RepositoryProvider(
      create: (context) => RepositoryC(),
      child: ChildA(),
    )
  )
)

变为:

MultiRepositoryProvider(
  providers: [
    RepositoryProvider(
      create: (context) => RepositoryA(),
    ),
    RepositoryProvider(
      create: (context) => RepositoryB(),
    ),
    RepositoryProvider(
      create: (context) => RepositoryC(),
    ),
  ],
  child: ChildA(),
)

用例(Usage)

让我们看一下如何使用BlocBuilder将CounterPage部件(widget)连接到CounterBloc。

counter_bloc.dart

abstract class CounterEvent {}class CounterIncrementPressed extends CounterEvent {}class CounterDecrementPressed extends CounterEvent {}

class CounterBloc extends Bloc {
  CounterBloc() : super(0) {
    on((event, emit) => emit(state + 1));
    on((event, emit) => emit(state - 1));
  }
}

counter_page.dart

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: BlocBuilder(
        builder: (context, count) {
          return Center(
            child: Text(
              '$count',
              style: TextStyle(fontSize: 24.0),
            ),
          );
        },
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () => context.read().add(CounterIncrementPressed()),
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.remove),
              onPressed: () => context.read().add(CounterDecrementPressed()),
            ),
          ),
        ],
      ),
    );
  }
}

至此,我们已经成功地将表示层(Presentation) 与业务逻辑层分离了。请注意,CounterPage部件(widget)对用户点击按钮时会发生的情况一无所知。窗口小部件只是告诉CounterBloc用户已按下了加号或减号按钮。

RepositoryProvider 用法


我们将看看如何在'flutter_weather'示例中使用 RepositoryProvider。

weather_repository.dart

class WeatherRepository {
  WeatherRepository({
    MetaWeatherApiClient? weatherApiClient
  }) : _weatherApiClient = weatherApiClient ?? MetaWeatherApiClient();

  final MetaWeatherApiClient _weatherApiClient;

  Future getWeather(String city) async {
    final location = await _weatherApiClient.locationSearch(city);
    final woeid = location.woeid;
    final weather = await _weatherApiClient.getWeather(woeid);
    return Weather(
      temperature: weather.theTemp,
      location: location.title,
      condition: weather.weatherStateAbbr.toCondition,
    );
  }
}

由于app对 WeatherRepository 有显式的依赖,我们通过构造函数注入一个实例。这允许我们根据构建风格和环境为 WeatherRepository 注入不同的实例。

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_weather/app.dart';
import 'package:weather_repository/weather_repository.dart';

void main() {
  runApp(WeatherApp(weatherRepository: WeatherRepository()));
}

因为我们的应用中只有一个仓库,所以我们将通过 RepositoryProvider.value 将它注入到我们的组件(widget)树中。如果你有多个仓库,你可以使用 MultiRepositoryProvider 提供多个存储库实例到子树中。

app.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather_repository/weather_repository.dart';

class WeatherApp extends StatelessWidget {
  const WeatherApp({Key? key, required WeatherRepository weatherRepository})
      : _weatherRepository = weatherRepository,
        super(key: key);

  final WeatherRepository _weatherRepository;

  @override
  Widget build(BuildContext context) {
    return RepositoryProvider.value(
      value: _weatherRepository,
      child: BlocProvider(
        create: (_) => ThemeCubit(),
        child: WeatherAppView(),
      ),
    );
  }
}

在大多数情况下,app的根组件将通过 RepositoryProvider 公开一个或多个仓库到子树。

weather_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_weather/weather/weather.dart';
import 'package:weather_repository/weather_repository.dart';

class WeatherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => WeatherCubit(context.read()),
      child: WeatherView(),
    );
  }
}

现在当初始化一个bloc时,我们能通过 context.read 来访问仓库(repository)的实例,并且通过构造器注入仓库(repository)到bloc中。

架构

bloc_architecture.png

使用Bloc可以将应用程序分为三层:

  1. 表现层(Presentation)

  2. 业务逻辑(Business Logic)

  3. 数据层(Data)

    • 数据源/库(Repository)

    • 数据提供者(Data Provider)

数据层(Data Layer)

数据层的责任是从一个或多个数据源或库中检索/处理数据。

数据层可以被分为以下两部分:

  • 数据源/库
  • 数据提供者

该层是应用程序的最低层,并且与数据库,网络请求和其他异步数据源进行交互。

数据提供者(Data Provider)

数据提供者的责任是提供原始数据。数据提供者所提供的数据应该是能在各个语言间通用。

数据提供者通常会公开简单的API来执行CRUD 操作。

作为数据层的一部分,我们可能有一个createData,readData,updateData和deleteData的方法。

class DataProvider {
    Future readData() async {
        // Read from DB or make network request etc...
    }
}

Repository

存储库层是与Bloc层进行通信的一个或多个数据提供者的包装。

class Repository {
    final DataProviderA dataProviderA;
    final DataProviderB dataProviderB;
    Future getAllDataThatMeetsRequirements() async {
        final RawDataA dataSetA = await dataProviderA.readData();
        final RawDataB dataSetB = await dataProviderB.readData();
        final Data filteredData = _filterData(dataSetA, dataSetB);
        return filteredData;
    }
}

我们的存储库层可以与多个数据提供者进行交互,并对数据执行转换,然后再将结果传递给业务逻辑层。

Bloc 业务逻辑层 (Business Logic) Layer

业务逻辑层的职责是用新状态响应来自表示层的输入。这一层可以依赖一个或多个存储库来检索构建应用程序状态所需的数据。

可以将业务逻辑层看作是用户界面(表示层)和数据层之间的桥梁。业务逻辑层收到来自表示层的事件/操作通知,然后与存储库进行通信,以构建一个供表示层使用的新状态。

class BusinessLogicComponent extends Bloc {
    BusinessLogicComponent(this.repository) {
        on((event, emit) {
            try {
                final data = await repository.getAllDataThatMeetsRequirements();
                emit(Success(data));
            } catch (error) {
                emit(Failure(error));
            }
        });
    }
    final Repository repository;
}

Bloc和Bloc之间的交流

因为bloc会暴露stream,所以创建一个监听其它bloc的bloc可能是很有诱惑力的。但是你不应该这样做,有比使用下面的代码更好的选择:

class BadBloc extends Bloc {
  final OtherBloc otherBloc;
  late final StreamSubscription otherBlocSubscription;
  BadBloc(this.otherBloc) {
    // No matter how much you are tempted to do this, you should not do this!
    // Keep reading for better alternatives!
    otherBlocSubscription = otherBloc.stream.listen((state) {
      add(MyEvent())
    });
  }
  @override
  Future close() {
    otherBlocSubscription.cancel();
    return super.close();
  }
}

虽然上面的代码是没有错误的(甚至在自己清除之后),但它有一个更大的问题:它在两个bloc之间创建了依赖关系。通常,应该不惜一切代价避免同一体系架构层中两个实体之间的依赖关系,因为这会造成难以维护的紧密耦合。由于bloc位于业务逻辑体系结构层中,任何bloc都不应该知道任何其他bloc。

architecture.png

bloc应该只通过事件和注入的存储库接收信息(也就是在构造函数中给bloc的存储库)。

如果一个bloc需要对另一个bloc做出回应,那么你还有两个选择。您可以将问题向上推一层(进入表现层(presentation layer)),或者向下推一层(进入领域层(domain layer))。

通过表现层连接bloc

你可以使用 BlocListener 监听一个bloc,并且在第一个bloc改变时添加一个消息到其它bloc。

class MyWidget extends StatelessWidget {
  const MyWidget({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return BlocListener(
      listener: (context, state) {
        // When the first bloc's state changes, this will be called.
        //
        // Now we can add an event to the second bloc without it having
        // to know about the first bloc.
        BlocProvider.of(context).add(SecondBlocEvent());
      },
      child: TextButton(
        child: const Text('Hello'),
        onPressed: () {
          BlocProvider.of(context).add(FirstBlocEvent());
        },
      ),
    );
  }
}

上面的代码阻止了 SecondBloc 需要知道 FirstBloc,从而鼓励松耦合。这个天气应用使用这个技术根据接收到的天气信息改变应用主题(theme)。

在某些情况下,你可能不希望在表示层中耦合两个bloc。相反,两个bloc共享同一个数据源并在数据更改时进行更新通常是有意义的。

通过领域层连接bloc

两个bloc能从存储库中监听流,并在存储库数据变化时独立的更新它们的状态。在大型企业应用程序中,使用响应式存储库来保持状态同步是很常见的。

首先创建或使用提供数据 Stream 的存储库。例如,下面的存储库展示了一直循环app创意的流:

class AppIdeasRepository {
  int _currentAppIdea = 0;
  final List _ideas = [
    "Future prediction app that rewards you if you predict the next day's news",
    'Dating app for fish that lets your aquarium occupants find true love',
    'Social media app that pays you when your data is sold',
    'JavaScript framework gambling app that lets you bet on the next big thing',
    'Solitaire app that freezes before you can win',
  ];
  Stream productIdeas() async* {
    while (true) {
      yield _ideas[_currentAppIdea++ % _ideas.length];
      await Future.delayed(const Duration(minutes: 1));
    }
  }
}

相同的存储库可以注入到每个需要对新app创意做出响应的bloc中。下面是一个 AppIdeaRankingBloc ,它为上面的库中每个传入的app创意产生一个状态:

class AppIdeaRankingBloc
    extends Bloc {
  AppIdeaRankingBloc({required AppIdeasRepository appIdeasRepo})
      : _appIdeasRepo = appIdeasRepo,
        super(AppIdeaInitialRankingState()) {
    on((event, emit) async {
      // When we are told to start ranking app ideas, we will listen to the
      // stream of app ideas and emit a state for each one.
      await emit.forEach(
        _appIdeasRepo.productIdeas(),
        onData: (String idea) => AppIdeaRankingIdeaState(idea: idea),
      );
    });
  }
  final AppIdeasRepository _appIdeasRepo;
}

表现层(也可理解为用户界面)

表示层的职责是弄清楚如何基于一个或多个bloc的状态(State) 进行渲染。另外,它应该处理用户输入和应用程序生命周期事件。

大多数应用程序流程将从AppStart事件开始,该事件触发应用程序获取一些数据以呈现给用户。

在这种情况下,表示层将添加一个AppStart事件。

另外,表示层将必须根据bloc层的状态(State)确定要在屏幕上呈现的内容。

class PresentationComponent {
    final Bloc bloc;

    PresentationComponent() {
        bloc.add(AppStarted());
    }

    build() {
        // render UI based on bloc state
    }
 }

Bloc 源码解析

BlocProvider源码解析

CounterPage对应的页面BlocProvider配置

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});
  @override
  Widget build(BuildContext context) {
    return BlocProvider(create: (context) => CounterCubit(),
    child: const CounterView(),);
  }
}

BlocProvider源码

typedef Create = T Function(BuildContext context);
final Widget? child;

//1. 外部传入的create函数,创建的对应的cubit对象
final Create? _create = (context) => CounterCubit();
final T? _value;
class BlocProvider>
    extends SingleChildStatelessWidget with BlocProviderSingleChildWidget {
  /// {@macro bloc_provider}
  const BlocProvider({
    Key? key,
    required Create create,
    this.child,
    this.lazy = true,
  })  : _create = create,
        _value = null,
        super(key: key, child: child);
        
 //第三步:把外部传入create函数存入InheritedProvider widget中
@override
  Widget buildWithChild(BuildContext context, Widget? child) {
    assert(
      child != null,
      '$runtimeType used outside of MultiBlocProvider must specify a child',
    );
    final value = _value;
    return value != null
        ? InheritedProvider.value(
            value: value,
            startListening: _startListening,
            lazy: lazy,
            child: child,
          )
        : InheritedProvider(
            create: _create,
            dispose: (_, bloc) => bloc.close(),
            startListening: _startListening,
            child: child,
            lazy: lazy,
          );
  }
  abstract class SingleChildStatelessWidget extends StatelessWidget
    implements SingleChildWidget {
    
  /// Creates a widget that has exactly one child widget.
  const SingleChildStatelessWidget({Key? key, Widget? child})
      : _child = child,
        super(key: key);
  final Widget? _child;
  
  /// A [build] method that receives an extra `child` parameter.
  ///
  /// This method may be called with a `child` different from the parameter
  /// passed to the constructor of [SingleChildStatelessWidget].
  /// It may also be called again with a different `child`, without this widget
  /// being recreated.
  Widget buildWithChild(BuildContext context, Widget? child);
  
  //第二步:widget创建时调用build方法,然后调用buildWithChild方法
  @override
  Widget build(BuildContext context) => buildWithChild(context, _child);
  
  @override
  SingleChildStatelessElement createElement() {
    return SingleChildStatelessElement(this);
  }
}

把外部传入create函数存入InheritedProvider widget中, 然后create存入了_CreateInheritedProvider对象中

class InheritedProvider extends SingleChildStatelessWidget {
  /// Creates a value, then expose it to its descendants.
  ///
  /// The value will be disposed of when [InheritedProvider] is removed from
  /// the widget tree.
  
  //_CreateInheritedProvider
  final _Delegate _delegate;

  InheritedProvider({
    Key? key,
    Create? create,
    T Function(BuildContext context, T? value)? update,
    UpdateShouldNotify? updateShouldNotify,
    void Function(T value)? debugCheckInvalidValueType,
    StartListening? startListening,
    Dispose? dispose,
    this.builder,
    bool? lazy,
    Widget? child,
  })  : _lazy = lazy,
        _delegate = _CreateInheritedProvider(
          create: create,
          update: update,
          updateShouldNotify: updateShouldNotify,
          debugCheckInvalidValueType: debugCheckInvalidValueType,
          startListening: startListening,
          dispose: dispose,
        ),
        super(key: key, child: child);

继续看_CreateInheritedProvider源码

class _CreateInheritedProvider extends _Delegate {
  _CreateInheritedProvider({
    this.create,
    this.update,
    UpdateShouldNotify? updateShouldNotify,
    this.debugCheckInvalidValueType,
    this.startListening,
    this.dispose,
  })  : assert(create != null || update != null),
        _updateShouldNotify = updateShouldNotify;
        
   //typedef Create = T Function(BuildContext context);
  final Create? create;
  
  final T Function(BuildContext context, T? value)? update;
  final UpdateShouldNotify? _updateShouldNotify;
  final void Function(T value)? debugCheckInvalidValueType;
  
  ///这个比较重要,这个用来监听Bloc对象发送过来的Stream,然后通知界面rebuild
  
 /* typedef StartListening = VoidCallback Function(
  InheritedContext element,
  T value,
 );
 */
  final StartListening? startListening;
  final Dispose? dispose;

create函数保存在了_CreateInheritedProvider对象中,默认为懒加载,所以create函数并没有执行,对应的CounterCubit对象并没有执行。

当使用BlocProvider.of(context)或者使用context.read()获取CounterCubit对象时,执行下面的value属性,


class _CreateInheritedProviderState
    extends _DelegateState> {
  VoidCallback? _removeListener;
  bool _didInitValue = false;
  T? _value;
  _CreateInheritedProvider? _previousWidget;
  FlutterErrorDetails? _initError;
  
  ///当使用BlocProvider.of(context)
  ///或者使用context.read()
  @override
  T get value {
    if (_didInitValue && _initError != null) {
      // TODO(rrousselGit) update to use Error.throwWithStacktTrace when it reaches stable
      throw StateError(
        'Tried to read a provider that threw during the creation of its value.\n'
        'The exception occurred during the creation of type $T.\n\n'
        '${_initError?.toString()}',
      );
    }
    bool? _debugPreviousIsInInheritedProviderCreate;
    bool? _debugPreviousIsInInheritedProviderUpdate;
    assert(() {
      _debugPreviousIsInInheritedProviderCreate =
          debugIsInInheritedProviderCreate;
      _debugPreviousIsInInheritedProviderUpdate =
          debugIsInInheritedProviderUpdate;
      return true;
    }());
    if (!_didInitValue) {
      _didInitValue = true;
      /// 当delegate.create 不为空时, 执行delegate.create!(element!);
      if (delegate.create != null) {
        assert(debugSetInheritedLock(true));
        try {
          assert(() {
            debugIsInInheritedProviderCreate = true;
            debugIsInInheritedProviderUpdate = false;
            return true;
          }());
          // 执行delegate.create!(element!), 这时才真正创建CounterCubit对象。并且保存在_value属性中
          _value = delegate.create!(element!);
        } catch (e, stackTrace) {
          _initError = FlutterErrorDetails(
            library: 'provider',
            exception: e,
            stack: stackTrace,
          );
          rethrow;
        } finally {
          assert(() {
            debugIsInInheritedProviderCreate =
                _debugPreviousIsInInheritedProviderCreate!;
            debugIsInInheritedProviderUpdate =
                _debugPreviousIsInInheritedProviderUpdate!;
            return true;
          }());
        }
        assert(debugSetInheritedLock(false));
        assert(() {
          delegate.debugCheckInvalidValueType?.call(_value as T);
          return true;
        }());
      }
      if (delegate.update != null) {
        try {
          assert(() {
            debugIsInInheritedProviderCreate = false;
            debugIsInInheritedProviderUpdate = true;
            return true;
          }());
          _value = delegate.update!(element!, _value);
        } finally {
          assert(() {
            debugIsInInheritedProviderCreate =
                _debugPreviousIsInInheritedProviderCreate!;
            debugIsInInheritedProviderUpdate =
                _debugPreviousIsInInheritedProviderUpdate!;
            return true;
          }());
        }
        assert(() {
          delegate.debugCheckInvalidValueType?.call(_value as T);
          return true;
        }());
      }
    }
    element!._isNotifyDependentsEnabled = false;
    _removeListener ??= delegate.startListening?.call(element!, _value as T);
    element!._isNotifyDependentsEnabled = true;
    assert(delegate.startListening == null || _removeListener != null);
    return _value as T;
  }

下面看widget中获取CounterCubit对象


class CounterView extends StatelessWidget {
  const CounterView({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("计数器"),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const SizedBox(
            height: 30,
          ),
          BlocBuilder(builder: (context, state) {
            // print("BlocBuilder: $state");
            return Text(
              "BlocBuilder: counter: $state",
              style: const TextStyle(color: Colors.black),
            );
          }),
          const SizedBox(
            height: 30,
          ),
          BlocSelector(
            selector: (state) {
              print("test::BlocSelector: $state");
              return state;
            },
            builder: (context, state) {
              return Text(
                "BlocListener: counter: $state",
                style: const TextStyle(color: Colors.black),
              );
            },
          ),
          const SizedBox(
            height: 30,
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: () => _increment(context), child: const Text("加法")),
              ElevatedButton(onPressed: () => _decrement(context), child: const Text("减法")),
            ],
          )
        ],
      ),
    );
  }
  _increment(BuildContext context) {
    //获取CounterCubit对象
    context.read().increment();
  }
  _decrement(BuildContext context) {
    BlocProvider.of(context).decrement();
  }
}

povider.dart中调用read方法, 使用ReadContext的扩展方法read

context.read()
extension ReadContext on BuildContext {

    ... 此处省略n行代码
    
    T read() {  
        return Provider.of(this, listen: false);
    }
}

BlocProvider.of(context)

class BlocProvider>   extends SingleChildStatelessWidget with 
        BlocProviderSingleChildWidget {
        
    ...此处省略n行代码
    
    static T of>(
        BuildContext context, {
        bool listen = false,
      }) {
        try {
        ///最终调用的还是Provider.of方法
          return Provider.of(context, listen: listen);
        } on ProviderNotFoundException catch (e) {
          if (e.valueType != T) rethrow;
          throw FlutterError(
            '''
            BlocProvider.of() called with a context that does not contain a $T.
            No ancestor could be found starting from the context that was passed to BlocProvider.of<$T>().
            This can happen if the context you used comes from a widget above the BlocProvider.
            The context used was: $context
            ''',
          );
        }
    }
}

通过查看源码以上两种方式,最终调用的都是Provider.of方法


  static T of(BuildContext context, {bool listen = true}) {
    assert(
      context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate,
      '''
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.
To fix, write:
Provider.of<$T>(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.
The context used was: $context
''',
    );
    ///通过_inheritedElementOf方法查找当前weidget树上的element对象
    final inheritedElement = _inheritedElementOf(context);
    if (listen) {
      // bind context with the element
      // We have to use this method instead of dependOnInheritedElement, because
      // dependOnInheritedElement does not support relocating using GlobalKey
      // if no provider were found previously.
      context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope>();
    }
    
    final value = inheritedElement?.value;
    
    if (_isSoundMode) {
      if (value is! T) {
        throw ProviderNullException(T, context.widget.runtimeType);
      }
      return value;
    }
    return value as T;
}

Provider.of方法调用_inheritedElementOf方法

static _InheritedProviderScopeElement? _inheritedElementOf(
    BuildContext context,
  ) {
    // ignore: unnecessary_null_comparison, can happen if the application depends on a non-migrated code
    assert(context != null, '''
Tried to call context.read/watch/select or similar on a `context` that is null.
This can happen if you used the context of a StatefulWidget and that
StatefulWidget was disposed.
''');
    assert(
      _debugIsSelecting == false,
      'Cannot call context.read/watch/select inside the callback of a context.select',
    );
    assert(
      T != dynamic,
      '''
Tried to call Provider.of. This is likely a mistake and is therefore
unsupported.
If you want to expose a variable that can be anything, consider changing
`dynamic` to `Object` instead.
''',
    );
    //获取当前context对应的共享element
    //
    final inheritedElement = context.getElementForInheritedWidgetOfExactType<
        _InheritedProviderScope>() as _InheritedProviderScopeElement?;
    if (inheritedElement == null && null is! T) {
      throw ProviderNotFoundException(T, context.widget.runtimeType);
    }
    return inheritedElement;
}

然后查看framework.dart文件中BuildConext类中的context.getElementForInheritedWidgetOfExactType方法源码,
发现是通过InheritedWidget来获取共享的CounterCubit对象

abstract class BuildContext {

    ///获取与给定类型' T '最近的小部件对应的元素,
    ///它必须是一个具体的[InheritedWidget]子类的类型。
    ///
    ///如果没有找到这样的元素返回null。
    ///
    ///调用这个方法是O(1)与一个小的常数因子。
    ///
    ///这个方法不建立与目标的关系
    /// [dependOnInheritedWidgetOfExactType]做的。
    ///
    ///这个方法不应该从[State]调用。处理],因为元素
    ///树不再稳定。引用它的祖先
    ///方法,通过调用保存对祖先的引用
    /// [dependOnInheritedWidgetOfExactType] in [State.didChangeDependencies]。它是
    ///从[State.deactivate]使用这个方法是安全的,它在任何时候都会被调用
    ///小部件从树中删除。
    InheritedElement? getElementForInheritedWidgetOfExactType();

}

然后再看Provider.of方法上面源码已经写过了,此处略过,之间看其方法中的
final value = inheritedElement?.value; 这行代码, 最终调用了inherited_provider.dart中的 value属性,然后调用了_DelegateState中的属性value,

@overrideT get value => _delegateState.value;

abstract class _DelegateState> {  

    _InheritedProviderScopeElement? element;  

    T get value;

}

然后调用_CreateInheritedProviderState中的value属性,因为默认是懒加载,所以此时才真正开始创建CounterCubit对象。

class _CreateInheritedProviderState
    extends _DelegateState> {
  VoidCallback? _removeListener;
  bool _didInitValue = false;
  T? _value;
  _CreateInheritedProvider? _previousWidget;
  FlutterErrorDetails? _initError;
  @override
  T get value {
    if (_didInitValue && _initError != null) {
      // TODO(rrousselGit) update to use Error.throwWithStacktTrace when it reaches stable
      throw StateError(
        'Tried to read a provider that threw during the creation of its value.\n'
        'The exception occurred during the creation of type $T.\n\n'
        '${_initError?.toString()}',
      );
    }
    bool? _debugPreviousIsInInheritedProviderCreate;
    bool? _debugPreviousIsInInheritedProviderUpdate;
    assert(() {
      _debugPreviousIsInInheritedProviderCreate =
          debugIsInInheritedProviderCreate;
      _debugPreviousIsInInheritedProviderUpdate =
          debugIsInInheritedProviderUpdate;
      return true;
    }());
    if (!_didInitValue) {
      _didInitValue = true;
      if (delegate.create != null) {
        assert(debugSetInheritedLock(true));
        try {
          assert(() {
            debugIsInInheritedProviderCreate = true;
            debugIsInInheritedProviderUpdate = false;
            return true;
          }());
          _value = delegate.create!(element!);
        } catch (e, stackTrace) {
          _initError = FlutterErrorDetails(
            library: 'provider',
            exception: e,
            stack: stackTrace,
          );
          rethrow;
        } finally {
          assert(() {
            debugIsInInheritedProviderCreate =
                _debugPreviousIsInInheritedProviderCreate!;
            debugIsInInheritedProviderUpdate =
                _debugPreviousIsInInheritedProviderUpdate!;
            return true;
          }());
        }
        assert(debugSetInheritedLock(false));
        assert(() {
          delegate.debugCheckInvalidValueType?.call(_value as T);
          return true;
        }());
      }
      if (delegate.update != null) {
        try {
          assert(() {
            debugIsInInheritedProviderCreate = false;
            debugIsInInheritedProviderUpdate = true;
            return true;
          }());
          _value = delegate.update!(element!, _value);
        } finally {
          assert(() {
            debugIsInInheritedProviderCreate =
                _debugPreviousIsInInheritedProviderCreate!;
            debugIsInInheritedProviderUpdate =
                _debugPreviousIsInInheritedProviderUpdate!;
            return true;
          }());
        }
        assert(() {
          delegate.debugCheckInvalidValueType?.call(_value as T);
          return true;
        }());
      }
    }
    element!._isNotifyDependentsEnabled = false;
    _removeListener ??= delegate.startListening?.call(element!, _value as T);
    element!._isNotifyDependentsEnabled = true;
    assert(delegate.startListening == null || _removeListener != null);
    return _value as T;
}

Cubit更新数据源码解析

class CounterCubit extends Cubit {
  CounterCubit() : super(0);
 
  @override
  void onChange(Change change) {
    super.onChange(change);
    print("test:: onChange: ${change.currentState}");
  }
  @override
  void onError(Object error, StackTrace stackTrace) {
    print("test:: onError: ${error.toString()}, stackTrace: ${stackTrace.toString()}");
    super.onError(error, stackTrace);
  }
  
  
  void increment() => emit(state + 1);
  
  void decrement() => emit(state - 1);
}

emit方法解析,发现onChange方法在数据更新之前执行。最终是通过StreamController发送数据


abstract class BlocBase
    implements StateStreamableSource, Emittable, ErrorSink {
    
    late final _stateController = StreamController.broadcast();
    
    State _state;

  /// {@macro bloc_base}
  BlocBase(this._state) {
    // ignore: invalid_use_of_protected_member
    _blocObserver.onCreate(this);
  }

@overrideState get state => _state;

@overrideStream get stream => _stateController.stream;

  ... 此处省略n行代码
  
@protected
  @visibleForTesting
  @override
  void emit(State state) {
    try {
      if (isClosed) {
        throw StateError('Cannot emit new states after calling close');
      }
      if (state == _state && _emitted) return;
      onChange(Change(currentState: this.state, nextState: state));
      _state = state;
      _stateController.add(_state);
      _emitted = true;
    } catch (error, stackTrace) {
      onError(error, stackTrace);
      rethrow;
    }
  }

}

然后再看BlocBuilder源码

typedef BlocWidgetBuilder = Widget Function(BuildContext context, S state);

class BlocBuilder, S>
    extends BlocBuilderBase {
    
    //此处为伪代码
   final BlocWidgetBuilder builder = (context, state) {  
       // print("BlocBuilder: $state");  
       return Text(
           "BlocBuilder: counter: $state",  
           style: const TextStyle(color: Colors.black),
       );
   }

  /// {@macro bloc_builder}
  /// {@macro bloc_builder_build_when}
  const BlocBuilder({
    Key? key,
    required this.builder,
    B? bloc,
    BlocBuilderCondition? buildWhen,
  }) : super(key: key, bloc: bloc, buildWhen: buildWhen);
  /// The [builder] function which will be invoked on each widget build.
  /// The [builder] takes the `BuildContext` and current `state` and
  /// must return a widget.
  /// This is analogous to the [builder] function in [StreamBuilder].
  
 
  @override
  Widget build(BuildContext context, S state) => builder(context, state);
}

build方法回调

abstract class BlocBuilderBase, S>
    extends StatefulWidget {
  /// {@macro bloc_builder_base}
  const BlocBuilderBase({Key? key, this.bloc, this.buildWhen})
      : super(key: key);
  /// The [bloc] that the [BlocBuilderBase] will interact with.
  /// If omitted, [BlocBuilderBase] will automatically perform a lookup using
  /// [BlocProvider] and the current `BuildContext`.
  final B? bloc;
  
  /// {@macro bloc_builder_build_when}
  final BlocBuilderCondition? buildWhen;
  
  /// Returns a widget based on the `BuildContext` and current [state].
  Widget build(BuildContext context, S state);
  
  @override
  State> createState() => _BlocBuilderBaseState();
}


class _BlocBuilderBaseState, S>
    extends State> {
  late B _bloc;
  late S _state;
  @override
  void initState() {
    super.initState();
    _bloc = widget.bloc ?? context.read();
    _state = _bloc.state;
  }
  
  @override
  Widget build(BuildContext context) {
    if (widget.bloc == null) {
      // Trigger a rebuild if the bloc reference has changed.
      // See https://github.com/felangel/bloc/issues/2127.
      context.select((bloc) => identical(_bloc, bloc));
    }
    return BlocListener(
      bloc: _bloc,
      listenWhen: widget.buildWhen,
      listener: (context, state) => setState(() => _state = state),
      //StatefulWidget build方法回调
      child: widget.build(context, _state),
    );
  }
}

BlocListener 初始化时,最终通过BlocListenerBase(它是一个SingleChildStatefulWidget),然后调用其对应的_BlocListenerBaseState 中的initState方法

class _BlocListenerBaseState, S>
    extends SingleChildState> {
  StreamSubscription? _subscription;
  late B _bloc;
  late S _previousState;
  @override
  void initState() {
    super.initState();
    _bloc = widget.bloc ?? context.read();
    _previousState = _bloc.state;
    _subscribe();
  }
}

initState方法中调用了_subscribe方法, 该方法中设置了stream的监听方法,并且把_subscription
赋值给了成员变量StreamSubscription? _subscription; 在监听中调用了widget.listener(context, state)方法,

然后触发widget(这个widget就是BlocBuilder本身)重新build

StreamSubscription? _subscription;
//此处为伪代码
widget.listener = (context, state) => setState(() => _state = state);

void _subscribe() {
    _subscription = _bloc.stream.listen((state) {
      if (widget.listenWhen?.call(_previousState, state) ?? true) {
        widget.listener(context, state);
      }
      _previousState = state;
    });
}

bloc源码分析

然后分析CounterBloc对应的bloc源码流程,和cubit最大的不同就是,cubit没有事件event,bloc支持event事件,state因为在bloc_base中所以他们都支持
主要看两个方法,一个on监听方法,一个add发送事件的方法

  • on方法,把EventHandler缓存到_handlers数组中,然后设置eventStream的监听,然后把对应的subscripe缓存起来
typedef EventHandler = FutureOr Function(
  Event event,
  Emitter emit,
);

typedef EventTransformer = Stream Function(
  Stream events,
  EventMapper mapper,
);

abstract class Bloc extends BlocBase
    implements BlocEventSink {
    
  final _handlers = <_Handler>[];
  
  final _eventController = StreamController.broadcast();
  
  final _subscriptions = >[];

  void on(
    EventHandler handler, {
    EventTransformer? transformer,
  }) {
    assert(() {
      final handlerExists = _handlers.any((handler) => handler.type == E);
      if (handlerExists) {
        throw StateError(
          'on<$E> was called multiple times. '
          'There should only be a single event handler per event type.',
        );
      }
      _handlers.add(_Handler(isType: (dynamic e) => e is E, type: E));
      return true;
    }());
    final _transformer = transformer ?? _eventTransformer;
    final subscription = _transformer(
      _eventController.stream.where((event) => event is E).cast(),
      (dynamic event) {
        void onEmit(State state) {
          if (isClosed) return;
          if (this.state == state && _emitted) return;
          onTransition(Transition(
            currentState: this.state,
            event: event as E,
            nextState: state,
          ));
          emit(state);
        }
        final emitter = _Emitter(onEmit);
        final controller = StreamController.broadcast(
          sync: true,
          onCancel: emitter.cancel,
        );
        void handleEvent() async {
          void onDone() {
            emitter.complete();
            _emitters.remove(emitter);
            if (!controller.isClosed) controller.close();
          }
          try {
            _emitters.add(emitter);
            await handler(event as E, emitter);
          } catch (error, stackTrace) {
            onError(error, stackTrace);
            rethrow;
          } finally {
            onDone();
          }
        }
        handleEvent();
        return controller.stream;
      },
    ).listen(null);
    _subscriptions.add(subscription);
  }
    
}

再看添加方法 add

@override
  void add(Event event) {
    assert(() {
      final handlerExists = _handlers.any((handler) => handler.isType(event));
      if (!handlerExists) {
        final eventType = event.runtimeType;
        throw StateError(
          '''add($eventType) was called without a registered event handler.\n'''
          '''Make sure to register a handler via on<$eventType>((event, emit) {...})''',
        );
      }
      return true;
    }());
    try {
      onEvent(event);
      _eventController.add(event);
    } catch (error, stackTrace) {
      onError(error, stackTrace);
      rethrow;
    }
}

通过_eventController添加事件,然后执行on方法中的监听。示例代码如下:

class ArticleBloc extends Bloc {
  ArticleBloc(): super(ArticleUninitialized()) {
    on((event, emit) async {
      final currState = state;
      if (event is FetchEvent && !_hasReachMax(currState)) {
        try {
          if (currState is ArticleUninitialized) {
            // 加载数据
            final posts = await _fetchArticles(0, 20);
            emit.call(ArticleLoaded(articles: posts, hasReachedMax: false));
          } else if (currState is ArticleLoaded) {
            // 加载数据
            final posts = await _fetchArticles(currState.articles.length, 20);
            emit.call(posts.isEmpty
                ? currState.copyWith(hasReachedMax: true)
                : ArticleLoaded(
                articles: currState.articles + posts, hasReachedMax: false));
          }
        } catch (e) {
          emit.call(ArticleError());
        }
      }
    });
}

然后通过emitter发射器,调用call方法,最终回调到on监听方法中的onEmit方法

void onEmit(State state) {
          if (isClosed) return;
          if (this.state == state && _emitted) return;
          onTransition(Transition(
            currentState: this.state,
            event: event as E,
            nextState: state,
          ));
          emit(state);
}

最终还是调用emit方法执行widget树的更新,此处过程和cubit是同样的逻辑。

BlocSelector源码分析

此处和cubit emit更新状态的源码类似

typedef BlocWidgetSelector = T Function(S state);

typedef BlocWidgetBuilder = Widget Function(BuildContext context, S state);

class BlocSelector, S, T> extends StatefulWidget {
 
  //此处为伪代码
  final BlocWidgetBuilder builder = (state) {  
      print("test::BlocSelector: $state");  
      return state;
  }
  
  //此处为伪代码
  final BlocWidgetSelector selector = (context, state) {
              return Text(
                "BlocListener: counter: $state",
                style: const TextStyle(color: Colors.black),
              );
   }

  const BlocSelector({
    Key? key,
    required this.selector,
    required this.builder,
    this.bloc,
  }) : super(key: key);
}

BlocSelector实际上是一个StatefulWidget, 它对应的state为_BlocSelectorState

class _BlocSelectorState, S, T>
    extends State> {
  late B _bloc;
  late T _state;
  @override
  void initState() {
    super.initState();
    _bloc = widget.bloc ?? context.read();
    _state = widget.selector(_bloc.state);
  }

 @override
  Widget build(BuildContext context) {
    if (widget.bloc == null) {
      // Trigger a rebuild if the bloc reference has changed.
      // See https://github.com/felangel/bloc/issues/2127.
      context.select((bloc) => identical(_bloc, bloc));
    }
    return BlocListener(
      bloc: _bloc,
      listener: (context, state) {
        final selectedState = widget.selector(state);
        if (_state != selectedState) setState(() => _state = selectedState);
      },
      child: widget.builder(context, _state),
    );
  }

}

对应的builder方法中初始化了BlocListener对象 并调用了父类BlocListenerBase的构造函数, 它实际上也是一个widget


abstract class BlocListenerBase, S>
    extends SingleChildStatefulWidget {
  /// {@macro bloc_listener_base}
  const BlocListenerBase({
    Key? key,
    required this.listener,
    this.bloc,
    this.child,
    this.listenWhen,
  }) : super(key: key, child: child);

}

对应的state _BlocListenerBaseState initState方法调用了subscrible方法设置监听代码如下:
当外部使用Bloc发送事件时,最终触发wdiget的更新
其中widget.listener(context, state);这句代码回调到_BlocSelectorState中的setState方法,然后使页面重新build

class _BlocListenerBaseState, S>
    extends SingleChildState> {
  StreamSubscription? _subscription;
  late B _bloc;
  late S _previousState;
  @override
  void initState() {
    super.initState();
    _bloc = widget.bloc ?? context.read();
    _previousState = _bloc.state;
    _subscribe();
  }

  void _subscribe() {
    _subscription = _bloc.stream.listen((state) {
      if (widget.listenWhen?.call(_previousState, state) ?? true) {
        widget.listener(context, state);
      }
      _previousState = state;
    });
  }

}

你可能感兴趣的:(flutter bloc使用方法以及源码解析)