【Flutter 工程】001-Flutter 状态管理:Riverpod

news/2025/1/22 21:38:55/

【Flutter 工程】001-Flutter 状态管理:Riverpod

文章目录

  • 【Flutter 工程】001-Flutter 状态管理:Riverpod
  • 一、概述
    • 1、官方状态管理
    • 2、状态管理解决方案
    • 3、为什么选择 Riverpod
      • Riverpod 官方文档
      • Riverpod 提供的几种 Provider
  • 二、官方示例
    • 1、安装
    • 2、官方示例
    • 3、代码生成
    • 4、官方示例运行结果
  • 三、基本使用
    • 1、改造 `main.dart`
    • 2、创建 `home_page.dart`
    • 3、创建 `hello_state.dart`
    • 4、运行结果
  • 四、使用代码生成
    • 1、改造 `hello_state.dart`
    • 2、代码生成
    • 3、改造 `home_page.dart`
    • 4、运行结果
    • 5、为什么在Riverpod中使用代码生成

一、概述

1、官方状态管理

状态管理处理应用程序数据流动和 UI 更新的关键概念。在 Flutter 应用程序中,状态管理确保应用程序 UI 和数据保持同步,共享和同步数据,并提供良好的代码结构和可维护性。

Flutter 提供了 StatefulWidget 作为最基本的状态管理方法。有状态组件可以存储和更新自身状态,适用于简单的场景和局部状态。

然而,StatefulWidget 存在以下问题:

  1. 状态管理复杂性:当组件树庞大且状态需要在多个组件之间共享时,状态管理变得复杂,代码难以理解和维护。
  2. 性能问题:相比 StatelessWidgetStatefulWidget 在状态变化时会导致更多组件重建,可能影响应用程序性能,尽管Flutter已经进行了性能优化。
  3. 生命周期管理复杂性StatefulWidget 具有复杂的生命周期,需要处理多个生命周期方法(如initStatedidUpdateWidget 和dispose),导致代码复杂和难以管理。
  4. 难以测试:由于 StatefulWidget 具有内部状态,编写单元测试和集成测试变得更加困难,可能影响应用程序的质量和可靠性。
  5. 重用性差StatefulWidget 的状态通常与特定实例紧密耦合,降低了组件的可重用性。

2、状态管理解决方案

在 Flutter 中,还有其他的状态管理方法可供选择,以下是一些常见的状态管理方法。

  1. InheritedWidgetInheritedModel:这些是 Flutter 提供的允许状态在组件树中向下传递的特殊类型的组件。它们可以帮助你在应用程序的不同层级之间共享状态。这种方法对于较小的应用程序或有限的状态共享需求较为合适
  2. Provider: 一个依赖注入和状态管理第三方库,它是在 InheritedWidget 基础上做了封装,有上面组件的能力,但是更简单易用。Provider 可以监听状态变化,并在需要时重新构建关联的组件。这种方法适用于各种规模的应用程序,具有良好的可扩展性和灵活性。
  3. Riverpod: 一个相对较新的状态管理库,类似于 Provider,但提供了更多的功能和改进。Riverpod 允许你创建不可变的、可组合的和可测试的状态管理解决方案。这种方法适用于需要更高度可控和可测试性的应用程序
  4. BLoC(Business Logic Component) :一种基于响应式编程的状态管理方法。BLoC 将业务逻辑与 UI 分离,使你可以轻松地测试和重用代码。BLoC 通常与 RxDart(一种 Dart 的响应式编程库)一起使用,以提供强大的数据流处理能力。这种方法适用于需要处理复杂业务逻辑和大量数据流的应用程序
  5. Redux: 一种集中式状态管理库,它将应用程序的状态存储在一个单一的状态树中。Redux 使用纯函数(称为reducers)来处理状态更新,使你可以轻松地跟踪和管理应用程序的状态变化。这种方法适用于需要严格的状态管理和可预测性的应用程序

具体选择什么样的状态管理方法,这取决于你应用程序的需求、复杂性和个人喜好。不同的方法有不同的优缺点,因此在选择状态管理方法时,请务必充分了解每种方法的特点,并权衡其适用性。

3、为什么选择 Riverpod

究其原因,还是 Riverpod 的一些主要特点比较给力,与我们的需求契合,且听我慢慢道来……

  1. 不可变性。Riverpod 中的状态是不可变的,这意味着状态在更新时会创建一个新的对象,而不是修改现有对象。这有助于减少错误,并使状态更易于理解和跟踪。
  2. 类型安全。Riverpod 在编译时提供了更强的类型安全性,有助于减少类型错误并提高代码质量。
  3. 无需 BuildContext。 与 Provider 不同,Riverpod 不依赖于 BuildContext 来访问状态。这使得在组件之外的位置(如函数或类)访问状态变得更加容易,同时提高了可测试性。
  4. 可组合。Riverpod 允许你组合不同的 Provider 以创建更复杂的状态管理解决方案。这有助于保持代码的模块化和可维护性。
  5. 易于测试。由于 Riverpod 的状态不依赖于 BuildContext,你可以更轻松地编写单元测试。此外,Riverpod 提供了用于模拟状态和测试的实用工具。
  6. 家族功能。Riverpod 具有所谓的“家族”功能,允许你根据参数创建多个相同类型的 Provider 实例。这使得在使用相同逻辑但参数不同的多个组件时,可以更好地管理状态。
  7. 非常灵活。Riverpod 具有很高的灵活性,可以很好地适应不同的应用程序结构和需求。你可以使用 Riverpod 来构建简单的局部状态管理,或者构建复杂的全局状态管理解决方案。

总之,Riverpod 是一个强大的状态管理库,适用于各种规模的 Flutter 应用程序。它提供了不可变性、类型安全性、无需 BuildContext 的访问、可组合性、易于测试和家族功能等多种优点。如果你正在寻找一个现代、灵活且易于使用的状态管理解决方案,Riverpod 是一个值得考虑的选择。

Riverpod 官方文档

https://docs-v2.riverpod.dev/zh-hans/

Riverpod 提供的几种 Provider

image-20230522142246669

二、官方示例

1、安装

flutter pub add flutter_riverpod dev:custom_lint dev:riverpod_lint riverpod_annotation dev:build_runner dev:riverpod_generator

2、官方示例

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';part 'main.g.dart';// 我们创建一个 “provider”,它将用于保存一个值(这里是 “Hello world”)。
// 通过使用一个 provider,我们能够模拟或覆盖被暴露的值。

String helloWorld(HelloWorldRef ref) {return 'Hello world';
}void main() {runApp(// 为了能让组件读取 provider,我们需要将整个// 应用都包裹在 “ProviderScope” 组件内。// 这里也就是存储我们所有 provider 状态的地方。ProviderScope(child: MyApp(),),);
}// 扩展来自 Riverpod 的 HookConsumerWidget 而不是 HookWidget
class MyApp extends ConsumerWidget {Widget build(BuildContext context, WidgetRef ref) {final String value = ref.watch(helloWorldProvider);return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('Example')),body: Center(// 读取 provider 的值// 此处为了方便查看,设置了大字体child: Text(value, style: const TextStyle(fontSize: 40),),),),);}
}

3、代码生成

# --delete-conflicting-outputs 可选,会在生成代码冲突的时候,删除原来的代码,重新生成
flutter pub run build_runner build --delete-conflicting-outputs

4、官方示例运行结果

image-20230522133110041

三、基本使用

1、改造 main.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:study/pages/HomePage.dart';void main() {runApp(// 为了能让组件读取 provider,我们需要将整个// 应用都包裹在 “ProviderScope” 组件内。// 这里也就是存储我们所有 provider 状态的地方。const ProviderScope(child: MyApp(),),);
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: HomePage(),);}
}

2、创建 home_page.dart

/lib/pages/home_page.dart

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';import '../states/hello_state.dart';class HomePage extends HookConsumerWidget {const HomePage({super.key});Widget build(BuildContext context, WidgetRef ref) {final hello = ref.watch(helloStateProvider);return Scaffold(appBar: AppBar(title: const Text('Home'),),body: Center(child: SizedBox(height: 400,child: Column(children: [// 文本Text(hello.hello, style: const TextStyle(fontSize: 40),),// 更新文本ElevatedButton(style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 50))),onPressed: () {ref.read(helloStateProvider.notifier).setHello("文本更新了!");},child: const Text('Update'),),],),)),);}
}

3、创建 hello_state.dart

lib/state/hello_state.dart

import 'package:flutter_riverpod/flutter_riverpod.dart';class HelloState {final String hello;HelloState({this.hello = 'Hello World',});
}class HelloStateProvider extends StateNotifier<HelloState> {HelloStateProvider() : super(HelloState());void setHello(String hello) {state = HelloState(hello: hello,);}
}final helloStateProvider = StateNotifierProvider<HelloStateProvider, HelloState>((ref) => HelloStateProvider(),
);

4、运行结果

image-20230522140032127

四、使用代码生成

代码生成是指使用工具为我们生成代码。

在Dart中,它的缺点是需要额外的步骤来“编译”应用。 尽管这个问题可能会在不久的将来得到解决, 但Dart团队正在研究并解决这个问题的潜在方案。

使用Riverpod时,代码生成是完全可选的。 当然你也完全可以不使用。

与此同时,Riverpod支持代码生成,且推荐你使用它。

1、改造 hello_state.dart

import 'package:riverpod_annotation/riverpod_annotation.dart';part 'hello_state.g.dart';
class HelloList extends _$HelloList {List<String> build() {return ["hello world!"];}void addHello(String hello) {state = [...state, hello];}
}

2、代码生成

# --delete-conflicting-outputs 可选,会在生成代码冲突的时候,删除原来的代码,重新生成
flutter pub run build_runner build --delete-conflicting-outputs

3、改造 home_page.dart

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';import '../states/hello_state.dart';class HomePage extends HookConsumerWidget {const HomePage({super.key});Widget build(BuildContext context, WidgetRef ref) {final List<String> hellos = ref.watch(helloListProvider);return Scaffold(appBar: AppBar(title: const Text('Home'),),body: Center(child: SizedBox(height: 400,child: Column(children: [// 文本:遍历 hellos 列表...hellos.map((e) => Text(e, style: const TextStyle(fontSize: 40),)),// 更新文本ElevatedButton(style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 50))),onPressed: () {// 获取当前时间DateTime now = DateTime.now();ref.read(helloListProvider.notifier).addHello("hello ${now.second}");},child: const Text('Update'),),],),)),);}
}

4、运行结果

image-20230522141813454

5、为什么在Riverpod中使用代码生成

你可能在想:“如果在Riverpod中代码生成是可选的,为什么要使用?”

让你的代码生活更简单。

这包括但不限于:

  • 更好的语法, 更可读且更灵活,而且还能减少学习曲线。
    • 不需要担心FutureProviderProvider 还是其他 provider。仅需写下你的逻辑, Riverpod将为你选择最合适的provider。
    • 向provider传递参数现在不受限制。不再局限于使用 family 和传递单个参数, 现在可以传递任何形式的参数。这包括命名参数、可选参数甚至默认值。
  • 在Riverpod中编写的代码支持 有状态热重载
  • 更好地调试,通过生成额外的元数据然后用调试器调试。
  • Riverpod的一些功能将只支持代码生成。

与此同时,许多应用程序中已经使用了代码生成比如 Freezed 或 json_serializable。 在这种情况下,你的项目可能已经为代码生成配置好了,使用Riverpod应该很简单。


http://www.ppmy.cn/news/84413.html

相关文章

json-server的基本使用

1、mock是什么&#xff1f; mockjs 作用&#xff1a;生成随机数据&#xff0c;拦截 Ajax 请求 目的&#xff1a;很多时候前端开发页面的过程中&#xff0c;后端的接口并没有写好&#xff0c;这个时候需要前端自己定义接口及接口的返回数据的结构体&#xff0c;这个时候就需要…

SpringCloud Nacos实战应用

目录 1 Nacos安装1.1 Nacos概要1.2 Nacos架构1.3 Nacos安装1.3.1 Nacos Derby安装1.3.2 Nacos MySQL版安装1.3.3 Docker 安装Nacos 2 Nacos功能应用2.1 Nacos服务注册与发现2.2 负载均衡2.3 配置中心2.4 灰度发布 3 Nacos集群3.1 集群架构3.2 Nacos集群部署3.3 客户端接入Nacos…

【网络】无线路由器和路由器的配置方法

目录 &#x1f352;常见的接入互联网方式 &#x1f353;WAN配置 &#x1f353;LAN口配置 &#x1f353;WLAN配置 &#x1f353;WLAN安全 &#x1f352;路由设备管理 &#x1f353;路由器内部组件 &#x1f353;Cisco路由器的启动过程 &#x1f353;基础命令 &#x1f34e;show …

一篇关于 ApiKit 的简单介绍

简介 本文介绍 ApiKit 工具&#xff0c;它是 API 文档、API 调试、API Mock、API 自动化测试一体化协作平台。 一、常用解决方案 使用 Swagger 管理 API 文档 使用 Postman 调试 API 使用 mockjs 等工具 Mock API 数据 使用 JMeter 做 API 自动化测试 二、存在的问题 维护…

Software List

Ubuntu22 Goldendict Ubuntu安装划词翻译软件Goldendict 单词翻译 句子翻译_ubuntu划词翻译_Bourne_Boom的博客-CSDN博客有道词典长期未更新&#xff0c;由于某些模块不支持的问题已经无法在Ubuntu18.04中使用了。现在介绍另一款强大的翻译软件——Goldendict。1.安装&#…

【RabbitMQ】| 狮子带你(超详细)原生Java操作兔子队列

目录 一. &#x1f981; 前言二. &#x1f981; 原生Java操作RabbitMQⅠ. 简单模式1. 添加依赖2. 编写生产者3. 编写消费者 Ⅱ. 工作队列模式1. 编写生产者2. 编写消费者3. 实现 Ⅲ. 发布订阅模式1. 编写生产者2. 编写消费者 Ⅳ. 路由模式1. 编写生产者2. 编写消费者 Ⅴ. 通配符…

导入/导出 Postcat 格式文件,打通数据不再难

导入 Postcat 插件。 使用 导入功能有多个入口&#xff0c;你可以在 API 分组处点击加号导入 API&#xff1a; 也可以在点击设置&#xff0c;然后选择导入选项 导出 Postcat 插件 支持导出 Postcat JSON 文件。 使用 进入空间页面&#xff0c;可以看到导出功能&#xff0c;点…

云时通 X 九毛九 | SRM系统助力连锁餐饮行业高效管理供应链

餐饮行业作为第三产业中的传统服务性行业之一&#xff0c;始终保持旺盛的发展势头&#xff0c;随着时代的进步&#xff0c;现代餐饮企业更多以连锁业态发展&#xff0c;其特点是多种形式相结合而生&#xff0c;将新颖的就餐空间与潮流文化和传统的经典美味互相融合&#xff0c;…