Provider
Provider
(状态管理工具)是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI,下面我们给出一个示例
第一步:创建模型
class UserModel {
String name = "Jimi";
void changeName() {
name = "hello";
}
}
第二步:应用程序入口设置
return Provider(
create: (_) => UserModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProviderExample(),
),
);
第三步:使用共享数据
我们这里只需要知道有两个消费者,第一个用于展示模型的数据,第二个用于改变模型的数据。
- 第一个
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("改变值"),
),
);
},
),
],
),
),
);
}
}
运行结果
我们点击按钮通过print可以看出模型数据改变,但是模型数据改变之后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(
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("改变值"),
),
);
},
),
],
),
),
);
}
}
运行结果
补充:
Consumer、Selector
1、Consumber在Widget中调用了Prvoider.of,并将其构造实现委托给了构造器,比如我们常见的Builder,如果你的Widget依赖多个模型,那么它还提供了Consumer23456方便调用。一般来说,使用Provider.of
来取值的结果是一样,但是这里不需要在像使用Provider.of那样每次使用都要写一大串的重复性代码
里面有三个属性:
- context: 当前的上下文
- Provider.of(context): 模型对象
- child: 子组件(不需要刷新的部分)
2、Selector类和Consumer类似,只是对build调用Widget方法时提供更精细的控制,简单点来说,Selector也是一个消费者,它允许你可以从模型中准备定义哪些属性。
比如,用户模型中有50个属性,但是我只需要更新年龄,这样我希望不需要重建用户名、电话号码等组件,那么Selector就是用于解决这个问题
如:
Selector(
selector: (_, userModel1) => userModel1.name,
builder: (_, name, child) {
return Column(
children: [
Text(name,
style: TextStyle(
color: Colors.red,
fontSize: 30
)
),
ElevatedButton(
onPressed: (){
context.read().changeName();
// context.watch().changeName();
// Provider.of(context, listen: false).changeName();
},
child: Text("改变值"),
)
],
);
}
)
return ListView.builder(itemBuilder: (context, index) {
return Selector< ViewModel, Foo>(
selector: (context, viewModel) => viewModel.foos[index],
shouldRebuild: (pre, next) => pre != next, // 此行可以省略
builder: (context, foo, child) {
return ListTile(
title: Text(foo.didSelected),
);
},
);
});
三大方式:
- BuildContext.read: BuildContext.read
()可以替换掉Provider.of (context,listen: false),它会找到CountNotifier1并返回它。 - BuildContext.watch: BuildContext.watch
()可以替换掉Provider.of (context,listen: false),看起来和read没有什么不同,但是使用watch你就不需要在使用Consumer。 - BuildContext.select: BuildContext.select
()可以替换掉Provider.of (context,listen: false),看起来和watch也没有什么不同,但是使用select你就不需要在使用Selector。
Provider.of() -------------->BuildContext.read(不会rebuild,除非包裹在Consumer或Consumer中,或者取值用watch重建)
Consumer ----------------->BuildContext.watch
Selector ------------------->BuildContext.select
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("改变值"),
),
);
},
),
],
),
),
);
}
}
运行结果
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(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("通过代理改变值"),
),
);
},
),
],
),
),
);
}
}
运行结果
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? _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),
);
}
}
运行结果
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。
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("改变值"),
),
);
},
),
],
),
),
);
}
}
运行结果
ListenableProxyProvider
ListenableProxyProvider
是ListenableProvider
的一个变体,但是在使用上和ChangeNotifierProvider
效果惊人的一致。
总结
Provider
为我们提供了非常多的提供者,总共有八种。但我们比较常用的是ChangeNotifierProvider
、MultiProvider
。
背景