Flutter状态管理篇之ChangeNotifier基础篇(一)

目录

前言

一、什么是ChangeNotifier

二、ChangeNotifier 的基本用法

三、结合Flutter UI 使用

四、结合 Provider 的高级用法

五、ChangeNotifier 的优势与注意事项

5.1 优势

5.2 注意事项

六、与 ValueNotifier 的比较

七、实际应用场景

八、总结


前言

        在 Flutter 开发中,ChangeNotifier 是一个强大的状态管理工具,位于 package:flutter/foundation.dart 中,广泛用于实现响应式 UI 更新。它通过提供监听者管理和通知机制,帮助开发者在状态变化时通知相关的 UI 组件。

        本文将详细介绍ChangeNotifier 的用法,包括其基本概念、实现方式、实际应用场景,以及与 Provider 等工具的结合方式。

一、什么是ChangeNotifier

        ChangeNotifier 是一个 mixin 类,实现了 Listenable 接口,用于管理状态并通知注册的监听者(VoidCallback 类型)。它允许开发者定义复杂的状态逻辑,并在状态变化时通过 notifyListeners 方法触发 UI 更新。ChangeNotifier 是 Flutter 响应式编程的核心,适合需要管理多字段状态或自定义通知逻辑的场景。

        ChangeNotifier的核心特征如下:

  1. 监听者管理:通过 addListenerremoveListener 管理监听者列表。
  2. 通知机制:通过 notifyListeners 通知所有监听者状态变化。
  3. 销毁支持:提供 dispose 方法清理资源,防止内存泄漏。
  4. 调试友好:内置调试支持(如 debugAssertNotDisposed),帮助发现错误。

二、ChangeNotifier 的基本用法

    ChangeNotifier通常通过mixin的方式混入自定义类,用于管理状态。

        以下是一个简单的计数器示例,展示其基本用法:

        图1.计时器实例

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),useMaterial3: true,),home: const MyHomePage(title: 'ChangeNotifier 用法示例'),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {final counter = Counter();void listener() {setState(() {}); // 触发 UI 更新}@overridevoid initState() {super.initState();counter.addListener(listener);}@overridevoid dispose() {counter.removeListener(listener);counter.dispose(); // 清理 Counter 资源super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),backgroundColor: Theme.of(context).colorScheme.primaryContainer,),body: Center(child: Padding(padding: const EdgeInsets.all(16.0),child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text('按钮点击次数',style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold,color: Theme.of(context).colorScheme.primary,),),const SizedBox(height: 20),Card(elevation: 8,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12),),color: Theme.of(context).colorScheme.surfaceVariant,child: Padding(padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 60),child: Text('${counter.count}',style: Theme.of(context).textTheme.displayMedium?.copyWith(fontWeight: FontWeight.bold,color: Theme.of(context).colorScheme.onSurfaceVariant,),),),),const SizedBox(height: 30),Row(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton.icon(onPressed: counter.decrement, // 直接调用方法,无需 setStateicon: const Icon(Icons.remove),label: const Text('减少'),style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),),),const SizedBox(width: 20),ElevatedButton.icon(onPressed: counter.increment, // 直接调用方法,无需 setStateicon: const Icon(Icons.add),label: const Text('增加'),style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),),),],),],),),),);}
}class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}void decrement() {if (_count > 0) { // 防止计数变为负数_count--;notifyListeners();}}}
  • 说明
    • Counter 类通过 with ChangeNotifier 混入通知功能。
    • _count 是私有状态,count getter 提供外部访问。
    • incrementdecrement 修改状态后调用 notifyListeners,触发监听者回调。

三、结合Flutter UI 使用

        在 Flutter 应用中,ChangeNotifier 通常与 UI 组件结合,通过监听状态变化自动更新界面。以下是一个完整的计数器应用,展示如何在 Flutter 中使用 ChangeNotifier

        下面是一个计时器的例子:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';void main() {runApp(ChangeNotifierProvider(create: (_) => Counter(),child: const MyApp(),),);
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.white),),home: const MyHomePage(title: 'ChangeNotifier用法'),);}
}class MyHomePage extends StatelessWidget {const MyHomePage({super.key, required this.title});final String title;@overrideWidget build(BuildContext context) {final counter = context.watch<Counter>();return Scaffold(appBar: AppBar(title: Text(title),),body: Center(child: Padding(padding: const EdgeInsets.all(16.0),child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text('按钮点击次数',style: Theme.of(context).textTheme.titleLarge,),const SizedBox(height: 20),Card(elevation: 8,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12),),color: Theme.of(context).colorScheme.surfaceVariant,child: Padding(padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 60),child: Text('${counter.count}',style: Theme.of(context).textTheme.displayMedium?.copyWith(color: Theme.of(context).colorScheme.primary,fontWeight: FontWeight.bold,),),),),const SizedBox(height: 30),Row(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton.icon(onPressed: counter.decrement,icon: const Icon(Icons.minimize),label: const Text('减少'),),const SizedBox(width: 20),ElevatedButton.icon(onPressed: counter.increment,icon: const Icon(Icons.add),label: const Text('增加'),),],),],),),),);}
}class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}void decrement() {if (_count > 0) { // 防止计数变为负数_count--;notifyListeners();}}
}
  • 依赖:需要添加 provider 包到 pubspec.yaml
    dependencies:provider: ^6.1.5
    
  • 说明
    • 使用 ChangeNotifierProvider 提供 Counter 实例,注入到 widget 树。
    • Consumer 监听 Counter 的变化,自动重建显示计数的 Text
    • 点击“+”或“-”按钮调用 incrementdecrement,触发 notifyListeners,更新 UI。

四、结合 Provider 的高级用法

    ChangeNotifier 常与 provider 包结合,用于更复杂的状态管理。以下是一个管理用户信息的示例,展示多字段状态管理。

        用户信息管理:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';class UserModel with ChangeNotifier {String _name = 'Anonymous';int _age = 0;String get name => _name;int get age => _age;void updateName(String newName) {_name = newName;notifyListeners();}void updateAge(int newAge) {_age = newAge;notifyListeners();}
}void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'UserModel 示例',theme: ThemeData(primarySwatch: Colors.green, useMaterial3: true),home: ChangeNotifierProvider(create: (_) => UserModel(),child: const UserPage(),),);}
}class UserPage extends StatelessWidget {const UserPage({super.key});@overrideWidget build(BuildContext context) {final userModel = context.read<UserModel>();return Scaffold(appBar: AppBar(title: const Text('用户信息')),body: Padding(padding: const EdgeInsets.all(16.0),child: Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Consumer<UserModel>(builder: (context, user, child) {return Text('姓名: ${user.name}, 年龄: ${user.age}',style: const TextStyle(fontSize: 20),);},),const SizedBox(height: 20),TextField(decoration: const InputDecoration(labelText: '输入姓名'),onChanged: (value) => userModel.updateName(value),),const SizedBox(height: 10),Row(children: [ElevatedButton(onPressed: () => userModel.updateAge(userModel.age + 1),child: const Text('年龄 +1'),),const SizedBox(width: 10),ElevatedButton(onPressed: () => userModel.updateAge(userModel.age - 1),child: const Text('年龄 -1'),),],),],),),);}
}
  • 说明
    • UserModel 管理 nameage 两个字段。
    • TextField 更新姓名,按钮更新年龄,每次变化调用 notifyListeners
    • Consumer 监听 UserModel 的变化,实时更新 UI。

五、ChangeNotifier 的优势与注意事项

5.1 优势

  1. 灵活性:支持管理复杂状态(多字段、自定义逻辑)。
  2. 响应式:通过 notifyListeners 触发 UI 更新,与 Provider 等工具无缝集成。
  3. 调试支持:内置 debugAssertNotDisposed,防止销毁后误用。
  4. 可扩展:通过 mixin 方式,易于扩展到自定义类。

5.2 注意事项

  1. 手动调用 notifyListeners:开发者需明确在状态变化时调用 notifyListeners,否则 UI 不会更新。
  2. 清理资源:在页面销毁时调用 dispose,避免内存泄漏。例如:
    class Counter with ChangeNotifier {@overridevoid dispose() {super.dispose(); // 必须调用父类的 dispose}
    }
    
  3. 避免递归通知:在 notifyListeners 期间移除监听者或调用 dispose 可能导致错误,需小心处理。
  4. 性能优化:避免频繁调用 notifyListeners,必要时检查状态是否真正变化。

六、与 ValueNotifier 的比较

    ChangeNotifierValueNotifier 都用于状态管理,但适用场景不同:

  • ChangeNotifier
    • 适合复杂状态管理(多字段、自定义通知逻辑)。
    • 需手动调用 notifyListeners
    • 更通用,但实现稍复杂。
  • ValueNotifier
    • 专注于单一值管理,自动在值变化时通知。
    • 更轻量,适合简单场景(如计数器、开关)。
    • 继承自 ChangeNotifier,API 更简单。

选择建议

  • 如果只需要管理单一值,使用 ValueNotifier(结合 ValueListenableBuilder)。
  • 如果需要管理多个字段或复杂逻辑,使用 ChangeNotifier(结合 Provider)。

七、实际应用场景

  1. 表单管理:管理多个输入字段的状态(如登录表单)。
  2. 复杂 UI 状态:如购物车、用户设置等需要多字段更新的场景。
  3. 与 Provider 结合:构建大型应用的全局状态管理。
  4. 动画控制:结合 ChangeNotifier 实现自定义动画状态。

八、总结

    ChangeNotifier 是 Flutter 中强大的状态管理工具,通过监听者机制实现响应式 UI 更新。它适合管理复杂状态,结合 ProviderConsumer 可以轻松集成到 Flutter 应用中。通过合理使用 notifyListenersdispose,开发者可以构建高效、可维护的状态管理逻辑。相比 ValueNotifierChangeNotifier 提供更大的灵活性,适合需要自定义通知逻辑的场景。希望本文能帮助你快速上手 ChangeNotifier,并在实际项目中灵活运用!

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

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

相关文章

react17更新哪些新特性

React 17 是一个“无新特性”的发布版本&#xff0c;它的主要目标是为未来的 React 版本打好基础&#xff0c;同时改善与旧版本共存和升级的体验。虽然没有引入新的开发者 API&#xff0c;但它在内部做了很多重要的改进。以下是 React 17 的核心更新内容和特性&#xff1a;&…

Unity 常见数据结构分析与实战展示 C#

Unity 常见数据结构分析与实战展示 提示&#xff1a;内容纯个人编写&#xff0c;欢迎评论点赞&#xff0c;来指正我。 文章目录Unity 常见数据结构分析与实战展示1. 引言2. Unity 数据结构概述3. 常见数据结构1. 数组&#xff08;Array&#xff09;2. 列表&#xff08;List&…

【Linux网络编程】应用层协议 - HTTP

目录 初识HTTP协议 认识URL HTTP协议的宏观格式 Socket封装 TcpServer HttpServer 整体设计 接收请求 web根目录与默认首页 发送应答 完善页面 HTTP常见Header HTTP状态码 HTTP请求方法 cookie与session Connection 抓包 初识HTTP协议 应用层协议一定是基于…

技术演进中的开发沉思-36 MFC系列: 对话框

MFC这个章节里&#xff0c;不能忽视的是对话框的开发。如果把 MFC 程序比作一栋办公楼&#xff0c;那对话框就是「会客室」—— 它是程序与用户面对面交流的地方&#xff1a;用户在这里输入数据&#xff0c;程序在这里展示信息&#xff0c;彼此的互动都从这个空间开始。今天围绕…

(李宏毅)deep learning(五)--learning rate

一&#xff0c;关于learning rate的讨论&#xff1a;&#xff08;1&#xff09;在梯度下降的过程中&#xff0c;当我们发现loss的值很小的时候&#xff0c;这时我们可能以为gradident已经到了local min0&#xff08;低谷&#xff09;,但是很多时候&#xff0c;loss很小并不是因…

pytorch:tensorboard和transforms学习

tensorboard:可视化数据 在anaconda安装&#xff1a; pip install tensorboard2.12.0最好使用这个版本 不然后面调用会报错 因为版本过高的原因 然后还碰到了安装的时候 安装到C盘去了 但是我用的虚拟环境是在E盘&#xff1a;此时去C盘把那些新安装的复制过来就好了 附录我C盘的…

常用的100个opencv函数

以下是OpenCV中最常用的100个函数及其作用与注意事项的全面整理&#xff0c;按功能模块分类&#xff0c;结合官方文档与工业实践优化排序。各函数均标注Python&#xff08;cv2&#xff09;和C&#xff08;cv::&#xff09;命名&#xff0c;重点参数以加粗突出&#xff1a; &…

【C++】红黑树,详解其规则与插入操作

各位大佬好&#xff0c;我是落羽&#xff01;一个坚持不断学习进步的大学生。 如果您觉得我的文章有所帮助&#xff0c;欢迎多多互三分享交流&#xff0c;一起学习进步&#xff01; 也欢迎关注我的blog主页: 落羽的落羽 一、红黑树的概念与规则 红黑树是一种更加特殊的平衡二…

Camera相机人脸识别系列专题分析之十七:人脸特征检测FFD算法之libhci_face_camera_api.so 296点位人脸识别检测流程详解

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: Camera相机人脸识别系列专题分析之十七:人脸特征检测FFD算法之libhci_face_camera_api.so 296点位人脸识别检测流程详解 目录 一、背景 二、:FFD算法libhci_face_camera_api.s…

PostgreSQL 16 Administration Cookbook 读书笔记:第7章 Database Administration

编写一个要么完全成功要么完全失败的脚本 事务&#xff08;transaction&#xff09;可以实现all or nothing。不过这里指的是psql的-和--single-transaction选项。可以实现transaction wrapper&#xff1a; 此选项只能与一个或多个 -c 和/或 -f 选项组合使用。它会导致 psql 在…

DeepSeekMath:突破开源语言模型在数学推理中的极限

温馨提示&#xff1a; 本篇文章已同步至"AI专题精讲" DeepSeekMath&#xff1a;突破开源语言模型在数学推理中的极限 摘要 数学推理由于其复杂且结构化的特性&#xff0c;对语言模型构成了重大挑战。本文介绍了 DeepSeekMath 7B&#xff0c;该模型在 DeepSeek-Code…

实体类序列化报错:Caused by: java.lang.NoSuchMethodException: com.xx.PoJo$Item.<init>()

原实体类代码EqualsAndHashCode(callSuper true) Data public class Pojo extends BaseBean {private static final long serialVersionUID -4291335073882689552L;ApiModelProperty("")private Integer id;......private List<Item> list;AllArgsConstructo…

基于单片机病床呼叫系统/床位呼叫系统

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 该系统是以单片机STM32F103为核心的基于无线网络的医院病房呼叫系统&#xff0c;分为从机和主机两…

[黑马头条]-登录实现思路

需求分析在黑马头条项目中&#xff0c;登录有两种方式&#xff1a;一种是用户输入账号密码后登录&#xff0c;这种方式登陆后的权限很大&#xff0c;可以查看&#xff0c;也可以进行其他操作&#xff1b;另一种方式就是用户点击不登录&#xff0c;以游客的身份进入系统&#xf…

了解.NET Core状态管理:优化技巧与常见问题解决方案

前言 欢迎关注dotnet研习社&#xff0c;今天我们聊聊“ .NET Core 中的状态管理”。 在Web应用程序中&#xff0c;管理和维持状态是一个非常重要的主题&#xff0c;尤其是在无状态的环境中&#xff0c;如 HTTP 协议和 RESTful API。对于基于 .NET Core 构建的应用程序&#xff…

504网关超时可能是哪些原因导致?

在网络访问中&#xff0c;504 网关超时&#xff08;Gateway Timeout&#xff09;如同一个突然亮起的警示灯&#xff0c;打断用户的浏览或操作流程。这个 HTTP 状态码意味着服务器作为网关或代理时&#xff0c;未能在规定时间内收到上游服务器的响应。引发504错误的核心因素有哪…

ComfyUI 常见报错问题解决方案合集(持续更新ing)

前言&#xff1a; 本文汇总了 5 大高频问题 及其解决方案&#xff0c;涵盖&#xff1a; HuggingFace 认证修复&#xff08;Token 申请 手动下载指南&#xff09; ComfyUI 版本更新&#xff08;完整命令 依赖管理&#xff09; 自启动配置&#xff08;Conda 环境 权限修复&…

完美解决Linux服务器tomcat开机自启动问题

经过多次测试终于彻底解决tomcat开机自启动的问题了 PID3ps aux | grep /home/server/shichuan/ | grep java | awk {print $2} if [ -n "$PID3" ]; then 这个判断pid的方式还是可能出现启动失败的情况 # tail -n 1 /home/server/shichuan/logs/catalina.out |grep…

kotlin部分常用特性总结

<h3>Kotlin中类和对象初始化</h3><ul> <li>添加open关键字代表可以被继承</li> <li>Any 是所有类的父类,类似Object,包含 equals() hashCode() toString()方法</li> <li>constructor 关键字代表构造函数, constructor关键字可…

PHP 就业核心技能速查手册

# PHP 就业核心技能速查手册 > 高效聚焦市场所需&#xff0c;快速提升竞争力 --- ## 一、语法基础&#xff08;必会&#xff01;&#xff09; php // 1. 变量与数据类型 $price 19.99; // 浮点型 $isStock true; // 布尔型 // 2. 流程控制 foreach ($…