【Flutter】状态管理:高级状态管理 (Riverpod, BLoC)
当项目变得更加复杂时,简单的状态管理方式(如 setState() 或 Provider)可能不足以有效地处理应用中状态的变化和业务逻辑的管理。在这种情况下,高级状态管理框架,如 Riverpod 和 BLoC,可以提供更强大的工具,用于处理复杂的状态流、逻辑分离以及响应式编程。
在本教程中,我们将深入学习 Riverpod 和 BLoC 这两种高级状态管理框架,理解它们的核心概念,学会如何将业务逻辑与 UI 分离,并使用 Stream 处理复杂的状态流。
Riverpod 状态管理框架
Riverpod 是由 Provider 的作者开发的一个更加灵活、强大且类型安全的状态管理框架。相比 Provider,Riverpod 提供了更清晰的状态管理方式,同时避免了一些常见的错误和限制。它支持全局和局部的状态管理,适用于大型应用的开发。
安装 Riverpod
首先,在 pubspec.yaml 文件中添加 riverpod 依赖:
dependencies:flutter:sdk: flutterflutter_riverpod: ^2.0.0
然后运行 flutter pub get 安装依赖。
Riverpod 核心概念
在使用 Riverpod 之前,需要了解它的几个核心概念:
Provider:是Riverpod的基本状态提供者。它可以创建、管理并共享状态。ConsumerWidget:用于监听Provider并响应其状态变化。StateProvider:提供一种简单的方式来管理和监听状态。StateNotifier和StateNotifierProvider:用于管理复杂的业务逻辑和状态。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';// 定义一个 StateProvider 来管理状态
final counterProvider = StateProvider<int>((ref) => 0);void main() {runApp(ProviderScope(child: MyApp(),),);
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: CounterPage(),);}
}class CounterPage extends ConsumerWidget {Widget build(BuildContext context, WidgetRef ref) {// 通过 ref 读取 counterProvider 的状态final counter = ref.watch(counterProvider);return Scaffold(appBar: AppBar(title: Text('Riverpod Counter Example'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text('You have pushed the button this many times:'),Text('$counter',style: Theme.of(context).textTheme.headline4,),],),),floatingActionButton: FloatingActionButton(onPressed: () {// 更新状态ref.read(counterProvider.notifier).state++;},tooltip: 'Increment',child: Icon(Icons.add),),);}
}
代码详解
-
StateProvider:counterProvider是一个StateProvider,它用于管理整型计数器状态。我们定义了一个初始值0,并通过ref.watch监听状态的变化。 -
ProviderScope:这是Riverpod的核心组件,用于提供上下文中可访问的状态。ProviderScope必须在应用的最顶层。 -
ConsumerWidget:CounterPage继承自ConsumerWidget,用于监听状态提供者counterProvider,并在状态变化时重新构建 UI。 -
状态更新:
ref.read(counterProvider.notifier).state++用于更新状态。这里我们通过read方法获取StateProvider的notifier,然后修改其状态。
BLoC 状态管理框架
BLoC(Business Logic Component)是一种基于响应式编程的状态管理模式,它通过 Stream 处理复杂的状态流,实现了业务逻辑和 UI 的完全分离。这种模式适用于大型项目,能够确保代码的可维护性和扩展性。
BLoC 的核心思想是将事件流(Event)转换为状态流(State),从而使得业务逻辑独立于界面逻辑。
安装 flutter_bloc
在 pubspec.yaml 文件中添加 flutter_bloc 包依赖:
dependencies:flutter:sdk: flutterflutter_bloc: ^8.0.0bloc: ^8.0.0
运行 flutter pub get 安装依赖。
BLoC 核心概念
BLoC:用于处理输入的事件并输出相应的状态。它封装了业务逻辑和状态转换。Cubit:Cubit是BLoC的简化版本,通常用于处理简单的状态变化。Stream:BLoC和Cubit都依赖Stream来传递状态更新。BlocProvider和BlocBuilder:用于提供BLoC实例并在 UI 中监听状态变化。
示例:使用 Cubit 实现计数器
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';// 定义 Cubit,用于管理计数器状态
class CounterCubit extends Cubit<int> {CounterCubit() : super(0);void increment() => emit(state + 1); // 更新状态
}void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: BlocProvider(create: (context) => CounterCubit(),child: CounterPage(),),);}
}class CounterPage extends StatelessWidget {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('BLoC Counter Example'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text('You have pushed the button this many times:'),BlocBuilder<CounterCubit, int>(builder: (context, count) {return Text('$count',style: Theme.of(context).textTheme.headline4,);},),],),),floatingActionButton: FloatingActionButton(onPressed: () {// 获取 CounterCubit 实例并调用 incrementcontext.read<CounterCubit>().increment();},tooltip: 'Increment',child: Icon(Icons.add),),);}
}
代码详解
-
CounterCubit:Cubit是BLoC的简化版本,用于处理简单的状态更新。这里我们定义了一个计数器Cubit,初始状态为0,并通过increment()方法更新状态。 -
BlocProvider:提供CounterCubit实例,并使其在子组件中可访问。 -
BlocBuilder:用于监听Cubit的状态变化,并根据新的状态更新 UI。 -
状态更新:通过
context.read<CounterCubit>().increment()调用Cubit的increment方法,更新状态。
使用 Stream 处理复杂状态流
在 BLoC 中,Stream 是核心工具,用于传递状态更新。我们可以将用户的输入事件(如点击按钮)作为 Stream 的输入,并将业务逻辑的输出作为状态流输出给 UI。
示例:BLoC 处理多种事件
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';// 定义事件
abstract class CounterEvent {}class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}// 定义 BLoC 类
class CounterBloc extends Bloc<CounterEvent, int> {CounterBloc() : super(0);Stream<int> mapEventToState(CounterEvent event) async* {if (event is Increment) {yield state + 1;} else if (event is Decrement) {yield state - 1;}}
}void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: BlocProvider(create: (context) => CounterBloc(),child: CounterPage(),),);}
}class CounterPage extends StatelessWidget {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('BLoC Stream Example'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text('You have pushed the button this many times:'),BlocBuilder<CounterBloc, int>(builder: (context, count) {return Text('$count',style: Theme.of(context).textTheme.headline4,);},),],),),floatingActionButton: Row(mainAxisAlignment: MainAxisAlignment.end,children: <Widget>[FloatingActionButton(onPressed: () {context.read<CounterBloc>().add(Increment());},tooltip: 'Increment',child: Icon(Icons.add),),SizedBox(width: 10),FloatingActionButton(onPressed: () {context.read<CounterBloc>().add(Decrement());},tooltip: 'Decrement',child: Icon(Icons.remove),),],),);}
}
代码解析
-
事件类型:
CounterEvent是事件基类,Increment和Decrement是具体事件。 -
CounterBloc类:BLoC类负责接收事件,并使用mapEventToState方法将事件映射为状态更新流。 -
BlocProvider和BlocBuilder:与之前的Cubit示例类似,BlocProvider提供CounterBloc实例,BlocBuilder监听状态流并更新 UI。
总结
通过本教程的学习,你已经掌握了 Riverpod 和 BLoC 这两种高级状态管理框架。这两种框架都适用于大型项目中的复杂状态管理,能够有效地将业务逻辑与 UI 分离,并通过响应式编程处理状态流。
- Riverpod 适合类型安全、灵活的状态管理需求,提供简单易用的 API。
- BLoC 则非常适合需要严格分离业务逻辑和 UI 的项目,特别是在需要处理复杂状态流和多种事件的情况下。
掌握这些工具,将帮助你在实际项目中更加高效地管理复杂状态,构建出高质量的 Flutter 应用。
相关文章:
【Flutter】状态管理:高级状态管理 (Riverpod, BLoC)
当项目变得更加复杂时,简单的状态管理方式(如 setState() 或 Provider)可能不足以有效地处理应用中状态的变化和业务逻辑的管理。在这种情况下,高级状态管理框架,如 Riverpod 和 BLoC,可以提供更强大的工具…...
OAK相机的RGB-D彩色相机去畸变做对齐
▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去,或者产生的…...
smartctl硬盘检查工具
一、smartctl工具简介 Smartmontools是一种硬盘检测工具,通过控制和管理硬盘的SMART(Self Monitoring Analysis and Reporting Technology),自动检测分析及报告技术)技术来实现的,SMART技术可以对硬盘的磁头单元、盘片电机驱动系统、硬盘…...
清空MySQL数据表
要清空 MySQL 数据表,您可以使用 TRUNCATE 或 DELETE 命令 使用 TRUNCATE 命令 TRUNCATE 命令用于删除表中的所有数据,并重置自增 ID(如果存在): TRUNCATE TABLE table_name;将 table_name 替换为您要清空的表的名称…...
2024年妈杯MathorCup大数据竞赛A题超详细解题思路
2024年妈杯大数据竞赛初赛整体难度约为0.6个国赛。A题为台风中心路径相关问题,为评价预测问题;B题为库存和销量的预测优化问题。B题难度稍大于A题,可以根据自己队伍情况进行选择。26日早六点之前发布AB两题相关解题代码论文。 下面为大家带来…...
Kafka系列之:Kafka集群磁盘条带划分和Kafka集群磁盘扩容详细方案
Kafka系列之:Kafka集群磁盘条带划分和Kafka集群磁盘扩容详细方案 一、lsblk命令二、Kafka节点磁盘条带化方案一三、Kafka节点磁盘条带化方案二四、理解逻辑区块LE五、查看kafka节点磁盘条带划分情况六、Kafka节点磁盘扩容一、lsblk命令 lsblk命令用于列出块设备的信息,包括磁…...
【LeetCode】修炼之路-0007- Reverse Integer (整数反转)【python】
题目 Reverse Integer Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the signed 32-bit integer range [-231, 231 - 1], then return 0. Assume the environment does not allow you to store 64-b…...
【Flutter】页面布局:线性布局(Row 和 Column)
在 Flutter 中,布局(Layout)是应用开发的核心之一。通过布局组件,开发者可以定义应用中的控件如何在屏幕上排列。Row 和 Column 是 Flutter 中最常用的两种线性布局方式,用于水平和垂直排列子组件。在本教程中…...
C语言巨难题:执行操作可获得的最大总奖励 I(C语言版)
1.题目: 给你一个整数数组 rewardValues,长度为 n,代表奖励的值。 最初,你的总奖励 x 为 0,所有下标都是 未标记 的。你可以执行以下操作 任意次 : 从区间 [0, n - 1] 中选择一个 未标记 的下标 i。如果…...
【力扣】GO解决子序列相关问题
文章目录 一、引言二、动态规划方法论深度提炼子序列问题的通用解法模式 三、通用方法论应用示例:最长递增子序列(LeetCode题目300)Go 语言代码实现 四、最长连续递增序列(LeetCode题目674)Go 语言代码实现 五、最长重…...
Ubuntu20.04安装VM tools并实现主机和虚拟机之间文件夹共享
1、Ubuntu20.04安装VM tools 参考这个,很详细:Ubuntu 20.04 安装 VMwareTools 教程 2、实现主机与VMware虚拟机共享文件夹 设置共享文件夹参考:windows和虚拟机互传文件的三种方式 挂载操作参考:主机与VMware虚拟机共享文件夹&…...
Linux 学习笔记(十七)—— 文件系统
终极目标:理解 inode 和 软硬连接; 文件系统:Ext2; 文件 文件内容 文件属性; ——> 磁盘上存储的文件 存储的文件内容 存储的文件属性; Linux系统中:文件内容使用数据块存储,文件属性使用inode(固定…...
【计算机网络 - 基础问题】每日 3 题(五十八)
✍个人博客:https://blog.csdn.net/Newin2020?typeblog 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞…...
Netty入门基础:IO模型中BIO\NIO概念及区别【附演示代码】
文章目录 😀BIO💢实战demo 🌈NIO🏍Buffer核心属性核心方法 🎗Channel🎈Selector核心方法 🧨实战demo 🎨粘包与半包 😀BIO 传统IO模型,同步阻塞,每…...
vue2 使用环境变量
一. 在根目录下创建.env.xxx文件 .env 基础系统变量,无论何种环境,都可使用其中配置的值,其他环境中的变量会覆盖.env中的同名变量。 .env.development 开发环境 .env.production 生产环境 .env.staging 测试环境 二. 内容格式 vue2 使用是以…...
数据预处理
继续提取代码片段: 12. **导入iris数据集并查看前5行数据**: python from sklearn.datasets import load_iris iris load_iris() X iris.data print(iris数据集的维度为:, X.shape) print(iris数据集的前5行数据为:\n, X[:5]) …...
django宠物领养管理系统-计算机毕业设计源码26858
目录 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设计 3…...
使用TeamViewer远程局域网内的两台电脑
有个场景,有人还不知道TV可以局域网操作,记录一下。 主要就是修改设置,将取消激活改为接受 然后输入受控端的ip即可...
GUI简介、Swing的常用组件、java程序的运行过程、class文件、JAR、runable_jar、双括号初始化
GUI简介 GUI:图形用户界面,在计算机中采用图形的方式显示用户界面 java的GUI开发 AWT:java最早推出的GUI编程开发包,界面风格跟随操作系统SWT:eclipse就是java使用SWT开发的Swing:在AWT的基础上扩充了功能…...
@Autowired和@Resource和getBean()区别
今天遇到一个对我来说很奇葩的错误,我想在Service中注入bean,我这里使用了Autowired和Resource都不能注入,导致初始化失败,使用了getBean()方法就可以注入。从来没有遇到过这个问题。后来我查询了一下,才明白了原理。我…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
