ChangeNotifier实现_Flutter在鸿蒙平台实现状态通知机制
作者付文龙红目香薰仓库地址https://gitcode.com/feng8403000/FlutterfromBeginnertoAdvancedForHarmonyOS.git联系邮箱372699828qq.com一、ChangeNotifier简介ChangeNotifier是Flutter自带的可监听对象继承自Listenable是Provider状态管理的核心数据模型。它提供了简单而强大的状态变化通知机制使得状态管理变得非常直观。1.1 ChangeNotifier的设计理念ChangeNotifier的设计理念非常简单当数据发生变化时通知所有监听者。这种设计模式被称为观察者模式Observer Pattern它将数据模型与UI组件解耦使得数据变化可以被多个组件同时监听。1.2 ChangeNotifier的优势特性说明轻量级仅100多行代码性能优秀简单易用API设计简洁易于理解和使用内置支持Flutter SDK自带无需额外依赖灵活扩展支持自定义监听逻辑二、ChangeNotifier核心特性2.1 继承ListenableChangeNotifier继承自Listenable接口提供了基本的监听管理能力addListener(VoidCallback listener)添加监听者removeListener(VoidCallback listener)移除监听者hasListeners检查是否有监听者2.2 notifyListeners()notifyListeners()是ChangeNotifier最核心的方法它会通知所有已注册的监听者数据已变化。当调用此方法时所有监听者的回调函数都会被执行。2.3 dispose()dispose()方法用于清理所有监听者防止内存泄漏。ChangeNotifierProvider会自动调用此方法无需手动管理。2.4 轻量级实现ChangeNotifier的实现非常轻量级主要包含以下几个部分一个列表存储所有监听者添加和移除监听者的方法通知所有监听者的方法清理监听者的方法classChangeNotifierimplementsListenable{ListVoidCallback??_listeners;overridevoidaddListener(VoidCallbacklistener){_listeners??[];_listeners!.add(listener);}overridevoidremoveListener(VoidCallbacklistener){_listeners?.remove(listener);}protectedvoidnotifyListeners(){if(_listeners!null){finallocalListenersListVoidCallback?.from(_listeners!);for(finallistenerinlocalListeners){listener?.call();}}}overridevoiddispose(){_listenersnull;}}三、创建ChangeNotifier数据模型3.1 基本结构创建ChangeNotifier数据模型的基本结构如下classCartProviderextendsChangeNotifier{// 私有字段存储状态数据ListCartItem_items[];double _discount0.0;// 公开getter暴露数据ListCartItemgetitems_items;doublegetdiscount_discount;intgetitemCount_items.length;// 计算属性计算派生数据doublegettotalPrice{return_items.fold(0.0,(sum,item)sumitem.price*item.quantity);}doublegetdiscountedPrice{returntotalPrice*(1-_discount);}// 操作方法修改数据并通知监听者voidaddItem(CartItemitem){_items.add(item);notifyListeners();}voidremoveItem(Stringid){_items.removeWhere((item)item.idid);notifyListeners();}voidupdateQuantity(Stringid,int quantity){finalindex_items.indexWhere((item)item.idid);if(index!-1){_items[index].quantityquantity;notifyListeners();}}voidsetDiscount(double discount){_discountdiscount.clamp(0.0,1.0);notifyListeners();}voidclearCart(){_items.clear();_discount0.0;notifyListeners();}}3.2 CartItem数据模型classCartItem{finalStringid;finalStringname;finaldouble price;int quantity;CartItem({requiredthis.id,requiredthis.name,requiredthis.price,this.quantity1,});CartItemcopyWith({String?id,String?name,double?price,int?quantity,}){returnCartItem(id:id??this.id,name:name??this.name,price:price??this.price,quantity:quantity??this.quantity,);}}四、ChangeNotifier使用要点4.1 私有字段与公开getterChangeNotifier的数据模型应该遵循以下原则字段通常设为私有以_开头通过getter暴露数据禁止外部直接修改这样可以确保数据修改只能通过方法进行便于管理和追踪classCartProviderextendsChangeNotifier{ListCartItem_items[];// 私有字段ListCartItemgetitems_items;// 公开getter}4.2 notifyListeners()的时机调用notifyListeners()的时机非常重要在所有数据修改完成后调用不要在循环中频繁调用批量修改时合并为一次调用voidaddMultipleItems(ListCartItemitems){_items.addAll(items);notifyListeners();// 在所有修改完成后调用}4.3 计算属性使用getter计算派生数据避免重复计算doublegettotalPrice{return_items.fold(0.0,(sum,item)sumitem.price*item.quantity);}doublegetdiscountedPrice{returntotalPrice*(1-_discount);}4.4 生命周期管理ChangeNotifierProvider会自动管理ChangeNotifier的生命周期创建Provider时自动创建ChangeNotifier实例Widget树销毁时自动调用dispose()方法ChangeNotifierProvider(create:(context)CartProvider(),// 创建实例child:constMyApp(),)五、批量更新优化5.1 优化前多次调用notifyListeners()voidaddMultipleItems(ListCartItemitems){for(variteminitems){_items.add(item);notifyListeners();// 不推荐每次循环都调用}}这种方式会导致多次UI重建影响性能。5.2 优化后批量修改后只调用一次voidaddMultipleItems(ListCartItemitems){_items.addAll(items);notifyListeners();// 推荐批量修改后只调用一次}这种方式可以减少UI重建次数提升性能。5.3 复杂操作的批量更新voidupdateCart(ListCartItemnewItems,double discount){_items.clear();_items.addAll(newItems);_discountdiscount;notifyListeners();// 一次调用多次修改}六、ChangeNotifier与其他方案对比6.1 ChangeNotifier vs StateNotifier方案复杂度学习曲线适用场景ChangeNotifier低低中小项目状态管理StateNotifier中中需要不可变状态的项目StateNotifier是Riverpod生态中的状态管理方案它强制使用不可变状态适合需要严格状态控制的项目。6.2 ChangeNotifier vs Bloc方案复杂度学习曲线适用场景ChangeNotifier低低中小项目状态管理Bloc高高复杂业务逻辑Bloc是一个基于流的状态管理方案适合处理复杂的业务逻辑和异步操作。七、高级用法7.1 自定义监听逻辑ChangeNotifier允许自定义监听逻辑例如只在特定条件下通知监听者classCartProviderextendsChangeNotifier{ListCartItem_items[];bool _shouldNotifytrue;voidaddItem(CartItemitem){_items.add(item);if(_shouldNotify){notifyListeners();}}voidpauseNotifications(){_shouldNotifyfalse;}voidresumeNotifications(){_shouldNotifytrue;notifyListeners();// 通知暂停期间的所有变化}}7.2 组合多个ChangeNotifier可以组合多个ChangeNotifier实现状态的分层管理classAppStateextendsChangeNotifier{finalCartProvidercartCartProvider();finalUserProvideruserUserProvider();finalThemeProviderthemeThemeProvider();voidupdateAll(){cart.notifyListeners();user.notifyListeners();theme.notifyListeners();notifyListeners();}}7.3 使用ValueNotifier管理单一值对于单一值的状态管理可以使用ValueNotifierfinalcounterValueNotifierint(0);// 使用ValueListenableBuilder监听变化ValueListenableBuilderint(valueListenable:counter,builder:(context,value,child){returnText(Count:$value);},);八、实际应用场景8.1 购物车状态管理classCartProviderextendsChangeNotifier{ListCartItem_items[];double _discount0.0;ListCartItemgetitems_items;doublegetdiscount_discount;intgettotalQuantity{return_items.fold(0,(sum,item)sumitem.quantity);}doublegettotalPrice{return_items.fold(0.0,(sum,item)sumitem.price*item.quantity);}doublegetdiscountedPrice{returntotalPrice*(1-_discount);}voidaddItem(CartItemitem){finalexistingItem_items.firstWhere((i)i.iditem.id,orElse:()item,);if(existingItemitem){_items.add(item);}else{existingItem.quantityitem.quantity;}notifyListeners();}voidremoveItem(Stringid){_items.removeWhere((item)item.idid);notifyListeners();}voidupdateQuantity(Stringid,int quantity){finalindex_items.indexWhere((item)item.idid);if(index!-1){if(quantity0){_items.removeAt(index);}else{_items[index].quantityquantity;}notifyListeners();}}voidsetDiscount(double discount){_discountdiscount.clamp(0.0,1.0);notifyListeners();}voidclearCart(){_items.clear();_discount0.0;notifyListeners();}}8.2 用户状态管理classUserProviderextendsChangeNotifier{User?_user;bool _isLoadingfalse;String?_error;User?getuser_user;boolgetisLoading_isLoading;String?geterror_error;FuturevoidfetchUser(StringuserId)async{_isLoadingtrue;_errornull;notifyListeners();try{_userawaitUserApi.fetchUser(userId);}catch(e){_errore.toString();}finally{_isLoadingfalse;notifyListeners();}}voidlogout(){_usernull;notifyListeners();}}8.3 主题状态管理classThemeProviderextendsChangeNotifier{bool _isDarkfalse;ThemeModegetthemeMode_isDark?ThemeMode.dark:ThemeMode.light;voidtoggleTheme(){_isDark!_isDark;notifyListeners();}ThemeDatagetthemeData{return_isDark?ThemeData.dark().copyWith(primaryColor:Colors.blue):ThemeData.light().copyWith(primaryColor:Colors.blue);}}九、常见问题与解决方案9.1 问题忘记调用notifyListeners()现象修改了数据但UI没有更新。解决方案确保在所有数据修改完成后调用notifyListeners()。voidaddItem(CartItemitem){_items.add(item);notifyListeners();// 不要忘记调用}9.2 问题在循环中频繁调用notifyListeners()现象性能问题UI卡顿。解决方案合并多次调用为一次。voidaddMultipleItems(ListCartItemitems){_items.addAll(items);notifyListeners();// 合并调用}9.3 问题内存泄漏现象应用内存占用持续增加。解决方案使用ChangeNotifierProvider自动管理生命周期不要手动创建ChangeNotifier实例。// 推荐ChangeNotifierProvider(create:(context)CartProvider(),child:constMyApp(),)// 不推荐finalcartCartProvider();// 手动创建容易忘记dispose9.4 问题并发修改导致状态不一致现象多个异步操作同时修改状态导致数据不一致。解决方案使用互斥锁或队列管理异步操作。classCartProviderextendsChangeNotifier{bool _isUpdatingfalse;finalListvoidFunction()_pendingUpdates[];FuturevoidupdateCart(ListCartItemnewItems)async{if(_isUpdating){_pendingUpdates.add(()updateCart(newItems));return;}_isUpdatingtrue;try{_itemsnewItems;notifyListeners();}finally{_isUpdatingfalse;if(_pendingUpdates.isNotEmpty){finalupdate_pendingUpdates.removeAt(0);update();}}}}十、总结ChangeNotifier是Provider状态管理的核心数据模型它提供了简单而强大的状态变化通知机制。通过合理使用ChangeNotifier可以实现高效的状态管理。在使用ChangeNotifier时需要注意以下几点使用私有字段和公开getter禁止外部直接修改数据在所有数据修改完成后调用notifyListeners()批量修改时合并多次调用为一次使用ChangeNotifierProvider自动管理生命周期对于复杂场景可以考虑使用StateNotifier或Bloc掌握ChangeNotifier的使用方法是学习Provider状态管理的基础。

相关新闻

【Rust自学】13.4. 闭包 Pt.4:使用闭包捕获环境

【Rust自学】13.4. 闭包 Pt.4:使用闭包捕获环境

13.4 闭包 Pt.4:使用闭包捕获环境 13.4.0. 写在正文之前 Rust语言在设计过程中受到了很多语言的启发,而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等…

2026/7/23 18:57:44 阅读更多 →
【无标题】简短的介绍

【无标题】简短的介绍

自我介绍大学生小伟目标:本人无太大志向只想能有一个好的工作安稳过日目前看来我只能努力学习加油

2026/7/23 18:57:44 阅读更多 →
【Rust自学】13.5. 迭代器 Pt.1:迭代器的定义、iterator trait和next方法

【Rust自学】13.5. 迭代器 Pt.1:迭代器的定义、iterator trait和next方法

13.5 迭代器 Pt.1:迭代器的定义、iterator trait和next方法 13.5.0. 写在正文之前 Rust语言在设计过程中受到了很多语言的启发,而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分…

2026/7/23 18:57:44 阅读更多 →

最新新闻

AI日报编制3.0:多模态数据整合与智能分析实践

AI日报编制3.0:多模态数据整合与智能分析实践

1. 项目概述"AI日报 - 2026年04月08日"这个标题看似简单,实际上蕴含着一个专业科技媒体或研究机构的核心工作流程。作为长期跟踪AI领域发展的从业者,我深知这类日报的编制远不止是信息汇总那么简单。它需要建立在对行业动态的持续监测、技术趋…

2026/7/23 19:10:49 阅读更多 →
科技型中小企业如何申请政府补贴?申报指南

科技型中小企业如何申请政府补贴?申报指南

一、为什么要看这份榜单 科技型中小企业在发展过程中,政府补贴是重要的资金来源,但申请流程复杂、政策更新快、材料准备繁琐,许多企业因缺乏专业指导而错失良机。本榜单旨在帮助科技型中小企业选择合适的科技咨询服务机构,提高政府…

2026/7/23 19:10:49 阅读更多 →
0基础录音转换成文字在线教程,看完就能直接上手

0基础录音转换成文字在线教程,看完就能直接上手

没有基础想做录音转换成文字在线操作,照着来就能直接上手,不用提前学什么复杂操作。这是我踩了三个学期坑整理出来的避坑方案,专门给需要转课堂录音、论文调研访谈录音的零基础学生准备的。如果你需要100%逐字高精度转写涉密或正式出版级的录…

2026/7/23 19:10:49 阅读更多 →
汽车电子面试问题记录

汽车电子面试问题记录

1.CAN如何实现仲裁 2.错误帧的类型 3.C语言基础 结构体,指针,指针函数,函数指针的区别 4.使用硬件IIC会有什么问题,硬件IIIC和软件IIC对比 5.CAN OTA的流程 6.静态变量如何实现跨文件使用:通过函数间接调用。 h…

2026/7/23 19:10:49 阅读更多 →
私域邦网络卖二返本模式系统开发

私域邦网络卖二返本模式系统开发

卖二返本模式系统开发指南编辑𝑽:araolin(土土哥)卖二返本模式是一种营销策略,消费者购买一定数量的商品后,系统自动返还部分或全部本金。以下是开发此类系统的关键步骤:核心功能设计用户管理模…

2026/7/23 19:10:49 阅读更多 →
ollama+open webui使用轻量级DeepSeek模型

ollama+open webui使用轻量级DeepSeek模型

ollama安装 在官网选择版本下载ollama,这里以Windows为例,我将OllamaSetup.exe下载到文件夹D:\program\ollama Download Ollama on macOS 如果是想直接安装可以双击OllamaSetup.exe,点击Install,它直接默认安装C盘。 下面是安…

2026/7/23 19:09:48 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击: https://codechina.net 第一章:AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后,我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 17:49:47 阅读更多 →

月新闻