Flutter Provider状态管理---八种提供者使用分析

文章系列 Flutter Provider状态管理---介绍、类图分析、基本使用
Flutter Provider状态管理---八种提供者使用分析
视频系列 Flutter Provider状态管理---介绍、类图分析、基本使用
Flutter Provider状态管理---八种提供者使用分析
源码仓库地址 github仓库地址
前言 在我们上一篇文章中对Provider进行了介绍以及类结构的说明,最后还写了一个简单的示例,通过上一章节我们对Provider有了一个基本的了解,这一章节我们来说说Provider的8种提供者以及他们的使用区别。
Provider Provider是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI,下面我们给出一个示例
第一步:创建模型

class UserModel {String name = "Jimi"; void changeName() { name = "hello"; } }

第二步:应用程序入口设置
return Provider( create: (_) => UserModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: ProviderExample(), ), );

第三步:使用共享数据
关于Consumer后面将消费者在提及,我们这里只需要知道有两个消费者,第一个用于展示模型的数据,第二个用于改变模型的数据。
  • 第一个Comsumer是用于读取模型的数据name
  • 第二个Consumer用于改变模型的数据name
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_example/user_model.dart'; import 'package:provider/provider.dart'; class ProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("ProviderExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("改变值"), ), ); }, ), ], ), ), ); } }

运行结果
我们点击按钮的会导致模型数据改变,但是模型数据改变之后UI并没有变化也没有重建,那是因为Provider提供者组件不会监听它提供的值的变化。
Flutter Provider状态管理---八种提供者使用分析
文章图片

ChangeNotifierProvider 它跟Provider组件不同,ChangeNotifierProvider会监听模型对象的变化,而且当数据改变时,它也会重建Consumer(消费者),下面我们给出一个示例
第一步:创建模型
细心点我们可以发现这里定义的模型有两处变化,如下:
  • 混入了ChangeNotifier
  • 调用了notifyListeners()
因为模型类使用了ChangeNotifier,那么我们就可以访问notifyListeners()并且在调用它的任何时候,ChangeNotifierProvider都会收到通知并且消费者将重建UI。
import 'package:flutter/material.dart'; class UserModel1 with ChangeNotifier {String name = "Jimi"; void changeName() { name = "hello"; notifyListeners(); } }

第二步:应用程序入口设置
return ChangeNotifierProvider( create: (_) => UserModel1(), child: MaterialApp( debugShowCheckedModeBanner: false, home: ChangeNotifierProviderExample(), ), );

第三步:使用共享数据
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart'; import 'package:provider/provider.dart'; class ChangeNotifierProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("ChangeNotifierProvider"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("改变值"), ), ); }, ), ], ), ), ); } }

运行结果
Flutter Provider状态管理---八种提供者使用分析
文章图片

FutureProvider 简单来说,FutureProvider用于提供在组件树中准备好使用其值时可能尚未准备好的值,主要是确保空值不会传递给任何子组件,而且FutureProvider有一个初始值,子组件可以使用该Future值并告诉子组件使用新的值来进行重建。
注意:
  • FutureProvider只会重建一次
  • 默认显示初始值
  • 然后显示Future
  • 最后不会再次重建
第一步:创建模型
这里和Provider不同的是增加了构造函数,以及changeName变成了Future,我们模拟网络请求延迟两秒后改变其值。
class UserModel2{UserModel2({this.name}); String? name = "Jimi"; Future changeName() async { await Future.delayed(Duration(milliseconds: 2000)); name = "hello"; } }

第二步:提供Future
我们有一个方法,就是异步获取userModel2,模拟网络请求延迟两秒执行,最后修改了name并返回UserModel2
import 'package:flutter_provider_example/future_provider_example/user_model2.dart'; class UserFuture {Future asyncGetUserModel2() async { await Future.delayed(Duration(milliseconds: 2000)); return UserModel2(name: "获取新的数据"); }}

第三步:应用程序入口设置
initialData是默认值,create参数我们传了一个Future,因为它接收的模型Create?>
return FutureProvider( initialData: UserModel2(name: "hello"), create: (_) => UserFuture().asyncGetUserModel2(), child: MaterialApp( debugShowCheckedModeBanner: false, home: FutureProviderExample(), ), );

第四步:使用共享数据
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/future_provider_example/user_model2.dart'; import 'package:provider/provider.dart'; class FutureProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("FutureProviderExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (_, userModel, child) { return Text(userModel.name ?? "", style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("改变值"), ), ); }, ), ], ), ), ); } }

运行结果
我们可以看到先展示默认值hello,最后获取到结果的时候展示了获取新的数据,我们尝试改变其值,虽然值改变但是并没有刷新UI。
Flutter Provider状态管理---八种提供者使用分析
文章图片

StreamProvider StreamProvider提供流值,是围绕StreamBuilder,所提供的值会在传入的时候替换掉新值。和FutureProvider一样,主要的区别在于值会根据多次触发重新构建UI。
如果你对StreamBuilder不太了解的话,那么你就很难理解StreamProvider,StreamProvider文档地址
第一步:创建模型
class UserModel3{UserModel3({this.name}); String? name = "Jimi"; void changeName() { name = "hello"; } }

第二步:提供Stream
下面这段代码类似计时器,每隔一秒钟生成一个数字
import 'package:flutter_provider_example/stream_provider_example/user_model3.dart'; class UserStream {Stream getStreamUserModel() { return Stream.periodic(Duration(milliseconds: 1000), (value) => UserModel3(name: "$value") ).take(10); } }

第三步:应用程序入口设置
这里也有initialData初始值,和FutureProvider类似,只是create属性是获取一个Stream流。
return StreamProvider( initialData: UserModel3(name: "hello"), create: (_) => UserStream().getStreamUserModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: StreamProviderExample(), ), );

第四步:使用共享数据
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/stream_provider_example/user_model3.dart'; import 'package:provider/provider.dart'; class StreamProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("StreamProviderExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (_, userModel, child) { return Text(userModel.name ?? "", style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("改变值"), ), ); }, ), ], ), ), ); } }

运行结果
Flutter Provider状态管理---八种提供者使用分析
文章图片

MultiProvider 在上面的例子中我们都只是返回了一个提供者,在实际开发过程中肯定会有多个提供者,我们虽然可以采用嵌套的方式来解决,但是这样无疑是混乱的,可读性级差。这个时候强大的MultiProvder就产生了,我们来看下示例:
第一步:创建两个模型
import 'package:flutter/material.dart'; class UserModel1 with ChangeNotifier {String name = "Jimi"; void changeName() { name = "hello"; notifyListeners(); } }class UserModel4 with ChangeNotifier {String name = "Jimi"; int age = 18; void changeName() { name = "hello"; age = 20; notifyListeners(); } }

第二步:应用程序入口设置
相对于方式一这种嵌套方式设置,方式二就显得尤为简单。
方式一:嵌套设置
return ChangeNotifierProvider( create: (_) => UserModel1(), child: ChangeNotifierProvider( create: (_) => UserModel4(), child: MaterialApp( debugShowCheckedModeBanner: false, home: MultiProviderExample(), ), ), );

方式二:使用MultiProvider
return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => UserModel1()), ChangeNotifierProvider(create: (_) => UserModel4()), /// 添加更多 ], child: MaterialApp( debugShowCheckedModeBanner: false, home: MultiProviderExample(), ), );

第三步:使用共享数据
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart'; import 'package:flutter_provider_example/multi_provider_example/user_model4.dart'; import 'package:provider/provider.dart'; class MultiProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("MultiProviderExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer( builder: (_, userModel, child) { return Text(userModel.age.toString(), style: TextStyle( color: Colors.green, fontSize: 30 ) ); }, ), Consumer2( builder: (_, userModel1, userModel4, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel1.changeName(); userModel4.changeName(); }, child: Text("改变值"), ), ); }, ), ], ), ), ); } }

运行结果
Flutter Provider状态管理---八种提供者使用分析
文章图片

ProxyProvider 当我们有多个模型的时候,会有模型依赖另一个模型的情况,在这种情况下,我们可以使用ProxyProvider从另一个提供者获取值,然后将其注入到另一个提供者中。我们来看下代码演示
第一步:创建两个模型
下面我们创建了两个模型UserModel5WalletModel,而WalletModel依赖与UserModel5,当调用WalletModelchangeName方法时会改变UserModel5里面的name,当然我们在实际开发的过程中并不是这么简单,这里只是演示模型依赖时如果使用ProxyProvider
import 'package:flutter/material.dart'; class UserModel5 with ChangeNotifier {String name = "Jimi"; void changeName({required String newName}) { name = newName; notifyListeners(); } }class WalletModel {UserModel5? userModel5; WalletModel({this.userModel5}); void changeName() { userModel5?.changeName(newName: "JIMI"); } }

第二步:应用程序入口设置
return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => UserModel5()), ProxyProvider( update: (_, userModel5, walletModel) => WalletModel(userModel5: userModel5), ) ], child: MaterialApp( debugShowCheckedModeBanner: false, home: ProxyProviderExample(), ), );

第三步:使用共享数据
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/proxy_provider_example/user_model5.dart'; import 'package:flutter_provider_example/proxy_provider_example/wallet_model.dart'; import 'package:provider/provider.dart'; class ProxyProviderExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("ProxyProviderExample"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Consumer( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(newName: "hello"); }, child: Text("改变值"), ), ); }, ), Consumer( builder: (_, walletModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ walletModel.changeName(); }, child: Text("通过代理改变值"), ), ); }, ), ], ), ), ); } }

运行结果
Flutter Provider状态管理---八种提供者使用分析
文章图片

ChangeNotifierProxyProvider 和ProxyProvider原理一样,唯一的区别在于它构建和同步ChangeNotifierChangeNotifierProvider,当提供者数据变化时,将会重构UI。
下面我们给出一个例子:
  • 获取书籍列表
  • 获取收藏书籍列表
  • 点击书籍可加入或者取消收藏
  • 通过代理实时重构UI
第一步:创建两个模型
1、BookModel BookModel用户存储模型数据,将书籍转换成模型。
class BookModel {static var _books = [ Book(1, "夜的命名数"), Book(2, "大奉打更人"), Book(3, "星门"), Book(4, "大魏读书人"), Book(5, "我师兄实在太稳健了"), Book(6, "深空彼岸"), ]; // 获取书籍长度 int get length => _books.length; // 根据ID获取书籍 Book getById(int id) => _books[id -1]; // 根据索引获取数据 Book getByPosition(int position) => _books[position]; // 更多.... }class Book { final int bookId; final String bookName; Book(this.bookId, this.bookName); }

2、BookManagerModel BookManagerModel主要用于管理书籍、收藏书籍、取消收藏等操作
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart'; class BookManagerModel with ChangeNotifier {// 依赖bookModel final BookModel _bookModel; // 获取数据所有的ID List? _bookIds; // 构造函数 BookManagerModel(this._bookModel, {BookManagerModel? bookManagerModel}) : _bookIds = bookManagerModel?._bookIds ?? []; // 获取所有的书 List get books => _bookIds!.map((id) => _bookModel.getById(id)).toList(); // 根据索引获取数据 Book getByPosition(int position) => books[position]; // 获取书籍的长度 int get length => _bookIds?.length ?? 0; // 添加书籍 void addFaves(Book book) { _bookIds!.add(book.bookId); notifyListeners(); }// 删除书籍 void removeFaves(Book book) { _bookIds!.remove(book.bookId); notifyListeners(); } }

第二步:应用程序入口设置
return MultiProvider( providers: [ Provider(create: (_) => BookModel()), ChangeNotifierProxyProvider( create: (_) => BookManagerModel(BookModel()), update: (_, bookModel, bookManagerModel) => BookManagerModel(bookModel), ) ], child: MaterialApp( debugShowCheckedModeBanner: false, home: ChangeNotifierProxyProviderExample(), ), );

第三步:设置BottomNavigationBar
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_a.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_b.dart'; class ChangeNotifierProxyProviderExample extends StatefulWidget { @override _ChangeNotifierProxyProviderExampleState createState() => _ChangeNotifierProxyProviderExampleState(); }class _ChangeNotifierProxyProviderExampleState extends State {var _selectedIndex = 0; var _pages = [PageA(), PageB()]; @override Widget build(BuildContext context) { return Scaffold( body: _pages[_selectedIndex], bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, onTap: (index) { setState(() { _selectedIndex = index; }); }, items: [ BottomNavigationBarItem( icon: Icon(Icons.book), label: "书籍列表" ), BottomNavigationBarItem( icon: Icon(Icons.favorite), label: "收藏" ) ], ), ); } }

第四步:书籍列表UI构建
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart'; import 'package:provider/provider.dart'; class PageA extends StatelessWidget { @override Widget build(BuildContext context) {var bookModel = Provider.of(context); return Scaffold( appBar: AppBar( title: Text("书籍列表"), ), body: ListView.builder( itemCount: bookModel.length, itemBuilder: (_, index) => BookItem(id: index + 1), ), ); } }

第五步:收藏列表UI构建
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart'; import 'package:provider/provider.dart'; class PageB extends StatelessWidget { @override Widget build(BuildContext context) {var bookManagerModel = Provider.of(context); var bookCount = bookManagerModel.length; return Scaffold( appBar: AppBar( title: Text("收藏列表"), ), body: ListView.builder( itemCount: bookCount, itemBuilder: (_, index) => BookItem(id: bookManagerModel.getByPosition(index).bookId), ), ); } }

其他辅助封装类
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart'; import 'package:provider/provider.dart'; class BookButton extends StatelessWidget {final Book book; BookButton({ Key? key, required this.book }) : super(key: key); @override Widget build(BuildContext context) {var bookManagerModel = Provider.of(context); return GestureDetector( onTap: bookManagerModel.books.contains(this.book) ?() => bookManagerModel.removeFaves(this.book) :() => bookManagerModel.addFaves(this.book), child: SizedBox( width: 100, height: 60, child: bookManagerModel.books.contains(this.book) ?Icon(Icons.star, color: Colors.red,) :Icon(Icons.star_border), ), ); } }

import 'package:flutter/material.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart'; import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_button.dart'; import 'package:provider/provider.dart'; class BookItem extends StatelessWidget {final int id; BookItem({ Key? key, required this.id }) : super(key: key); @override Widget build(BuildContext context) {var bookModel = Provider.of(context); var book = bookModel.getById(id); return ListTile( leading: CircleAvatar( child: Text("${book.bookId}"), ), title: Text("${book.bookName}", style: TextStyle( color: Colors.black87 ), ), trailing: BookButton(book: book), ); } }

运行结果
Flutter Provider状态管理---八种提供者使用分析
文章图片

ListenableProxyProvider ListenableProxyProviderListenableProvider的一个变体,但是在使用上和ChangeNotifierProvider效果惊人的一致,如果大家对ListenableProxyProvider有更深的理解,请联系我补充。
总结 【Flutter Provider状态管理---八种提供者使用分析】Provider为我们提供了非常多的提供者,总共有八种。但我们比较常用的是ChangeNotifierProviderMultiProviderChangeNotifierProxyProvider,关于其他的提供者可根据自己的实际应用场景来。

    推荐阅读