当前位置: 首页 > article >正文

Flutter Provider 状态管理完全指南

Flutter Provider 状态管理完全指南引言Provider 是 Flutter 中最流行的状态管理方案之一它基于 InheritedWidget 实现提供了简单而强大的状态管理方式。本文将深入探讨 Provider 的各种用法和高级技巧。基础概念回顾Provider 类型Provider- 最基础的提供者ChangeNotifierProvider- 用于 ChangeNotifierFutureProvider- 用于异步操作StreamProvider- 用于流数据MultiProvider- 多个提供者组合Consumer 模式ConsumerMyModel( builder: (context, model, child) { return Text(model.value.toString()); }, )高级技巧一ChangeNotifier创建模型class Counter extends ChangeNotifier { int _count 0; int get count _count; void increment() { _count; notifyListeners(); } void decrement() { _count--; notifyListeners(); } void reset() { _count 0; notifyListeners(); } }注册 Providervoid main() { runApp( ChangeNotifierProvider( create: (context) Counter(), child: MyApp(), ), ); }消费数据class CounterWidget extends StatelessWidget { override Widget build(BuildContext context) { return ConsumerCounter( builder: (context, counter, child) { return Column( children: [ Text(Count: ${counter.count}), ElevatedButton( onPressed: counter.increment, child: Text(Increment), ), ], ); }, ); } }高级技巧二Selector选择特定数据SelectorCounter, int( selector: (context, counter) counter.count, builder: (context, count, child) { return Text(Count: $count); }, )多个选择器Selector2Counter, ThemeMode, String( selector: (context, counter, theme) { return ${counter.count} - ${theme.name}; }, builder: (context, value, child) { return Text(value); }, )高级技巧三FutureProvider基本用法FutureProviderString( create: (context) async { await Future.delayed(Duration(seconds: 2)); return Loaded data; }, initialData: Loading..., builder: (context, child) { final data context.watchString(); return Text(data); }, )处理错误FutureProviderString( create: (context) async { throw Exception(Error); }, catchError: (context, error) Error: $error, builder: (context, child) { final data context.watchString(); return Text(data); }, )高级技巧四StreamProvider基本用法StreamProviderint( create: (context) Stream.periodic( Duration(seconds: 1), (i) i, ), initialData: 0, builder: (context, child) { final count context.watchint(); return Text(Count: $count); }, )处理流错误StreamProviderint( create: (context) errorStream, initialData: 0, catchError: (context, error) -1, builder: (context, child) { final count context.watchint(); return count -1 ? Text(Error occurred) : Text(Count: $count); }, )高级技巧五MultiProvider组合多个 ProviderMultiProvider( providers: [ ChangeNotifierProvider(create: (context) Counter()), ChangeNotifierProvider(create: (context) ThemeManager()), FutureProvider(create: (context) fetchUser()), ], child: MyApp(), )实战案例用户认证状态管理AuthProviderclass AuthProvider extends ChangeNotifier { User? _user; bool _isLoading false; String? _error; User? get user _user; bool get isLoading _isLoading; String? get error _error; bool get isAuthenticated _user ! null; Futurevoid login(String email, String password) async { _isLoading true; _error null; notifyListeners(); try { final user await authService.login(email, password); _user user; } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } Futurevoid logout() async { await authService.logout(); _user null; notifyListeners(); } }登录页面class LoginPage extends StatelessWidget { final _emailController TextEditingController(); final _passwordController TextEditingController(); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(Login)), body: ConsumerAuthProvider( builder: (context, auth, child) { if (auth.isLoading) { return Center(child: CircularProgressIndicator()); } return Padding( padding: EdgeInsets.all(16), child: Column( children: [ if (auth.error ! null) Text(auth.error!, style: TextStyle(color: Colors.red)), TextField( controller: _emailController, decoration: InputDecoration(labelText: Email), keyboardType: TextInputType.emailAddress, ), TextField( controller: _passwordController, decoration: InputDecoration(labelText: Password), obscureText: true, ), ElevatedButton( onPressed: () { auth.login( _emailController.text, _passwordController.text, ); }, child: Text(Login), ), ], ), ); }, ), ); } }实战案例主题切换ThemeProviderclass ThemeProvider extends ChangeNotifier { ThemeMode _themeMode ThemeMode.system; ThemeMode get themeMode _themeMode; void toggleTheme() { switch (_themeMode) { case ThemeMode.light: _themeMode ThemeMode.dark; break; case ThemeMode.dark: _themeMode ThemeMode.system; break; case ThemeMode.system: _themeMode ThemeMode.light; break; } notifyListeners(); } ThemeData get themeData { switch (_themeMode) { case ThemeMode.light: return ThemeData.light(); case ThemeMode.dark: return ThemeData.dark(); case ThemeMode.system: default: return MediaQuery.platformBrightnessOf(context) Brightness.dark ? ThemeData.dark() : ThemeData.light(); } } }主题切换按钮class ThemeToggleButton extends StatelessWidget { override Widget build(BuildContext context) { return ConsumerThemeProvider( builder: (context, themeProvider, child) { return IconButton( icon: Icon( themeProvider.themeMode ThemeMode.dark ? Icons.light_mode : Icons.dark_mode, ), onPressed: themeProvider.toggleTheme, ); }, ); } }实战案例购物车管理CartProviderclass CartProvider extends ChangeNotifier { final ListCartItem _items []; ListCartItem get items _items; double get total { return _items.fold(0, (sum, item) sum item.price * item.quantity); } int get itemCount { return _items.fold(0, (sum, item) sum item.quantity); } void addItem(Product product) { final existingItem _items.firstWhere( (item) item.product.id product.id, orElse: () CartItem(product: product, quantity: 0), ); if (existingItem.quantity 0) { _items.add(CartItem(product: product, quantity: 1)); } else { existingItem.quantity; } notifyListeners(); } void removeItem(Product product) { final index _items.indexWhere((item) item.product.id product.id); if (index ! -1) { if (_items[index].quantity 1) { _items[index].quantity--; } else { _items.removeAt(index); } notifyListeners(); } } void clearCart() { _items.clear(); notifyListeners(); } } class CartItem { final Product product; int quantity; CartItem({required this.product, required this.quantity}); double get price product.price; }购物车图标class CartIcon extends StatelessWidget { override Widget build(BuildContext context) { return ConsumerCartProvider( builder: (context, cart, child) { return Stack( children: [ IconButton( icon: Icon(Icons.shopping_cart), onPressed: () Navigator.pushNamed(context, /cart), ), if (cart.itemCount 0) Positioned( right: 0, top: 0, child: Container( padding: EdgeInsets.all(2), decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(10), ), constraints: BoxConstraints( minWidth: 20, minHeight: 20, ), child: Text( cart.itemCount.toString(), style: TextStyle( fontSize: 12, color: Colors.white, ), textAlign: TextAlign.center, ), ), ), ], ); }, ); } }常见问题与解决方案Q1如何获取 Provider 但不重建A使用listen: falsefinal counter Provider.ofCounter(context, listen: false);Q2如何在 initState 中使用 ProviderA使用listen: falseoverride void initState() { super.initState(); final counter Provider.ofCounter(context, listen: false); counter.increment(); }Q3如何传递参数给 ProviderA使用create函数ChangeNotifierProvider( create: (context) MyModel(initialValue: 42), child: MyWidget(), )最佳实践1. 使用 Consumer 而非 Provider.of// 推荐 ConsumerCounter( builder: (context, counter, child) { return Text(${counter.count}); }, ) // 不推荐 Provider.ofCounter(context).count2. 拆分 Provider// 推荐按功能拆分 MultiProvider( providers: [ ChangeNotifierProvider(create: (context) AuthProvider()), ChangeNotifierProvider(create: (context) CartProvider()), ChangeNotifierProvider(create: (context) ThemeProvider()), ], ) // 不推荐单一大 Provider ChangeNotifierProvider(create: (context) AppState())3. 使用 Selector 优化性能// 推荐只监听需要的数据 SelectorCounter, int( selector: (context, counter) counter.count, builder: (context, count, child) { return Text(Count: $count); }, ) // 不推荐监听整个对象 ConsumerCounter( builder: (context, counter, child) { return Text(Count: ${counter.count}); }, )总结Provider 是 Flutter 中简单而强大的状态管理方案。通过本文的学习你应该能够使用 ChangeNotifier 创建可观察模型使用各种 Provider 类型使用 Consumer 和 Selector 消费数据实现复杂的状态管理场景优化性能掌握这些技巧能够帮助你构建更加健壮和可维护的 Flutter 应用。

相关文章:

Flutter Provider 状态管理完全指南

Flutter Provider 状态管理完全指南 引言 Provider 是 Flutter 中最流行的状态管理方案之一,它基于 InheritedWidget 实现,提供了简单而强大的状态管理方式。本文将深入探讨 Provider 的各种用法和高级技巧。 基础概念回顾 Provider 类型 Provider - 最基…...

CSS 混合模式完全指南

CSS 混合模式完全指南 引言 CSS 混合模式(Blend Modes)是一种强大的视觉效果工具,它允许你控制多个元素或图层如何混合在一起。本文将深入探讨各种混合模式的用法和高级技巧。 混合模式类型 基础混合模式 模式效果描述normal默认模式&#xf…...

C++ 知识点22 函数模板

C 函数模板一、为什么要有函数模板?先看痛点:你要写两个交换函数,int 版、double 版:// int 交换 void swapInt(int &a, int &b) {int t a; a b; b t; } // double 交换 void swapDouble(double &a, double &b…...

Flutter 自定义动画完全指南

Flutter 自定义动画完全指南 引言 动画是现代移动应用的重要组成部分,它能够提升用户体验,使界面更加生动。Flutter 提供了强大的动画系统,本文将深入探讨如何创建自定义动画效果。 动画基础回顾 动画类型 补间动画 (Tween Animation) - 最常…...

cpdown:精准下载Git仓库文件,告别克隆整个项目的低效操作

1. 项目概述与核心价值最近在整理本地开发环境,发现一个高频痛点:从各种代码托管平台(比如 GitHub、GitLab、Gitee)下载单个文件或特定目录时,总是特别麻烦。要么得克隆整个仓库,动辄几百兆,浪费…...

基于浏览器自动化的高级爬虫框架autoclaw实战指南

1. 项目概述与核心价值最近在折腾自动化脚本时,发现了一个挺有意思的GitHub项目,叫jmoraispk/autoclaw。乍一看名字,可能会联想到“自动爪子”或者“爬虫”,实际上,它也确实是一个专注于自动化网页交互和数据抓取的工具…...

别再为Modbus RTU超时头疼了!STM32CubeMX+FreeModbus从站移植,搞定串口与定时器配置的黄金法则

STM32CubeMXFreeModbus从站移植实战:破解RTU超时难题的工程化思维 当你在深夜调试Modbus RTU从站设备,串口调试助手反复弹出"Timeout"错误提示时,那种挫败感每个嵌入式工程师都深有体会。超时问题就像幽灵般难以捉摸——代码编译通…...

别再傻傻分不清!Ansys Workbench三大建模界面(SCDM/DM/Mechanical)保姆级对比与选用指南

Ansys Workbench三大建模界面深度解析:如何根据项目需求选择最佳工具 在工程仿真领域,Ansys Workbench作为行业标杆软件套件,其内置的三大建模界面——SpaceClaim(SCDM)、DesignModeler(DM)和Me…...

AD7606模块的20kHz高速采样怎么玩?深入对比带缓存与不带缓存的两种采集模式

AD7606模块20kHz高速采样的工程实践:带缓存与无缓存模式深度解析 在工业自动化、电力监测和振动分析等领域,多通道高速数据采集系统常面临一个关键抉择:如何在有限的处理器资源下实现最优的采样性能?AD7606作为一款经典的八通道16…...

别再只盯着原理图了!用Python+OpenCV动手模拟激光三角测距(斜射/直射对比)

用PythonOpenCV模拟激光三角测距:斜射与直射的实战对比 激光三角测距技术听起来高大上,但真正理解它的精髓往往需要跳出公式推导的泥潭。作为一名长期在工业检测领域摸爬滚打的技术人员,我发现用代码模拟物理过程是最有效的学习方式。本文将…...

从原理到实战:使用Kali Linux进行WiFi安全渗透测试

1. WiFi安全渗透测试基础 很多人可能觉得WiFi密码破解是个神秘的黑客技术,其实它只是网络安全领域中一个基础的安全测试手段。作为一名安全研究员,我经常需要在获得授权的情况下,对客户的无线网络进行安全评估。Kali Linux作为专业的渗透测试…...

别再到处找激活码了!手把手教你用vlmcsd在Windows上自建KMS服务器(附各版本密钥)

企业级Windows批量激活解决方案:安全高效的本地KMS部署指南 在数字化办公环境中,批量激活Windows操作系统一直是IT管理员面临的常见挑战。传统单机激活方式效率低下,而依赖外部KMS服务器又存在连接不稳定、隐私泄露等潜在风险。本文将深入探讨…...

终极ROFL播放器指南:如何免费快速解锁英雄联盟回放文件分析

终极ROFL播放器指南:如何免费快速解锁英雄联盟回放文件分析 【免费下载链接】ROFL-Player (No longer supported) One stop shop utility for viewing League of Legends replays! 项目地址: https://gitcode.com/gh_mirrors/ro/ROFL-Player 还在为无法查看英…...

从仿真到论文图表:手把手教你用FDTD参数扫描和Matlab处理WO3薄膜光学数据

从仿真到论文图表:FDTD参数扫描与Matlab数据可视化全流程解析 在光电材料研究中,WO₃薄膜因其优异的电致变色特性备受关注。当我们需要系统研究薄膜厚度对光学性能的影响时,FDTD Solutions的参数扫描功能配合Matlab的数据处理能力&#xff0c…...

鸿蒙数据持久化三板斧:Preferences、RDB、分布式数据一文搞定,告别数据丢失

📖 鸿蒙NEXT开发实战系列 | 第21篇 | 数据篇 🎯 适合人群:有鸿蒙基础的开发者 ⏰ 阅读时间:约15分钟 | 💻 开发环境:DevEco Studio 5.0 ⬅️ 上一篇:20-网络篇-网络请求与数据加载 ➡️ 下一篇&…...

STM32CubeMX LL库配置外部中断,从按键消抖到中断嵌套的实战避坑指南

STM32CubeMX LL库外部中断深度优化:从硬件消抖到中断嵌套的工程实践 当你的嵌入式系统需要实时响应外部事件时,外部中断(EXTI)往往是最高效的选择。但在实际项目中,简单配置EXTI只是开始——按键抖动导致的误触发、中断优先级冲突引发的死锁、…...

SAP资产会计进阶:深入理解AS91、AB01与ABLDT在期初数据处理中的角色与联动

SAP资产会计核心事务代码解析:AS91、AB01与ABLDT的协同逻辑与实战应用 在SAP S4 HANA资产模块的实施与运维中,期初数据处理往往是项目成败的关键节点。不同于日常资产操作,期初数据迁移涉及历史价值追溯、折旧逻辑重建以及多系统数据对齐等复…...

别再死记硬背了!用Python+Graphviz把离散数学的图论和关系画出来(附代码)

用PythonGraphviz将离散数学中的抽象概念可视化 离散数学是计算机科学的基础课程之一,但其中的图论、二元关系等概念往往因为高度抽象而让学习者感到困惑。传统的死记硬背方式不仅效率低下,也难以真正理解这些概念的本质。本文将介绍如何利用Python的net…...

从配置字到实际运动:手把手教你用EtherCAT调试伺服电机的控制模式(以倍福TwinCAT3为例)

从配置字到实际运动:手把手教你用EtherCAT调试伺服电机的控制模式(以倍福TwinCAT3为例) 在工业自动化现场,伺服电机的精准控制往往决定着整条产线的运行效率。当面对一台全新的伺服驱动器时,如何快速完成从参数配置到实…...

从日偏食图像处理开始:手把手在VS2019里跑通你的第一个OpenCV 4.3程序

从日偏食图像处理开始:手把手在VS2019里跑通你的第一个OpenCV 4.3程序 当那张日偏食照片第一次在屏幕上成功显示时,仿佛打开了计算机视觉的大门。本文将带你从零开始,用VS2019和OpenCV 4.3实现这个充满仪式感的"Hello World"——不…...

从CMake报错到编译成功:一站式解决absl依赖配置难题

1. 当CMake突然报错:absl依赖缺失的紧急处理 第一次看到这个报错时,我正赶着在截止日期前完成gRPC服务的部署。控制台突然弹出的红色错误让我心头一紧:"Could not find a package configuration file provided by absl"。这种依赖缺…...

【PyTorch实战】从零构建Prototypical Network:小样本图像分类的度量学习核心

1. 小样本学习与Prototypical Network基础 当你第一次听说"小样本学习"时,可能会觉得这是个遥不可及的高深概念。其实它的核心思想很简单:就像人类能通过少量例子快速学习新事物一样,让AI模型也具备这种能力。想象一下,…...

技术Leader的困境:为什么你越努力,团队越依赖你?

在软件测试领域,我们比任何角色都更懂“依赖”这个词。测试环境依赖稳定、测试数据依赖真实、测试用例依赖需求文档。但有一种依赖,最致命却也最容易被忽视——团队对你的依赖。很多从一线测试骨干晋升为测试Leader的人,都会陷入一个怪圈&…...

工程实践:AI 编程从提示词走向流水线,才需要 API 中转站

这类内容的核心判断应该换一下:用户不是先想买 API,中间才想到 Claude / Codex;很多时候正相反,是先想用 Claude / Codex 提升开发效率,才开始寻找稳定、可接入、可支付、可迁移的 API 入口。目标用户画像想把需求分析…...

HBase集群启动后秒退?手把手教你排查ZooKeeper路径配置与htrace-core缺失问题

HBase集群启动后秒退?深度排查ZooKeeper路径与依赖缺失问题 当你在深夜部署HBase集群时,看到服务启动后几秒钟内突然消失,那种感觉就像在黑暗中摸索开关。这不是简单的配置错误,而是系统在向你发出求救信号。让我们像侦探一样&…...

机器学习之随机森林详解

摘要随机森林(Random Forest)是一种基于Bagging集成学习思想的 ensemble method,通过构建多棵决策树并综合其预测结果来实现分类和回归任务。本文详细介绍了随机森林的核心原理、关键超参数、OOB误差估计机制,以及其在特征重要性分…...

终极Mac菜单栏整理指南:用Ice让你的桌面从此清爽高效

终极Mac菜单栏整理指南:用Ice让你的桌面从此清爽高效 【免费下载链接】Ice Powerful menu bar manager for macOS 项目地址: https://gitcode.com/GitHub_Trending/ice/Ice 你是否厌倦了Mac菜单栏上密密麻麻的图标?是否经常因为找不到需要的应用图…...

Linux桌面便签终极方案:Sticky让你的灵感永不丢失

Linux桌面便签终极方案:Sticky让你的灵感永不丢失 【免费下载链接】sticky A sticky notes app for the linux desktop 项目地址: https://gitcode.com/gh_mirrors/stic/sticky 在Linux桌面上高效管理零散信息一直是许多用户的痛点。Sticky作为一款专为Linux…...

绝地求生罗技鼠标宏实战指南:5步实现高效压枪技巧

绝地求生罗技鼠标宏实战指南:5步实现高效压枪技巧 【免费下载链接】logitech-pubg PUBG no recoil script for Logitech gaming mouse / 绝地求生 罗技 鼠标宏 项目地址: https://gitcode.com/gh_mirrors/lo/logitech-pubg 对于《绝地求生》玩家来说&#xf…...

规则驱动流程引擎:告别if-else,构建灵活业务自动化核心

1. 项目概述:一个规则驱动的流程引擎最近在梳理一些业务自动化需求时,我又把目光投向了规则引擎和流程编排这个老话题。无论是电商的风控审核、金融的信贷审批,还是内容平台的自动化运营,我们总在重复一个模式:定义一堆…...