Flutter使用Provider进行状态管理的实现
一、使用Provider进行状态管理的基本用法
Provider
是Flutter中一个非常流行的状态管理工具,它可以帮助开发者更有效地管理Widget树中的数据。Provider
的核心思想是将数据模型放置在Widget树中可以被多个子Widget访问的地方,而不必通过构造函数手动传递。
1.添加provider依赖
dependencies: flutter: sdk: flutter provider: ^6.0.0
2.创建一个数据模型
import 'package:flutter/material.dart'; class CounterModel with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); // 通知监听者数据改变 } }
3.在应用中提供模型
在你的应用中,你需要在一个合适的位置(如MaterialApp
的上方)使用ChangeNotifierProvider
来创建并提供CounterModel
的实例。
import 'package:provider/provider.dart'; void main() { runApp( // 注意:ChangeNotifierProvider要包装在MaterialApp之外 ChangeNotifierProvider( create: (context) => CounterModel(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomeScreen(), ); } }
4.使用Consumer或Provider.of读取和显示数据
在你的HomeScreen
中,你可以使用Consumer
或Provider.of
来读取CounterModel
的数据,并构建UI。
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Provider Example'), ), body: Center( // 使用Consumer来监听CounterModel child: Consumer<CounterModel>( builder: (context, counter, child) => Text('${counter.count}'), ), ), floatingActionButton: FloatingActionButton( onPressed: () { // 不需要监听改变时,可以直接使用Provider.of来访问模型 Provider.of<CounterModel>(context, listen: false).increment(); }, child: Icon(Icons.add), ), ); } }
当你点击浮动按钮时,increment
方法会被调用,CounterModel
中的计数器会增加,并通过notifyListeners
通知Consumer
重新构建,这样UI上显示的数字就会更新。
注意:
- 使用
Provider.of(context)
时,如果你不需要监听变化,可以设置listen: false
,这样可以提高性能。 - 当模型更新时,只有通过
Consumer
或者Provider.of(context)
(并且listen
设置为true
)获取模型的Widget才会重新构建。
二、管理多个不同的状态
如果你有多个不同的状态需要管理,你通常会为每种状态创建不同的模型。每个模型专注于管理一组相关的状态数据和行为。这样可以帮助你保持代码的清晰和分离关注点,使得每个模型都保持简单和专注。
但是,如果你的状态数据非常紧密相关,并且它们通常一起改变,那么将它们放在同一个模型中也是有意义的。
1.创建多个模型
计数器模型
class CounterModel with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
主题色彩模型
class ThemeModel with ChangeNotifier { ThemeData _themeData = ThemeData.light(); ThemeData get themeData => _themeData; void toggleTheme() { _themeData = _themeData == ThemeData.light() ? ThemeData.dark() : ThemeData.light(); notifyListeners(); } }
2.同时管理多个状态
在你的应用中,你可以使用MultiProvider
来同时提供多个模型:
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => CounterModel()), ChangeNotifierProvider(create: (context) => ThemeModel()), ], child: MyApp(), ), ); }
你现在可以根据ThemeModel
提供的主题数据来构建你的应用程序:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<ThemeModel>( builder: (context, themeModel, child) { return MaterialApp( theme: themeModel.themeData, home: HomeScreen(), ); }, ); } }
这样,你就可以在HomeScreen
或者其他任何Widget中分别访问和操作CounterModel
和ThemeModel
了。通过这种方式,你可以将应用的不同部分的状态管理分离开来,从而使你的代码更加模块化和可维护。
三、异步获取状态
有时,我们不想在模型内部中直接管理状态,而是每次修改SharedPreferences中的缓存数据,以及
直接从SharedPreferences
获取状态。更进一步,比如异步从网络获取状态,也是类似的。
比如,我们要管理一个订阅状态。
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class SubscriptionStatusModel extends ChangeNotifier { // 订阅状态的异步读取方法 Future<bool> get isSubscribed async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool('isSubscribeValid') ?? false; } // 更新SharedPreferences中的订阅状态 Future<void> updateSubscriptionStatus(bool newStatus) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool('isSubscribeValid', newStatus); notifyListeners(); // 通知监听器可能有变化 } }
在上面的代码中,isSubscribed
现在是一个异步 getter,每次调用时都会从 SharedPreferences
中读取订阅状态。updateSubscriptionStatus
方法现在会将新状态写入 SharedPreferences
并通知监听器。
这样修改后,你就需要在UI中相应地处理Future。例如,如果你在一个widget中使用这个模型,你可能需要使用 FutureBuilder
:
FutureBuilder<bool>( future: provider.isSubscribed, // 假设` provider `是你的SubscriptionStatusModel实例 builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // 等待数据时返回加载指示器 return CircularProgressIndicator(); } else if (snapshot.hasError) { // 处理错误情况 return Text('Error: ${snapshot.error}'); } else { // 使用订阅状态来构建widget bool isSubscribed = snapshot.data ?? false; return Text(isSubscribed ? 'Subscribed' : 'Not subscribed'); } }, )
实际用例:
Stack( alignment: Alignment.center, children: [ CustomScrollView( // ... ), Consumer<SubscriptionStatusModel>(builder: (context, provider, child) { return FutureBuilder<bool>( future: provider.isSubscribed, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // 等待数据时返回加载指示器 // return CircularProgressIndicator(); return SizedBox.shrink(); } else if (snapshot.hasError) { // 处理错误情况 // return Text('Error: ${snapshot.error}'); return SizedBox.shrink(); } else { // 使用订阅状态来构建widget bool isSubscribed = snapshot.data ?? false; return Visibility( visible: !isSubscribed, child: Positioned( left: 0, right: 0, bottom: 16, child: _subscribeButton(), ), ); } }, ); }), ], ),
到此这篇关于Flutter使用Provider进行状态管理的实现的文章就介绍到这了,更多相关Flutter Provider状态管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Android 将本地资源图片转换成Drawable,进行设置大小操作
这篇文章主要介绍了Android 将本地资源图片转换成Drawable,进行设置大小操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-08-08Android使用自定义View实现360手机卫士波浪球进度的效果
360卫士的波浪球进度的效果,一般最常用的方法就是画线的方式,先绘sin线或贝塞尔曲线,然后从左到右绘制竖线,然后再裁剪圆区域2018-05-05Android SwipeRefreshLayout下拉刷新源码解析
这篇文章主要为大家详细解析了Android SwipeRefreshLayout下拉刷新源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2016-11-11Android实现界面内嵌多种卡片视图(ViewPager、RadioGroup)
这篇文章主要为大家详细介绍了Android实现界面内嵌多种卡片视图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2017-09-09
最新评论