Flutter鸿蒙next 使用 BLoC 模式进行状态管理详解
1. 引言
在 Flutter 中,随着应用规模的扩大,管理应用中的状态变得越来越复杂。为了处理这种复杂性,许多开发者选择使用不同的状态管理方案。其中,BLoC(Business Logic Component)模式作为一种流行的状态管理方法,因其将应用的业务逻辑与 UI 层分离而广泛应用。
本文将详细介绍如何在 Flutter 中使用 BLoC 模式进行状态管理,涵盖基本概念、实现步骤及代码示例,同时进行深入的代码解释,帮助开发者理解这一模式的精髓。
2. 什么是 BLoC 模式?
BLoC 是一种将业务逻辑从 UI 层分离的模式。它基于 Streams(流)和 Sinks(输入)的概念,将状态管理和 UI 更新通过流式的方式来实现。通过这种方式,UI 层通过监听数据流(Stream)来获取状态更新,避免了状态直接与 UI 层耦合,提高了代码的可维护性和可测试性。
核心组件:
- Event:用户或系统触发的事件,代表一种动作或请求。
- State:UI 显示的当前状态,通常是数据的一个集合。
- Bloc:接收事件并转换为状态的核心业务逻辑组件。
Bloc 模式的核心原则就是 通过事件驱动状态的更新,并且 UI 层只关心状态的变化,而不关心事件的处理过程。
3. 使用 BLoC 的流程
使用 BLoC 模式的基本流程大致如下:
- 创建 Event 和 State 类:首先,定义一些 Event 和 State 类。
- 创建 Bloc 类:Bloc 类用于处理事件和状态之间的映射。
- 提供 Bloc:在 UI 中通过
BlocProvider提供 Bloc 实例,确保其生命周期的管理。 - 通过 BlocBuilder 或 BlocListener 更新 UI:UI 通过监听状态变化来更新界面。
4. 代码示例
接下来,创建一个简单的计数器应用,通过 BLoC 模式管理计数器的增减操作。
代码结构:
CounterEvent:定义可能的用户操作事件。CounterState:定义计数器的状态。CounterBloc:处理事件并输出状态。
代码实现:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';// 1. 定义 Event
abstract class CounterEvent {}class IncrementEvent extends CounterEvent {}class DecrementEvent extends CounterEvent {}// 2. 定义 State
class CounterState {final int counter;CounterState({required this.counter});
}// 3. 定义 Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {CounterBloc() : super(CounterState(counter: 0));@overrideStream<CounterState> mapEventToState(CounterEvent event) async* {if (event is IncrementEvent) {yield CounterState(counter: state.counter + 1);} else if (event is DecrementEvent) {yield CounterState(counter: state.counter - 1);}}
}// 4. 主程序界面
void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: BlocProvider(create: (context) => CounterBloc(),child: CounterPage(),),);}
}class CounterPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('BLoC Counter')),body: Center(child: BlocBuilder<CounterBloc, CounterState>(builder: (context, state) {return Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text('Counter Value: ${state.counter}', style: TextStyle(fontSize: 30)),Row(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton(onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),child: Text('Increment'),),SizedBox(width: 10),ElevatedButton(onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),child: Text('Decrement'),),],)],);},),),);}
}
5. 代码详细解释
1. 定义 Event 类
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
CounterEvent 是一个抽象类,所有事件类型都继承自该类。我们定义了两个具体的事件:IncrementEvent 和 DecrementEvent,分别表示计数器增加和减少。
2. 定义 State 类
class CounterState {final int counter;CounterState({required this.counter});
}
CounterState 用于表示计数器的状态,包含一个名为 counter 的整数变量,表示当前计数值。State 负责存储应用的状态,它是 BLoC 模式的核心。
3. 定义 Bloc 类
class CounterBloc extends Bloc<CounterEvent, CounterState> {CounterBloc() : super(CounterState(counter: 0));@overrideStream<CounterState> mapEventToState(CounterEvent event) async* {if (event is IncrementEvent) {yield CounterState(counter: state.counter + 1);} else if (event is DecrementEvent) {yield CounterState(counter: state.counter - 1);}}
}
CounterBloc 是处理业务逻辑的核心部分,继承自 Bloc<CounterEvent, CounterState>。在构造函数中,我们初始化了计数器状态为 0。mapEventToState 方法用于处理事件并将状态转换为新的状态。例如,当接收到 IncrementEvent 时,计数器的值加 1,并通过 yield 关键字返回一个新的 CounterState。
4. 提供 Bloc 并更新 UI
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: BlocProvider(create: (context) => CounterBloc(),child: CounterPage(),),);}
}
BlocProvider 用于提供 CounterBloc 实例,确保它在 widget 树中是可访问的。在这里,我们将 CounterBloc 提供给 CounterPage 进行状态管理。
BlocBuilder<CounterBloc, CounterState>(builder: (context, state) {return Column(children: [Text('Counter Value: ${state.counter}'),Row(children: [ElevatedButton(onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),child: Text('Increment'),),ElevatedButton(onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),child: Text('Decrement'),),],),],);},
)
BlocBuilder 监听 CounterBloc 输出的 CounterState,并根据 state 更新 UI。点击按钮时,通过 context.read<CounterBloc>().add() 来触发相应的事件,从而更新计数器的状态。
6. 总结
通过 BLoC 模式,我们成功将业务逻辑从 UI 层分离,使得应用的状态管理更加清晰、可维护。BLoC 模式的核心思想是通过事件驱动状态的变化,UI 只需要关注状态的变化,而不需要直接操作状态或事件,降低了组件之间的耦合性。
这种模式特别适合于需要管理大量复杂状态的应用,例如社交媒体、新闻应用等,通过合理的事件与状态划分,可以实现高效的 UI 更新和良好的代码结构。
相关文章:
Flutter鸿蒙next 使用 BLoC 模式进行状态管理详解
1. 引言 在 Flutter 中,随着应用规模的扩大,管理应用中的状态变得越来越复杂。为了处理这种复杂性,许多开发者选择使用不同的状态管理方案。其中,BLoC(Business Logic Component)模式作为一种流行的状态管…...
Gen-RecSys——一个通过生成和大规模语言模型发展起来的推荐系统
概述 生成模型的进步对推荐系统的发展产生了重大影响。传统的推荐系统是 “狭隘的专家”,只能捕捉特定领域内的用户偏好和项目特征,而现在生成模型增强了这些系统的功能,据报道,其性能优于传统方法。这些模型为推荐的概念和实施带…...
Android 重新定义一个广播修改系统时间,避免系统时间混乱
有时候,搞不懂为什么手机设备无法准确定义系统时间,出现混乱或显示与实际不符,需要重置或重新设定一次才行,也是真的够无语的!! vendor/mediatek/proprietary/packages/apps/MtkSettings/AndroidManifest.…...
第3章:角色扮演提示-Claude应用开发教程
更多教程,请访问claude应用开发教程 设置 运行以下设置单元以加载您的 API 密钥并建立 get_completion 辅助函数。 !pip install anthropic# Import pythons built-in regular expression library import re import anthropic# Retrieve the API_KEY & MODEL…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Vision Kit
1.问题描述: 人脸活体检测页面会有声音提示,如何控制声音开关? 解决方案: 活体检测暂无声音控制开关,但可通过其他能力控制系统音量,从而控制音量。 活体检测页面固定音频流设置的是8(无障碍…...
【问题解决】Tomcat由低于8版本升级到高版本使用Tomcat自带连接池报错无法找到表空间的问题
问题复现 项目上历史项目为解决漏洞扫描从Tomcat 6.0升级到了9.0版本,服务启动的日志显示如下警告,数据源是通过JNDI方式在server.xml中配置的,控制台上狂刷无法找到表空间的错误(没截图) 报错: 06-Nov-…...
Git LFS
Git LFS(Git Large File Storage)是一个用于管理和版本控制大文件的工具,它扩展了 Git 的功能,帮助处理大文件或二进制文件的存储和管理问题。 为什么需要 Git LFS? Git 默认是针对文本文件进行优化的,尤…...
基于Redis缓存机制实现高并发接口调试
创建接口 这里使用的是阿里云提供的接口服务直接做的测试,接口地址 curl http://localhost:8080/initData?tokenAppWithRedis 这里主要通过参数cacheFirstfalse和true来区分是否走缓存,正常的业务机制可能是通过后台代码逻辑自行控制的,这…...
数字化转型实践:金蝶云星空与钉钉集成提升企业运营效率
数字化转型实践:金蝶云星空与钉钉集成提升企业运营效率 本文介绍了深圳一家电子设备制造企业在数字化转型过程中,如何通过金蝶云星空与钉钉的高效集成应对挑战、实施解决方案,并取得显著成果。集成项目在提高沟通效率、自动化审批流程和监控异…...
Flutter 鸿蒙next 中使用 MobX 进行状态管理
Flutter & 鸿蒙next 中使用 MobX 进行状态管理 在应用开发中,状态管理是一个至关重要的环节,特别是在复杂的Flutter或鸿蒙next项目中。状态的变化往往会影响UI的更新,因此,选择一种高效、灵活的状态管理工具显得尤为重要。Mo…...
1.62亿元!812个项目立项!上海市2024年度“科技创新行动计划”自然科学基金项目立项
本期精选SCI&EI ●IEEE 1区TOP 计算机类(含CCF); ●EI快刊:最快1周录用! 知网(CNKI)、谷歌学术期刊 ●7天录用-检索(100%录用),1周上线; 免费稿件评估 免费匹配期…...
Redis数据库测试和缓存穿透、雪崩、击穿
Redis数据库测试实验 实验要求 1.新建一张user表,在表内插入10000条数据。 2.①通过jdbc查询这10000条数据,记录查询时间。 ②通过redis查询这10000条数据,记录查询时间。 3.①再次查询这一万条数据,要求根据年龄进行排序&#…...
[vulnhub] DarkHole: 2
https://www.vulnhub.com/entry/darkhole-2,740/ 端口扫描主机发现 探测存活主机,185是靶机 # nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-08 18:02 CST Nmap scan report for 192.168.75.1 Host is up (0.…...
《XGBoost算法的原理推导》12-2 t轮迭代中对样本i的预测值 公式解析
本文是将文章《XGBoost算法的原理推导》中的公式单独拿出来做一个详细的解析,便于初学者更好的理解。 好的,公式(12-2)表示的是 XGBoost 在第 t t t 轮迭代中对样本 i i i 的预测值。它说明了在第 t t t 轮迭代中,模型的预测是通过累加之前…...
./bin/mindieservice_daemon启动成功
接MindIE大模型测试及报错Fatal Python error: PyThreadState_Get: the function must be called with the GIL held,-CSDN博客经过调整如下红色部分参数,昇腾310P3跑起来了7b模型: rootdev-8242526b-01f2-4a54-b89d-f6d9c57c692d-qjhpf:/home/apulis-de…...
Linux: network: ip link M-DOWN的具体含义是什么?
文章目录 参考简介实例代码解释openstack上的显示如果是在一个interface上建立了vlan参考 https://unix.stackexchange.com/questions/348327/using-ip-what-does-m-down-mean www.policyrouting.org/iproute2.doc.html#ss9.1 简介 是指上一级的接口的状态。 实例 4: ersp…...
Spring中的过滤器和拦截器
Spring中的过滤器和拦截器 一、引言 在Spring框架中,过滤器(Filter)和拦截器(Interceptor)是实现请求处理的两种重要机制。它们都基于AOP(面向切面编程)思想,用于在请求的生命周期…...
leetcode20.括号匹配
题目描述 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个…...
Unity性能优化-具体操作
批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。 Draw Call性能消耗原因是命令从Runtime到Driver的过程中&…...
【嵌入式开发——ARM】1ARM架构
嵌入式领域,使用ARM架构的芯片公司可不占少数吧,intel的x86架构主要占据PC、服务器市场,ARM架构主要占据移动市场。x86架构和ARM架构不同的主要原因,是背后使用的计算机指令集不同。计算机有自己的语言系统(汇编&#…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
