文章系列
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<UserModel>( 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<UserModel>( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel>( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("扭转值"), ), ); }, ), ], ), ), ); }}
运行后果
咱们点击按钮的会导致模型数据扭转,然而模型数据扭转之后UI并没有变动也没有重建,那是因为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<UserModel1>( 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<UserModel1>( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel1>( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("扭转值"), ), ); }, ), ], ), ), ); }}
运行后果
FutureProvider
简略来说,FutureProvider
用于提供在组件树中筹备好应用其值时可能尚未筹备好的值,次要是确保空值不会传递给任何子组件,而且FutureProvider
有一个初始值,子组件能够应用该Future
值并通知子组件应用新的值来进行重建。
留神:
FutureProvider
只会重建一次- 默认显示初始值
- 而后显示
Future
值 - 最初不会再次重建
第一步:创立模型
这里和Provider
不同的是减少了构造函数,以及changeName
变成了Future
,咱们模仿网络申请提早两秒后扭转其值。
class UserModel2{ UserModel2({this.name}); String? name = "Jimi"; Future<void> 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<UserModel2> asyncGetUserModel2() async { await Future.delayed(Duration(milliseconds: 2000)); return UserModel2(name: "获取新的数据"); }}
第三步:应用程序入口设置
initialData
是默认值,create
参数咱们传了一个Future<UserModel2>
,因为它接管的模型Create<Future<T>?>
return FutureProvider<UserModel2>( 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<UserModel2>( builder: (_, userModel, child) { return Text(userModel.name ?? "", style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel2>( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("扭转值"), ), ); }, ), ], ), ), ); }}
运行后果
咱们能够看到先展现默认值hello
,最初获取到后果的时候展现了获取新的数据
,咱们尝试扭转其值,尽管值扭转然而并没有刷新UI。
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<UserModel3> getStreamUserModel() { return Stream<UserModel3>.periodic(Duration(milliseconds: 1000), (value) => UserModel3(name: "$value") ).take(10); }}
第三步:应用程序入口设置
这里也有initialData
初始值,和FutureProvider
相似,只是create
属性是获取一个Stream
流。
return StreamProvider<UserModel3>( 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<UserModel3>( builder: (_, userModel, child) { return Text(userModel.name ?? "", style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel3>( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(); }, child: Text("扭转值"), ), ); }, ), ], ), ), ); }}
运行后果
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<UserModel1>( create: (_) => UserModel1(), child: ChangeNotifierProvider<UserModel4>( create: (_) => UserModel4(), child: MaterialApp( debugShowCheckedModeBanner: false, home: MultiProviderExample(), ), ),);
形式二:应用MultiProvider
return MultiProvider( providers: [ ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1()), ChangeNotifierProvider<UserModel4>(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<UserModel1>( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel4>( builder: (_, userModel, child) { return Text(userModel.age.toString(), style: TextStyle( color: Colors.green, fontSize: 30 ) ); }, ), Consumer2<UserModel1, UserModel4>( builder: (_, userModel1, userModel4, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel1.changeName(); userModel4.changeName(); }, child: Text("扭转值"), ), ); }, ), ], ), ), ); }}
运行后果
ProxyProvider
当咱们有多个模型的时候,会有模型依赖另一个模型的状况,在这种状况下,咱们能够应用ProxyProvider
从另一个提供者获取值,而后将其注入到另一个提供者中。咱们来看下代码演示
第一步:创立两个模型
上面咱们创立了两个模型UserModel5
和WalletModel
,而WalletModel
依赖与UserModel5
,当调用WalletModel
的changeName
办法时会扭转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<UserModel5>(create: (_) => UserModel5()), ProxyProvider<UserModel5, WalletModel>( 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<UserModel5>( builder: (_, userModel, child) { return Text(userModel.name, style: TextStyle( color: Colors.red, fontSize: 30 ) ); }, ), Consumer<UserModel5>( builder: (_, userModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ userModel.changeName(newName: "hello"); }, child: Text("扭转值"), ), ); }, ), Consumer<WalletModel>( builder: (_, walletModel, child) { return Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ walletModel.changeName(); }, child: Text("通过代理扭转值"), ), ); }, ), ], ), ), ); }}
运行后果
ChangeNotifierProxyProvider
和ProxyProvider
原理一样,惟一的区别在于它构建和同步ChangeNotifier
的ChangeNotifierProvider
,当提供者数据变动时,将会重构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<int>? _bookIds; // 构造函数 BookManagerModel(this._bookModel, {BookManagerModel? bookManagerModel}) : _bookIds = bookManagerModel?._bookIds ?? []; // 获取所有的书 List<Book> 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<BookModel, BookManagerModel>( 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<ChangeNotifierProxyProviderExample> { 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<BookModel>(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<BookManagerModel>(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<BookManagerModel>(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<BookModel>(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), ); }}
运行后果
ListenableProxyProvider
ListenableProxyProvider
是ListenableProvider
的一个变体,然而在应用上和ChangeNotifierProvider
成果惊人的统一,如果大家对ListenableProxyProvider
有更深的了解,请分割我补充。
总结
Provider
为咱们提供了十分多的提供者,总共有八种。但咱们比拟罕用的是ChangeNotifierProvider
、MultiProvider
、ChangeNotifierProxyProvider
,对于其余的提供者可依据本人的理论利用场景来。