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
英文地址,請注明出處:http://en.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 ($…