BlocProvider add数据流程

news2024/11/16 7:31:10

我们看看往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是在BlocBuilder中listen的

  static VoidCallback _startListening(
    InheritedContext<StateStreamable?> e,
    StateStreamable value,
  ) {
    final subscription = value.stream.listen(
      (dynamic _) => e.markNeedsNotifyDependents(),
    );
    return subscription.cancel;
  }

这里的e是InheritedProviderScope<TestBloc?>,当时的代码堆栈,

 这里我们看看 e.markNeedsNotifyDependents(),

  @override
  void markNeedsNotifyDependents() {
    if (!_isNotifyDependentsEnabled) {
      return;
    }

    markNeedsBuild();
    _shouldNotifyDependents = true;
  }

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【华为重启门】华为/荣耀手机一直自动重启原因解决方案(荣耀V10)

文章目录1.问题描述2.分析原因3.解决方案4.实际操作1.问题描述 荣耀V10&#xff0c;莫名其妙的、无规律的死机重启。 一开始是重启后进入紧急备份&#xff0c;无法正常开机。 之后莫名其妙可以正常开机了&#xff0c;但是总是会重启。 2.分析原因 不搜不知道&#xff0c;一…

Maven安装及配置

1.下载 Maven – Download Apache Maven 2.安装 maven压缩包解压到一个没有中文&#xff0c;空格或其他特殊字符的文件夹内即可使用。 3.配置环境变量 1.右键此电脑->属性->高级系统设置->环境变量 2.新建系统变量MAVEN_HOME 3.编辑系统变量Path&#xff0c;添…

【源码解析】断路器Hystrix使用和工作原理

断路器Hystrix使用和工作原理 介绍 在微服务架构的分布式系统中&#xff0c;众多微服务有复杂的依赖关系&#xff0c;这些依赖在某些情况下不可避免的会出现一些请求失败。当一个依赖由于延迟高出现阻塞&#xff0c;调用该依赖的服务线程就会发生排队阻塞。如果这个时候出现大…

二叉树实现及应用(C语言模拟实现可以存放任意结点的栈、队列,二叉树遍历的递归与非递归实现,附上源码和实验报告,用了自取)

XIAN TECHNOLOGICAL UNIVERSITY 目录 课程设计报告 1绪论 2课程设计目的和内容 3算法的基本思想 1 .建立二叉树结构      建立二叉树时&#xff0c;要先明确是按哪一种遍历规则输入&#xff0c;该二叉树是按你所输入的遍历规则来建立的。本实验用的先序遍历行建树。二叉树…

第六章. 图解数组计算模块Numpy—数据的相关概念和创建数组

第六章. 图解数组计算模块Numpy 6.1 数据的相关概念和创建数组 Numpy是Python数组计算&#xff0c;矩阵运算和科学计算的核心库&#xff0c;它的用途是以数组的形式对数据进行操作&#xff0c;由于Numpy是通过C语言实现的&#xff0c;所以运算速度比较快。 1. Numpy的功能&…

技术贴 | SQL 编译与执行 -parser

前言SQL 编译与执行系列技术博客将按照以下顺序分别介绍整个 SQL 执行引擎。图一 SQL 编译与执行研读流程parser 部分&#xff0c;包括词法解析和语法解析。compile 部分&#xff0c;包括语义解析以及计划的构建。optimize 部分&#xff0c;包括计划的优化。exec 部分&#xff…

十四、TCP多线程、原子类AtomicInteger、日志、枚举

tcp多线程 tcp客户端 多线程收发代码 package com.heima.test2;import java.io.*; import java.net.Socket; import java.nio.charset.Charset; import java.util.Scanner;class ClientSend implements Runnable {Socket socket;Scanner sc new Scanner(System.in);public C…

2019年数维杯国际大学生数学建模B题无人机避障问题设计规划求解全过程文档及程序

2019年数维杯国际大学生数学建模 B题 无人机避障问题设计规划 问题重述&#xff1a; 任务1&#xff1a;假设无人机在飞行过程中不受风向、湿度等外界因素的影响&#xff0c;飞行速度和拍摄角度恒定&#xff0c;无人机对一定宽度的区域进行直线飞行模式航拍。执行此航拍的飞行…

SpringBoot(一): SpringBoot的创建和使用

Spring的创建和使用1. 什么是Spring&#xff1f;2. SpringBoot的优点3. SpringBoot项目的创建3.1 使用IDEA创建3.2 使用网页创建4. 项目目录介绍和运行4.1 目录介绍4.2 项目运行4.3 输出hello world4.4 约定大于配置1. 什么是Spring&#xff1f; Spring的诞生是为了简化Java程…

Spring-boot启动失败 Unregistering JMX-exposed beans on shutdown 异常处理

目录一、异常错误二、原因三、解决方法一、异常错误 Spring-boot启动Run时&#xff0c;出现 o.s.j.e.a.AnnotationMBeanExporter - Unregistering JMX-exposed beans on shutdown 错误 *************************** APPLICATION FAILED TO START Description: The Tomcat conn…

【小程序】包与数据共享

文章目录使用 npm 包Vant WeappAPI Promise化全局事件共享MobX分包分包概念使用分包独立分包分包预下载使用 npm 包 目前&#xff0c;小程序中已经支持使用 npm 安装第三方包&#xff0c;从而来提高小程序的开发效率。但是&#xff0c;在小程序中使用npm 包有如下 3 个限制&am…

【韩顺平Linux】学习笔记3

【韩顺平Linux】学习笔记3一、文件目录指令pwd指令 ls指令cd指令mkdir指令rmdir指令touch指令cp指令rm指令mv指令cat指令more指令less 指令echo指令 head指令tail指令> 指令 >>指令ln指令history指令二、时间日期指令三、查找指令四、压缩和解压一、文件目录指令 根目…

【前端】Vue项目:旅游App-(3)TabBar:点击active效果、点击路由跳转

文章目录目标代码与过程设置active主题颜色添加点击active效果点击路由跳转效果总代码修改或新增的文件common.cssindex.csstab-bar.vue目标 添加点击active效果实现点击路由跳转效果 上一篇TabBar搭建&#xff1a;【前端】Vue项目&#xff1a;旅游App-&#xff08;2&#xff…

LVGL学习笔记12 - 复选框CheckBox

目录 1. Parts 1.1 LV_PART_MAIN 1.2 LV_PART_INDICATOR 2. 状态 3. 样式 3.1 设置字符串颜色 3.2 设置点击框外框颜色 3.3 修改点击框弧度 3.4 修改字符串与点击框的间隔 4. 事件 复选框通过lv_checkbox_create创建。一个CheckBox由一个点击框加一个Label组成。 obj1 …

Minikube Mac 安装 使用

Minikube Mac 安装 使用 环境要求 硬件要求 至少 2核 CPUs2GB 以上内存20GB 以上磁盘空间网络环境容器或虚拟机, 例如: Docker, QEMU, Hyperkit, Hyper-V, KVM, Parallels, Podman, VirtualBox, or VMware Fusion/Workstation 本机环境 Mac Pro 10.13.6 Docker 18.09.1 …

半导体行业相关术语

目录 1.晶圆&#xff08;wafer&#xff09; 2. 自动化测试设备&#xff08;ATE Automatic Test Equipment&#xff09; 3.晶盒&#xff08;Cassette&#xff09; 4. 待测设备(DUT Device Under Test) 5. 探针接口板(PIB Prober Interface Board) 6. 设备接口板(DIB D…

干货 | web自动化总卡在文件上传和弹框处理上?

在有些场景中&#xff0c;需要上传文件&#xff0c;而 Selenium 无法定位到弹出的文件框&#xff0c;以及网页弹出的提醒。这些都是需要特殊的方式来处理。input 标签使用自动化上传&#xff0c;先定位到上传按钮&#xff0c;然后 send_keys 把路径作为值给传进去.如图所示&…

【计算机网络-物理层】通信基础

文章目录1 码元、速率、波特、带宽1.1 码元1.2 波特率1.3 比特率1.4 带宽1.5 相关例题2 奈氏准则、香农定理2.1 奈氏准则&#xff08;采样定理&#xff09;2.2 香农定理2.3 相关例题3 编码方式3.1 归零编码&#xff08;RZ&#xff09;3.2 非归零编码&#xff08;NRZ&#xff09…

【简单DP】[NOIP2007 普及组] 守望者的逃离

P1095 [NOIP2007 普及组] 守望者的逃离 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)题意&#xff1a;思路&#xff1a;独立做出来的一道DP&#xff01;一开始我去模拟过程找子问题&#xff0c;然后去找阶段是什么本来想的是以路程作为阶段&#xff0c;但是1e8数组开不下那么…

如何看待PyTorch 2.0?

作者&#xff5c;吴育昕 1 为什么是TorchDynamo Graph capture 把用户 Python 写的模型代码变成 graph&#xff0c;是一切编译的根基。而 PyTorch 在试了这么多方案之后似乎已经锁定 TorchDynamo 作为 graph capture 的未来方向了&#xff0c;所以写一点关于 TorchDynamo 的…