BlocProvider add数据流程

文章详细解释了在Flutter中使用Bloc框架处理事件和更新状态的过程。从调用`add`方法开始,经过事件处理器的判断、执行,到`emit`函数触发状态变更,最后影响UI的重建。整个流程涉及到BlocBuilder的监听、事件处理器的注册以及错误处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们看看往bloc中添加数据流程,以demo为例

  void _incrementCounter() {
    _counter++;
    BlocProvider.of<TestBloc>(context).add(LoadTestEvent(_counter));

  }

我们调用了BlocProvider获取对应的Bloc ,然后调用他的add方法

 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;
    }
  }

这里首先会判断存不存在对应的handler ,如果不存在则直接报错

然后onEvent这里没做什么事 我们可以在bloc重新该方法 在对应的event发生时进行额外处理

然后调用_eventController添加到Stream里面 这里会触发我们前面一文讲到的在on方法中的listen回调

final subscription = _transformer(
      _eventController.stream.where((event) => event is E).cast<E>(),
      (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<E>.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);

也就是这一段中间的处理代码

这里会把onEmit包裹到_Emitter类里面,定义了一个handleEvent并最终调用他

handleEvent中调用前面_Emitter的add方法,然后调用handler处理event,handler就是我们bloc中的处理方法

TestBloc() : super(TestState()) {
    //获取用户的孩子
    on<LoadTestEvent>(
      (event, emit) async {

        emit(TestSuccessState('Hello'+event.type.toString()));
        return;
      },
    );
  }

这里的处理方法里面调用了emit函数,具体就是_Emitter的call方法

@override
  void call(State state) {
    assert(
      !_isCompleted,
      '''\n\n
emit was called after an event handler completed normally.
This is usually due to an unawaited future in an event handler.
Please make sure to await all asynchronous operations with event handlers
and use emit.isDone after asynchronous operations before calling emit() to
ensure the event handler has not completed.

  **BAD**
  on<Event>((event, emit) {
    future.whenComplete(() => emit(...));
  });

  **GOOD**
  on<Event>((event, emit) async {
    await future.whenComplete(() => emit(...));
  });
''',
    );
    if (!_isCanceled) _emit(state);
  }

_emit就回到了前面的onEmit

final subscription = _transformer(
      _eventController.stream.where((event) => event is E).cast<E>(),
      (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<E>.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);

在onEmit首先判断state跟上次发送的是不是一样的,通过重新state的operator ==我们可以改变他的判断条件

class TestSuccessState extends TestState {
  String data;
  TestSuccessState(this.data);
  @override
  List<Object?> get props => [data];
  // @override
  // bool operator ==(Object other) {
  //   return false;
  // }
}

回到上面 如果state是一样的或者已经发送过 则直接返回

onTransition没有做什么 然后调用emit方法

 void emit(State state) {
    try {
      if (isClosed) {
        throw StateError('Cannot emit new states after calling close');
      }
      if (state == _state && _emitted) return;
      onChange(Change<State>(currentState: this.state, nextState: state));
      _state = state;
      _stateController.add(_state);
      _emitted = true;
    } catch (error, stackTrace) {
      onError(error, stackTrace);
      rethrow;
    }
  }

这里主要是调用了_stateController的add方法,_stateController的listen是在BlocListener中listen的

  @override
  void initState() {
    super.initState();
    _bloc = widget.bloc ?? context.read<B>();
    _previousState = _bloc.state;
    _subscribe();
  }  

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

这里回回调到listener

    return BlocListener<B, S>(
      bloc: _bloc,
      listenWhen: widget.buildWhen,
      listener: (context, state) => setState(() => _state = state),
      child: widget.build(context, _state),
    );

最终又会引发组件的build重构

<think>我们正在对比Flutter架构设计中BLoC和Riverpod的核心区别。参考引用[1]和[3],以及用户之前的问题中提到的架构设计。引用[1]提到Riverpod是Provider的重写版,具有依赖注入、响应式编程和资源管理能力。引用[3]则介绍了BLoC的基本概念:事件、状态和BLoC组件,通过Stream处理状态。我们需要从以下几个方面进行对比分析:1.设计理念与核心思想2.状态管理机制3.依赖注入能力4.代码组织与可测试性5.学习曲线与适用场景注意:回答中要自然引用引用[1]和引用[3],并在末尾生成相关问题。</think>### BLoC与Riverpod在Flutter架构设计中的核心区别对比分析 #### 一、设计理念差异 | **特性** | **BLoC** | **Riverpod** | |----------------|---------------------------------------|---------------------------------------| | **核心思想** | 基于事件驱动的状态管理 | 响应式依赖注入系统 | | **架构定位** | 业务逻辑层专用模式 | 全栈解决方案(状态+依赖管理)[^1] | | **数据流** | `Event → BLoC → State` 单向流 | 多向响应式数据流 | | **设计目标** | 分离UI与业务逻辑 | 取代Provider并解决其缺陷[^1] | #### 二、核心机制对比 1. **状态管理方式** - **BLoC**(引用[3]): ```dart // 显式定义事件和状态 class CounterEvent {} class Increment extends CounterEvent {} class CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<Increment>((event, emit) => emit(state + 1)); } } ``` - 强制遵循 `EventState` 范式 - 需要手动管理`StreamController` - **Riverpod**: ```dart // 声明式状态定义 final counterProvider = StateProvider<int>((ref) => 0); // 使用时不需事件类 ref.read(counterProvider.notifier).update((state) => state + 1); ``` - 无固定范式,支持自由状态组合 - 自动处理订阅和销毁[^1] 2. **依赖注入能力** - **BLoC**: - 需借助第三方库(如get_it)实现DI - 依赖关系需手动传递 - **Riverpod**: - 内置依赖注入系统 - 支持跨组件依赖解析: ```dart final userRepoProvider = Provider<UserRepository>((ref) { final dio = ref.watch(dioProvider); // 自动获取依赖 return UserRepository(dio); }); ``` #### 三、代码组织差异 | **维度** | **BLoC** | **Riverpod** | |----------------|---------------------------------------|---------------------------------------| | **文件结构** | 需创建`event`/`state`/`bloc`三个文件 | 单文件定义Provider | | **组件通信** | 通过`BlocProvider`层级传递 | 任意位置通过`ref`访问 | | **状态共享** | 需显式传递BLoC实例 | 全局可访问的Provider作用域[^1] | | **样板代码** | 高(事件/状态类定义) | 低(函数式声明) | #### 四、性能与测试对比 1. **性能优化** - BLoC:依赖`Stream`,需注意避免不必要的重建 - Riverpod:内置`select()`精确控制重建范围: ```dart final userName = ref.watch(userProvider.select((user) => user.name)); ``` 2. **可测试性** - **BLoC测试**(引用[3]): ```dart test('increment event', () { final bloc = CounterBloc(); bloc.add(Increment()); expect(bloc.state, 1); }); ``` - **Riverpod测试**: ```dart test('counter update', () async { final container = ProviderContainer(); container.read(counterProvider.notifier).state++; expect(container.read(counterProvider), 1); }); ``` #### 五、适用场景推荐 | **场景** | **推荐方案** | **原因** | |------------------------|--------------|---------------------------------------| | 严格事件驱动业务逻辑 | ✅ BLoC | 强制事件溯源,流程清晰 | | 跨组件共享服务 | ✅ Riverpod | 依赖注入更简洁 | | 中小型项目快速开发 | ✅ Riverpod | 低样板代码,上手快 | | 大型复杂状态机 | ✅ BLoC | 事件流可视化便于维护 | | 需要热重载兼容性 | ✅ Riverpod | 无`BuildContext`依赖[^1] | > **总结**: > BLoC适用于**事件驱动型复杂业务**(如电商下单流程),通过强制规范降低维护成本; > Riverpod更适合**灵活依赖管理场景**(如多数据源切换),通过响应式DI提升开发效率[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值