必看,使用Provider优雅解决Riverpod的参数依赖

上一篇文章详细说明了状态管理在开发中的位置和所依赖的基础方法,帧与帧之间的变化是对应状态变化的体现,但每个框架都有其侧重点,Getx侧重简单,简单的页面,简单的状态管理,相对应的是复杂参数, 以及依赖传递时非常臃肿,需要使用很多Listen来同步不同的状态。Bloc有完整的filter状态转移的监听,非常适合编辑器等复杂状态,例如回退操作,这两者一个适合简单业务,一个适合复杂协同业务,大多数项目都没有那么极端,所以Flutter官方推荐了Riverpod作为首推的状态管理工具。

具体对比请移步 一篇文章,告别Flutter状态管理争论,问题和解决

任何工具都有缺点,与此同时,就会有一个或者丑陋,或者优雅的Work around级别的解决方案,riverpod也是,这篇文章试图解决riverpod丑陋的参数传递问题。例如: 如下场景,一个日程任务有多种来源和多个视图才能确定某条任务, 我们假定参数为来源日期来源计划来源看板,那我们定位这一条任务就需要如下代码

/// 声明状态,需要三个入参

class TaskDetail extends _$TaskDetail {TaskModel build(int taskId, int planId, int viewId) {return service.query(taskId, planId, viewId);}
}
/// 使用需要明确的三个参数
class TaskDetailScreen extends HookConsumerWidget {final int taskId;final int planId;final int viewId;const TaskDetailScreen({super.key,required this.taskId,required this.planId,required this.viewId,});Widget build(BuildContext context, WidgetRef ref) {final taskModel = ref.watch(taskDetailProvider(taskId, planId, viewId));return Container();}
}

目前为止这段代码没有体现出任何的缺点,反而做到了状态的声明使用的分离, 对于简单到中等复杂页面,这非常友好,如果我们选择使用非组件化,将所有代码写到这个TaskDetailScreen那将没有任何问题。因为不涉及参数传递或者指针(notifier)传递。但实际复杂的项目中,通常可重用可阅读也是很重要的指标,这个时候我们不得不考虑使用组件来提升这两个属性。例如:我们有一个富文本组件, 如果我们只有简单的交互,我们可以通过传递taskModel或者增加Callback(String)等方法,与provider进行交互,但这通常会写成如下代码。

/// 需要透传参数或者notifer, 或者抛出Callback
class RichEditor extends ConsumerWidget {final int taskId;final int planId;final int viewId;const RichEditor({super.key,required this.taskId,required this.planId,required this.viewId,});Widget build(BuildContext context, WidgetRef ref) {return GestureDetector(onTap: () => ref.read(taskDetailProvider(taskId, planId, viewId).notifier).saveContent('content'),child: Text('editor'),);}
}

这是非常丑陋的,虽然这种情况在使用Bloc时会被非常优雅的解决,但也可以通过一些结合来尝试解决中等复杂难度的场景。

分析痛点和难点

riverpod在这个场景下的痛点时需要透传参数,且组件化时非常丑陋,容易出错,那他的难点在于什么?在于全局的Scope, 所有全局的管理,例如Getx等都会面临多个层级重复页面,多参数的在同一个路由栈这样常见的问题,或者同时显示多个相同组件(同一个provider)。难点在于无法隔离,也就是跟Context关联,局部状态。

找到难点,我们就可以从这个点出发去尝试解决这个问题,或者是这种特定用例下的问题,在不脱离riverpod的情况下,我们的直观选择是在局部使用ProviderScope, 例如:

 ProviderScope(overrides: [sharedPrefsProvider.overrideWithValue(sharedPrefs),], child: const TaskDetailScreen()),

这是一种方案,但这种不符合riverpod的最佳实践,也容易造成状态管理的混乱,第二种方案是将参数使用其它方式进行Context相关的关联,比如通过自定义的InheritedWidget 这种方案类似于ThemeData的局部化处理,这种方案理解简单,但需要不同的页面和不同的Provider定制化, 第二种方案明显也丑陋的,虽然解决了部分问题, 但需要引入自定义的Scope。

组合Hook?

我们知道Riverpod是状态分离的,也就是声明和管理使用状态是完全分离的,所以一个简单好用的界面内状态就可以极大简化这种类型的状态处理。所以,Flutter Hook就完美的补充了这部分。例如

final calendarFormat = useState(CalendarFormat.week);TableCalendar(calendarFormat: calendarFormat.value,onFormatChanged: (format) {if (format != calendarFormat.value) {calendarFormat.value = format;}},),

这里状态只和页面有关,声明使用修改 都在一个build函数中完成,所以使用注解riverpod是有一点冗余的。本以为可以像React一样,子组件可以跨级获取父组件的状态,后来发现flutter_hook并不是如此,hook和riverpod结合时,hook更像是一个完全的局部管理,并没有Recact Scope这种概念,跨级传递状态。

组合Bloc ?

Bloc是重量级的,声明需要BlocProvider, 消费需要使用BlocConsumer<BlocA, BlocAState>, 这也有点丑陋,虽然我们避免了每一个需要复杂参数都需要声明一个单独的InheritedWidget, 但同样多了很多模版代码。如果是这样的组合,加重了页面的复杂性以及阅读理解难度, 不如直接使用Bloc。

反思, 是不是违背了设计的初衷?

当笔者处处碰壁的时候,想起了之前为了解决打点参数透传而写的一个库data_trakcer, 似乎两者是同样的问题,但不同于打点的简单字段,这里需要明确的状态通知程序。 回顾响应式的设计原则 数据向下传递,操作向上传递, 似乎认为难点或者痛点其实本不应该被关注, 因为按照设计原则,一切都是合理的,我们必须对每个有操作的组件回调到声明notifer进行调用,或者将参数notifer进行一级一级的传递。

应该如何是好?

上述思考的过程,让笔者重新梳理了主流的一些状态管理,但还有很多是我不曾使用和了解的,笔者也不确定是否有其他方案解决了我所头疼的问题,也或许根本不是问题。当我在看Bloc Flutter时,笔者发现,Bloc实现局部Scope的原理其实底层是Provider。这个被遗忘的基础状态管理

static T of<T extends StateStreamableSource<Object?>>(BuildContext context, {bool listen = false,}) {try {return Provider.of<T>(context, listen: listen);} on ProviderNotFoundException catch (e) {}}

所以,是否可以使用Provider + Rivderpod解决我所遇到的问题?如下代码:

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:https_sync_client/domain/task_provider.dart';
import 'package:provider/provider.dart' as provider;class TaskDetailScreen extends HookConsumerWidget {final int taskId;final int planId;final int viewId;const TaskDetailScreen({super.key,required this.taskId,required this.planId,required this.viewId,});Widget build(BuildContext context, WidgetRef ref) {final taskDetailNotifier =ref.watch(taskDetailProvider(taskId, planId, viewId).notifier);return provider.Provider(create: (context) => taskDetailNotifier,child: const RichEditor(),);}
}class RichEditor extends HookConsumerWidget {const RichEditor({super.key,});Widget build(BuildContext context, WidgetRef ref) {return GestureDetector(onTap: () {context.read<TaskDetail>().update(content: "");},child: Center(child: Container(color: Colors.amber,width: 100,height: 100,),),);}
}

这段代码似乎成功了,又似乎解决了我的痛点。成本似乎只有provider.Provider一个容器和context.read。能不能真的实现痛点,希望各位自己验证一下,实践才是检验真理的唯一标准, 笔者也不确定这么操作是不是符合最佳实践,但在笔者工作当中两个场景是非常令人头痛的,一个打点重运营类项目,经常需要按钮级别的打点需求, 第二是,业务复杂的组件且会堆叠的情况。

总结

遇到问题,解决问题是技术人的一个思考准则,当我们遇到丑陋代码时,总可以找到合适的姿势去改变一点,使之优雅好用一点,世界上没有银弹, 但软件开发领域因为不断的进步,也在逐渐的越来越好(至少不想回去做Android原生)。可以把疑问放到评论区,一起讨论。

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

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

相关文章

【Kubernetes】在 CentOS 7 上搭建 Kubernetes

在CentOS 7上从零开始搭建一个单节点Kubernetes(K8S)集群并安装Kubernetes Dashboard,可以按照以下步骤进行: 1. 环境准备 确保您的CentOS 7系统已经安装并更新到最新版本。您还需要确保系统具有足够的资源来运行Kubernetes集群。 2. 关闭SELinux和防火墙 出于简化操作…

西圣VS飞利浦VS倍思开放式耳机哪款值得入手?爆款产品无广大测评

在当今这个无线耳机盛行的时代&#xff0c;开放式耳机以其独特的佩戴舒适度和出色的音质体验&#xff0c;逐渐赢得了消费者的青睐&#xff0c;西圣、飞利浦、倍思作为市场上的知名品牌&#xff0c;都推出了各具特色的开放式耳机产品&#xff0c;许多消费者也因此不知道哪款更加…

《MySQL数据库》day3 -- 约束条件(非空、唯一性、主键、外键)、事务的四大特性(隔离性)

文章目录 0.技巧1.约束&#xff08;非常重要&#xff09;1.1什么是约束&#xff1f;1.2约束的种类1.3非空约束 not null1.4唯一性约束 unique1.5主键约束 primary key1.6外键约束&#xff08;foreign key&#xff0c;简称FK&#xff09; 2.事务&#xff08;很重要&#xff09;2…

如何安装和使用 Yarn

如何安装和使用 Yarn Yarn 是一个流行的包管理器&#xff0c;专为 JavaScript 项目设计&#xff0c;以提高依赖项管理的效率和一致性。与 npm 相比&#xff0c;Yarn 提供了更快的依赖安装速度、更准确的版本控制和改进的安全性&#xff0c;使其成为许多开发者的首选。 安装前…

在vue中使用echarts饼图示例

1.安装 npm install echarts --save 2.官方示例 option {title: {text: Referer of a Website,subtext: Fake Data,left: center},tooltip: {trigger: item},legend: {orient: vertical,left: left},series: [{name: Access From,type: pie,radius: 50%,data: [{ value: 104…

用css滤镜做颜色不同的数据卡片(背景图对于css滤镜的使用)

<template> <div class"xx_modal_maincon"><div class"xx_model_bt">履约起始日至计算日配额及履约情况</div><el-row><el-col :span"6"><div class"xx_modal_mod"><div class"mod…

虚拟机Linux-openEuler硬盘空间扩容

虚拟机Linux-openEuler硬盘空间扩容 1、需求场景 我们在使用虚拟机时&#xff0c;可能会出现磁盘空间不够用导致各种bug出现的情况。 首先&#xff0c;我们要扩展虚拟机的可用磁盘空间。如图所示&#xff0c;我的原本硬盘大小为8G&#xff0c;我们扩展到30GB 2、打开虚拟机…

android_uiautomator元素定位

通过UIAUTOMATOR的text属性定位到元素&#xff0c;并打印文本from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy import time # For W3C actions from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriv…

1_88. 合并两个有序数组

1_88. 合并两个有序数组 难度: 简单 提示: 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。请你合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意…

【CSP试题回顾】202006-1-线性分类器(优化)

CSP-202006-1-线性分类器 解题思路 1.数据结构&#xff1a; 代码使用了一个名为 MyPoint 的结构体来表示空间中的点。这个结构体包含三个属性&#xff1a;x 和 y 表示点的坐标&#xff0c;type 表示点的类型。点的类型用整数表示&#xff0c;类型 ‘A’ 用 0 表示&#xff0…

使用U盘重装CentOS7系统

下载CentOS7 系统镜像 制作启动U盘之前&#xff0c;首先要准备一个系统镜像&#xff0c;这里我在CentOS官网直接下载镜像文件&#xff1a; CentOS官网 为了保证下载速度&#xff0c;这里我使用阿里云镜像下载&#xff1a; 阿里云镜像 如上图所示&#xff0c;我需要一个带UI界…

ES 进阶知识

索引Index 一个索引就是一个拥有几分相似特征的文档的集合。比如说&#xff0c;你可以有一个客户数据的索引&#xff0c;另一个产品目录的索引&#xff0c;还有一个订单数据的索引。一个索引由一个名字来标识&#xff08;必须全部是小写字母&#xff09;&#xff0c;并且当我们…

pytorch代码中optimizer.step()和scheduler.step()有什么区别

optimizer.step()通常用在每个patch_size之中(一个patch_size的数据更新一次模型参数)&#xff0c;而scheduler.step()通常用在epoch里面,但是不绝对&#xff0c;可以根据具体的需求来做。只有用了optimizer.step()&#xff0c;模型才会更新&#xff0c;而scheduler.step()是对…

【机器学习】无监督学习算法之:K均值聚类

K均值聚类 1、引言2、K均值聚类2.1 定义2.2 原理2.3 实现方式2.4 算法公式2.4.1 距离计算公式2.4.1 中心点计算公式 2.5 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; K均值聚类 我不懂&#xff0c;能不能给我讲一讲&#xff1f; 小鱼&#xff1a;行&#xf…

latex在写算法`\For` 和 `\EndFor` 以及 `FOR` 和 `\ENDFOR` ,报错Undefined control sequence.

这里写目录标题 1. 错误原因2. 进行改正3. 爱思唯尔期刊与施普林格期刊对于算法的格式不太一样&#xff0c;不能直接套用总结 1. 错误原因 我在算法中使用\For&#xff0c;\EndFor 2. 进行改正 换成FOR&#xff0c;\ENDFOR 3. 爱思唯尔期刊与施普林格期刊对于算法的格式不太…

cdp集群yarn组件ResourceManager无法启动问题记录

前言&#xff1a;cdp集群装了Kerberos认证&#xff0c;在进行权限集成时集群所有组件高可用状态失效&#xff0c;yarn组件ResourceManager停止 查看ResourceManager组件日志&#xff0c;报错如下&#xff1a; Error starting ResourceManager org.apache.hadoop.service.Servi…

CMake学习笔记(一)一个最简单的CMakeLists嵌套示例

目录 1 mkdir project_macro 2 在project_marco中建立CMakeLists.txt 3 建立专门的src文件夹 4 在src中添加main.cpp和CMakeLists.txt 5 回到project_macro目录&#xff0c;建立build文件夹 6 进入build 文件夹&#xff0c;开始cmake 7 在build文件夹里执行make指令 8 …

Softmax到底行还是列

对于二维张量&#xff0c;飞话不多说&#xff0c;直接看代码 input_tensor torch.tensor([[2,3,5],[2,2,2],[3,1,3]], dtypetorch.float32) # input_tensor torch.rand((1000,1000,100)) print(input_tensor) print("*"*40) print("沿着dim1&#xff0c;计算…

URL 中 pathname 的命名规则

URL 中 pathname 的命名规则 pathname 是 URL 中标识特定资源或页面的路径部分。它通常遵循以下命名约定&#xff1a; 使用小写字母和连字符&#xff1a;路径名应使用小写字母和连字符&#xff08;-&#xff09;分隔单词。避免使用大写字母、下划线或其他特殊字符。使用描述性…

直播行业网络安全建设

一、引言 直播行业近年来蓬勃发展&#xff0c;吸引了大量用户和资本的关注。然而&#xff0c;随着行业的壮大&#xff0c;网络安全问题也日益凸显。构建一个安全、稳定的直播行业网络对于保障用户权益、维护行业秩序具有重要意义。本文将详细探讨直播行业安全网络的构建与保障…