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

news2024/11/18 6:17:52

【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.coloradmin.cn/o/554669.html

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

相关文章

Linux中vim编辑器

目录 Linux中vim编辑器 学习vim的原因 vim的三种模式 命令模式 输入模式 末行模式 模式切换方法 模式的常用命令 命令模式 --- 光标移动 命令模式 --- 赋值、粘贴、删除 命令模式 --- 文件内容查找 命令模式 --- 撤销编辑及保存退出 末行模式 --- 保存文件及退出vi…

探究企业角色权限管理的重要性及实践方法

角色权限管理是企业网盘工具中的重要功能。它是指将特定角色分配给用户&#xff0c;然后根据用户的工作要求为这些角色分配访问权限的过程。通过使用基于角色的权限&#xff0c;组织可以确保员工只能访问执行工作职责所需的文件和文件夹。那么企业角色权限管理有必要吗&#xf…

提高测试效率,用封装自动化框架实现Web页面自动滚动!

目录 前言&#xff1a; 一、什么是Web自动化框架&#xff1f; 二、为什么需要封装&#xff1f; 三、自动页面滚动功能实现原理 四、封装代码实现 五、总结 前言&#xff1a; Web自动化是现代软件开发过程中的重要组成部分&#xff0c;为了提高开发效率&#xff0c;开发人…

【限时开放!99元交个朋友】ChatGPT AIGC研讨社拉新!

前言 不知道你是否有这样的困惑&#xff1a;想探索ChatGPT的奥秘&#xff0c;却不知道如何入手&#xff1f;想让ChatGPT助力学习工作&#xff0c;却不知如何调教&#xff1f;想在AI浪潮中勇立潮头&#xff0c;却找不到学习路径&#xff1f;怀揣创新想法&#xff0c;却不知如何…

【广州华锐互动】数字孪生编辑器:简单高效的可视化管理系统编辑工具

广州华锐互动开发的数字孪生编辑器&#xff0c;是一款基于数字孪生技术的可视化建模工具&#xff0c;通过将现实世界中的对象数字化&#xff0c;以虚拟现实的方式展现在计算机屏幕上&#xff0c;使用户可以进行模拟、预测和优化实际场景中的操作。 数字孪生编辑器已在工业、建…

【博士论文latex写作】

1.安装编译器 到官网下载镜像&#xff1a;https://mirrors.hit.edu.cn/CTAN/systems/texlive/Images/ 选择 texlive2023.iso 也可选择百度云2022版本&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1WLVX8Zn1E4hIYRupLBabNA?pwdgm63 提取码&#xff1a;gm63 打开后…

8.防火墙-SNAT和DNAT

文章目录 SNAT-内网客户访问外网服务原理操作实验 DNAT-外网客户访问内网服务原理操作实验 tcpdump SNAT-内网客户访问外网服务 原理 由内网到外网&#xff1a;从内网发到外网的数据包的源IP由私网IP转换成公网IP 由外网到内网&#xff1a;从外网发到内网的数据包的目的IP由公…

自动驾驶成为汽车产业未来趋势,连接器行业迎来进一步发展

连接器的应用范围越来越广泛&#xff0c;从汽车、工业、医疗、消费电子、通信、计算机到航空航天&#xff0c;几乎所有的领域都使用连接器。 以汽车行业为例&#xff0c;一辆燃油汽车通常需要几百到上千个连接器&#xff0c;而电动汽车则需要更多&#xff0c;因为需要连接电池…

iptables防火墙(二)

iptables防火墙&#xff08;二&#xff09; 一、SNAT策略1、SNAT策略简述2、配置实验 二、DNAT策略1、DNAT策略简述2、配置实验 三、Linux抓包工具tcpdump四、防火墙规则保存 一、SNAT策略 1、SNAT策略简述 SNAT策略就是将从内网传给外网的数据包的源IP由私网IP转换成公网IP&…

软考——邻接矩阵

数组(邻接矩阵)表示法 建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间关系)。 设图A=(V,E)有n个顶点,则图的邻接矩阵是一个二位数组A.arcs[n][n],定义为:

【现场问题】oracle,schema = metaData.getConnection().getSchema()报错

oracle获取Schema报错 问题描述折磨我的地方版本的问题 jar冲突 问题描述 折磨我的地方 版本的问题 ojdbc8&#xff0c;问题是我一直都是这个版本&#xff0c;我换了大小版本都不可以&#xff01;&#xff01;&#xff01;&#xff01; jar冲突 我怎么找得到这个jar的冲突呢…

06 Android开机启动之Bootanimation

Android开机启动之Bootanimation 前言 手机或其他Android智能终端开机都会先显示一个动画,然后才进入桌面,接下来看看这个bootanimation程序是怎么跑的 一、bootanimation框图 从上面的启动框图中可以看出: bootanimation也是init进程启动的,在init.rc中启动bootanim进程启…

Win10系统电脑开机黑屏一直转圈无法进入桌面怎么办?

Win10系统电脑开机黑屏一直转圈无法进入桌面怎么办&#xff1f;有用户电脑开机了之后无法进入到桌面中&#xff0c;开机了之后&#xff0c;电脑桌面只有显示一个黑屏和转圈的图标&#xff0c;一直都无法进入到桌面中。强制重启电脑之后依然是这样&#xff0c;那么这个情况怎么去…

USB摄像头描述符参数获取和来源分析

USB摄像头描述符参数获取和来源分析 文章目录 USB摄像头描述符参数获取和来源分析描述符USB设备描述符描述符 USB摄像头参数获取myuvc.c结果device descriptor设备描述符configuration descriptor配置描述符interface association接口关联inteface desciptor atsettingvideocon…

详解创建共享邮箱的步骤和方法

共享邮箱是一个类似于分发列表 (DL) 的组&#xff0c;它具有由组织内的一组用户共享的公共电子邮件地址。与 DL 不同&#xff0c;外部成员不能添加到共享邮箱&#xff0c;并且不支持流。发送到共享邮箱的电子邮件不会出现在个人用户的邮箱中&#xff0c;从而减少电子邮件重复。…

【设计模式】我终于读懂了观察者模式。。。

文章目录 &#x1f506;天气预报项目需求,具体要求如下&#x1f506;天气预报设计方案 - 普通方案&#x1f506;问题分析 &#x1f506;观察者模式&#x1f506;debug下去看一下&#x1f506;观察者模式的好处&#x1f506;观察者模式在Jdk 应用的源码分析 &#x1f506;天气预…

凭借这个笔记,拿下8家大厂offer....

如何拿到多家大厂的offer&#xff0c;没有过硬的实力&#xff0c;就需要不断的学习。 我是如何拿到&#xff0c;阿里&#xff0c;腾讯&#xff0c;百度等八家大厂的offer的&#xff0c;今天我就给大家来分享我的秘密武器&#xff0c;阿里大神整理的包括&#xff0c;测试基础&am…

【MySQL联合查询】轻松实现数据关联

1、联合查询 联合查询又称为多表查询&#xff0c;它的基本执行过程就是笛卡尔积 1.1 认识笛卡尔积 那么什么是笛卡尔积呢&#xff1f; 答&#xff1a;笛卡尔积就是将两张表放在一起进行计算&#xff0c;把第一张表的每一行分别取出来和第二张表的每一行进行连接&#xff0c;得到…

springboot导出excel 多个sheet导出

1.pom.xml <!--文件导出--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.poi</groupId><ar…

使用Win10自带的PowerShell命令校验文件和镜像文件的Hash值(MD5、SHA1/256等)正确性

通常为了保证我们从网上下载的文件的完整性和可靠性&#xff0c;我们把文件下载下来以后都会校验一下MD5值或SHA1值&#xff08;例如验证下载的Win10 ISO镜像是否为原始文件&#xff09;&#xff0c;这一般都需要借助专门的MD5检验工具来完成。但其实使用Windows系统自带的Wind…