Flutter 知识集锦 | 监听与通知 ChangeNotifier

news2024/11/18 16:30:50

theme: cyanosis

1. 数据的提供者与消费者

今天想要和大家好好聊聊 ChangeNotifier 这个东西,从名字上来看它由 change(改变)Notifier(通知器) 构成。打个比方:

有三个铁粉跟我说: "你发新文章的时候跟我说一声"。
之后我发布文章后,分享给了他们三个人。

很明显,这是一个 发布-订阅 模式,其中:

发布者是博主,是数据的提供者,也是通知事件的执行人。
订阅者是粉丝,是数据的消费者,需要依赖数据完成需求。

image.png

所以 ChangeNotifier 的角色很明显,他的职责是:在数据变化时,触发通知的动作。在整个过程中,发布者和订阅者是一对多的关系。所以对于通知器来说,需要维护一个列表通知订阅者。


在实际开发中,有很多类似的场景。比如不同界面中有若干个组件期望得到下载的进度数据,来完成自身的视觉表现。这里 下载进度 就是核心的数据,组件 相当于订阅者,需要感知数据的变化,完成展示需求。而发布者就是 下载进度数据的 提供者

| 案例演示 | 监听-通知关系 | | --- | --- | | 184.gif | 未标题-1.png |


2. 通过一个小案例了解 ChangeNotifier 的使用

下面,我们来完成上面下载进度的模拟案例,演示一下 ChangeNotifier 的使用。首先来分析一下:
在视图方面,主页面中有一个圆形的进度条 HomeProgressView 组件;点击头部栏左上角进入详情页,其中有一个矩形的进度条 DetailProgressView 组件。
在视图方面,主界面右下角按钮点击时,进度数据将会不断增加,直到 1 ;两个进度相关的组件,需要感知进度数值的变化,从而更新进度呈现。

这里只给出核心的代码,案例的完全代码已集成到 FlutterUnit,可以在仓库中自己查看 changenotifier01~

image.png


  • 数据方面处理

由于 ChangeNotifier 是一个混入类,无法直接实例化,使用时需要被混入来发挥效力。如下所示,这里定义 ProgressValueNotifier 类混入 ChangeNotifier。进度数据是一个 double 类型的浮点数,维护在 ProgressValueNotifier 中。

数据变化的时机就是 _value 改变时,在 set 方法中更新 _value 的值,并通过 notifyListeners 方法通知监听者数据已经变化,从而让订阅者们可以感知变化,并做出响应。

```dart ProgressValueNotifier progress = ProgressValueNotifier(); /// tag1

class ProgressValueNotifier with ChangeNotifier{

double _value = 0;

double get value =>_value;

String get valueStr => '${(value*100).toStringAsFixed(1)}%';

set value(double value){ _value = value.clamp(0, 1); notifyListeners(); } } ```

注 - tag1 : 这里为了方便访问 ProgressValueNotifier 对象,先将 progress 对象作为全局变量。后续文章会继续探讨对该对象的维护方式。


这里通过 Timer.periodic 开启一个 200 ms 的周期回调,触发 _updateProgress 方法。回调方法中,每次触发增加 1% 的进度,以此模拟下载进度数值的增加。

```dart ---->[page/home/home_page.dart]---- Timer? _timer;

void startTimer(){ if(timer!=null) return; if(progress.value==1.0){ progress.value=0; } timer = Timer.periodic(const Duration(milliseconds: 200),updateProgress); }

void _updateProgress(Timer timer) { if(progress.value>=1.0){ timer.cancel(); _timer = null; return; } progress.value += 0.01; } ```


  • 视图方面处理

详情页的进度和主页的进度视图上的处理是基本类似的,这里只拿 DetailProgressView 来说明一下。如下代码所示,主要需要处理三个部分:

[1]. 通过 ChangeNotifier 对象的 addListener 方法添加订阅关系。
[2]. 被加入回调的函数,将会在发布通知时触发。其中可以处理 更新逻辑
[3]. 在状态类销毁后,要及时移除监听。否则仍会在销毁后,触发更新,导致异常。

```dart class DetailProgressView extends StatefulWidget {

const DetailProgressView({super.key});

@override State createState() => _DetailProgressViewState(); }

class _DetailProgressViewState extends State {

@override void initState() { super.initState(); /// 1. 添加监听 - 订阅 progress.addListener(_update); }

@override Widget build(BuildContext context) { return Stack( alignment: Alignment.center, children:[ SizedBox( width: 200, height: 50, child: LinearProgressIndicator( value: progress.value, backgroundColor: Colors.grey, ), ), Text(progress.valueStr,style: TextStyle(color: Colors.white),) ], ); }

/// 2. 发布通知时,触发更新 void _update() { setState(() {}); }

@override void dispose() { /// 3. 组件销毁时,移除监听 progress.removeListener(_update); super.dispose(); } } ```

这样 ChangeNotifier 使用的一个小案例就介绍完了。大家可以自己在 FlutterUnit 中跑一跑,体验一下。下面来从源码的角度来分析一下 ChangeNotifier 的实现细节。


3. ChangeNotifier 源码分析

ChangeNotifier 类源码位于: flutter\lib\src\foundation\change_notifier.dart
首先,它是一个 mixin,说明该类型无法直接实例化对象 (混入类无构造函数)。其次,它实现了 Listenable 接口。

image.png

Listenable 是可监听对象的顶层接口,定义了 addListenerremoveListener 两个抽象方法。

```dart abstract class Listenable { const Listenable(); factory Listenable.merge(List listenables) = _MergingListenable;

void addListener(VoidCallback listener);

void removeListener(VoidCallback listener); } ```

ChangeNotifier 既然实现了 Listenable 接口,那自然核心任务是实现抽象方法的具体逻辑。下面是 ChangeNotifier 类的结构,其中核心是维护了 List<VoidCallback?> 类型的 _listeners 对象,作为一种订阅关系。

image.png


下面是添加监听的实现,调试中是详情页进入的时刻。在 addListener 处理完毕后,更新的回调函数将会被加入到 _listeners 回调列表中。

image.png

dart @override void addListener(VoidCallback listener) { if (_count == _listeners.length) { if (_count == 0) { _listeners = List<VoidCallback?>.filled(1, null); } else { final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(_listeners.length * 2, null); for (int i = 0; i < _count; i++) { newListeners[i] = _listeners[i]; } _listeners = newListeners; } } _listeners[_count++] = listener; }

ChangeNotifier#notifyListeners 方法中,将会变量 _count 次,触发 _listeners 列表对应索引的的回调函数。这就是通过函数对象,实现的添加监听和触发通知的一种机制。

image.png


4. ChangeNotifier 和 ValueNotifier

了解 ChangeNotifier 的使用之后,就非常好理解 ValueNotifier 。它支持一个泛型,继承自 ChangeNotifier ,实现 ValueListenable 接口。使用它可以监听某种特定类型的数据,从实现逻辑上来看就是在 set 时触发 notifyListeners 而言,也没有什么神奇的东西。

```dart class ValueNotifier extends ChangeNotifier implements ValueListenable { ValueNotifier(this._value) { /// 略... }

@override T get value => value; T _value; set value(T newValue) { if (value == newValue) { return; } _value = newValue; notifyListeners(); }

@override String toString() => '${describeIdentity(this)}($value)'; } ```


总的来说 ChangeNotifier 提供了一个 添加监听 - 发布通知 的机制,对于单类型的数据有 ValueNotifier 派生类方便使用。对于多种类型的数据,或者触发监听时机希望灵活控制时,可以自己混入 ChangeNotifier 来处理。可监听对象对于 Flutter 而言是一个非常重要的存在, ChangeNotifier 只是其中非常重要的一支。

我们平时使用的 TabControllerScrollControllerTextEditingControllerFocusNode 等;另外,滑动机制中,手势事件产生的数据和视口感知的滑动偏移量,通过 ScrollPosition 来连接。它们都是 ChangeNotifier 的派生类,足以见得 ChangeNotifier 在 Flutter 中的分量。

那本文就到这了,后续还会带来更多的精彩内容,下次再见~

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

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

相关文章

基于nodejs+vue网课学习平台

各功能简要描述如下: 1个人信息管理:包括对学生用户、老师和管理员的信息进行录入、修改&#xff0c;以及老师信息的审核等 2在库课程查询:用于学生用户查询相关课程的功能 3在库老师查询:用于学生用户查询相关老师教学的所有课程的功能。 4在库学校查询:用于学生用户查询相关学…

怎么把flac音频变为mp3?

怎么把flac音频变为mp3&#xff1f;FLAC音频格式在许多平台和应用程序中都得到支持和应用。FLAC音频格式被广泛支持和应用。许多平台、设备和应用程序都支持FLAC格式&#xff0c;如Windows、macOS和Linux操作系统、各种音乐播放器软件、智能手机和平板电脑、在线音乐平台和流媒…

Kaggle - LLM Science Exam(三):Wikipedia RAG

文章目录 一、赛事概述1.1 OpenBookQA Dataset1.2 比赛背景1.3 评估方法和代码要求1.4 比赛数据集1.5 优秀notebook 二、 [EDA, Data gathering] LLM-SE ~ Wiki STEM | 1k DS2.1 Data overview2.2 Data gathering 三、如何高效收集数据3.1 概述3.2 与训练数据关联的维基百科类别…

Q-learning如何与ABC等一些元启发式算法能够结合在一起?

1、出现的问题 Q-learning能和元启发式算法&#xff08;如ABC、PSO、GA、SSA等&#xff09;结合在一起&#xff0c;实现工作流调度问题&#xff1f; Q-learning和ABC (Artificial Bee Colony) 等元启发式算法可以结合在一起以解决特定类型的问题。Q-learning是一种强化学习算法…

http代理有什么好处,怎么通过http代理服务安全上网呢?

通过http代理上网是一种常见的网络代理方式。http代理是指通过代理服务器进行网络连接&#xff0c;以实现隐藏自己的真实IP地址、保护个人隐私等目的。下面我们将介绍通过http代理上网的好处以及如何使用http代理服务来安全上网。 一、通过http代理上网的好处 1. 保护个人隐私 …

LabVIEW建立生产者消费者

LabVIEW建立生产者消费者 生产者/消费者设计模式由并行循环组成&#xff0c;这些循环分为两类&#xff1a;生产者循环和消费者循环。生产者循环和消费者循环间的通信可以使用队列或通道连线来实现。 队列 LabVIEW内置的队列操作VI可在函数选板>>数据通信>>队列操…

Stable Diffusion绘画

正向提示词&#xff1a; 1girl, brown hair, multicolored hair, green eyes, maid, maid headdress, maid apron, cherry blossoms, sunbeam, wallpaper, 一个女孩&#xff0c;棕色头发&#xff0c;五彩头发&#xff0c;绿色眼睛&#xff0c;女仆&#xff0c;女仆头饰&#xf…

一个字符串模式匹配开源库

1 简介 theFuzz 是一个用于模糊字符串匹配和相似度计算的强大工具。它可以帮助我们在处理文本数据时进行模糊匹配和字符串比较&#xff0c;例如拼写纠正、字符串相似度计算和模糊搜索等。 2 基本原理 theFuzz 库的基本原理是使用不同的算法来计算字符串之间的相似度。其中最常用…

医疗机器人技术研究现状

医疗机器人技术是近年来快速发展的领域&#xff0c;它在医疗领域的应用已经取得了显著的成果。本文对医疗机器人技术的研究现状进行了综述&#xff0c;包括机器人手术、康复机器人、辅助诊断和治疗机器人等方面的研究进展。同时&#xff0c;本文还对医疗机器人技术的挑战和未来…

【python高级】设计模式、类工厂、对象工厂

一、说明 最近试着读Design pattern&#xff0c; 不过有些概念实在太抽象了&#xff0c; 整理一下自己所学抽象工厂的精神&#xff0c;就是要有abstract class&#xff08;not implement&#xff09;&#xff0c;而所有不同种类的对象&#xff0c;都是继承这个abstract class&a…

Ubuntu 环境配置 Minecraft 基岩版服务器

文章目录 Part 1 搭建服务器Part 2 配置服务器Part 3 管理服务器一、手动备份服务器二、手动恢复服务器备份三、升级服务器 Part 1 搭建服务器 更新软件包信息 sudo apt-get update sudo apt-get upgrade安装所需工具 sudo apt-get vim sudo apt-get install zip sudo apt-g…

数字孪生在制造运行管理(MOM)的七大应用场景

数字经济时代&#xff0c;数字孪生作为实现各行各业智能化、数字化的重要手段之一&#xff0c;受到了各方的广泛重视。随着各项关键使能技术的不断发展&#xff0c;数字孪生的应用价值有望得到进一步释放。这些关键使能技术包括建模、渲染、仿真、物联网、虚拟调试、可视化等&a…

《机器人学导论》——探究未来世界的奇妙之旅

你是否感到生活变得越来越便利、智能&#xff1f;是的&#xff0c;这些都与机器人技术的发展密不可分。而想要更深入地了解机器人技术&#xff0c;一本好的书籍是必不可少的。那么&#xff0c;今天作者要向大家推荐的就是这样一本优秀的机器人学大作——《机器人学导论》。 《…

Flutter笔记:发布一个多功能轮播组件 awesome_carousel

Flutter笔记 电商中文货币显示插件 Money Display 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/1338…

使用轮廓分数提升时间序列聚类的表现

我们将使用轮廓分数和一些距离指标来执行时间序列聚类实验&#xff0c;并且进行可视化 让我们看看下面的时间序列: 如果沿着y轴移动序列添加随机噪声&#xff0c;并随机化这些序列&#xff0c;那么它们几乎无法分辨&#xff0c;如下图所示-现在很难将时间序列列分组为簇: 上面…

SSM - Springboot - MyBatis-Plus 全栈体系(三十)

第七章 MyBatis-Plus MyBatis-Plus 高级用法&#xff1a;最优化持久层开发 一、MyBatis-Plus 快速入门 1. 简介 版本&#xff1a;3.5.3.1MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window) 的增强工具&#xff0c;在 MyBa…

vscode终端显示多个虚拟环境

问题&#xff1a;某次突然发现vscode前面出现多个虚拟环境&#xff0c;即&#xff08;.conda&#xff09;&#xff08;base&#xff09;&#xff0c;其中&#xff08;base&#xff09;是默认自动激活的&#xff0c;但是&#xff08;.conda&#xff09;不是&#xff0c;而且我退…

上海亚商投顾:沪指震荡调整跌 减肥药、华为概念股持续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 沪指上个交易日低开后震荡调整&#xff0c;深成指、创业板指盘中跌超1%&#xff0c;宁德时代一度跌超3%&#xff…

新增Node.js运行环境、新增系统缓存清理功能,1Panel开源面板v1.7.0发布

2023年10月16日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.7.0版本。 在这个版本中&#xff0c;1Panel新增Node.js运行环境&#xff1b;新增系统缓存清理功能&#xff1b;应用安装时支持选择远程数据库。此外&#xff0c;我们进行了40多项功能更新和…

GitHub验证的2FA

一、 起因&#xff1a; GitHub需要双重身份验证 (2FA) 是登录网站或应用时使用的额外保护层。启用 2FA 时&#xff0c;必须使用您的用户名和密码登录&#xff0c;并提供另一种只有您知道或可以访问的身份验证形式。 二、解决&#xff1a; 2.1 这里使用chrome的身份验证插件进…