Provider
Provider 为我们提供了一种简单、低样板的方法,将业务逻辑与应用程序中的小部件分开。由于它是基于InheritedWidget
类构建的,因此还可以轻松地重用和重构业务逻辑。将状态与 UI 分离是 Provider 解决的主要问题之一。
Provider能为我们做什么?
- 将状态和UI分开
- 根据状态的变化重建UI
步骤:
添加依赖项:
找到 pubspec.yaml 文件:
dependencies:
provider: ^6.0.5
导入包:import 'package:provider/provider.dart';
ChangeNotifier
ChangeNotifier
is a simple class included in the Flutter SDK which provides change notification to its listeners. In other words, if something is a ChangeNotifier
, you can subscribe to its changes. (It is a form of Observable, for those familiar with the term.)
ChangeNotifier
是 Flutter SDK 中包含的一个简单类,它向其侦听器提供更改通知。换句话说,如果某件事是 a ChangeNotifier
,您可以订阅它的更改。(对于熟悉该术语的人来说,它是 Observable 的一种形式。)
In provider
, ChangeNotifier
is one way to encapsulate your application state. For very simple apps, you get by with a single ChangeNotifier
. In complex ones, you’ll have several models, and therefore several ChangeNotifiers
. (You don’t need to use ChangeNotifier
with provider
at all, but it’s an easy class to work with.)
在 provider中
,ChangeNotifier
是封装应用程序状态的一种方法。对于非常简单的应用程序,您可以使用单个ChangeNotifier
. 在复杂的模型中,您将有多个模型,因此也有多个 ChangeNotifiers
. (您根本不需要使用ChangeNotifier
with provider
,但它是一个易于使用的类。)
个人理解:ChangeNotifier是真正存放数据的地方
//真正的数据存放处
class favorite extends ChangeNotifier{
bool isfavorite=false;
void modify(){
isfavorite=!isfavorite;
notifyListeners();
}
}
class food extends ChangeNotifier{
List<String> foods=["苹果","李子","桃子","橙子"];
void addfood(String food){
foods.add(food);
notifyListeners();
}
}
ChangeNotifierProvider
ChangeNotifierProvider
is the widget that provides an instance of a ChangeNotifier
to its descendants. It comes from the provider
package.
We already know where to put ChangeNotifierProvider
: above the widgets that need to access it. In the case of CartModel
, that means somewhere above both MyCart
and MyCatalog
.
You don’t want to place ChangeNotifierProvider
higher than necessary (because you don’t want to pollute the scope). But in our case, the only widget that is on top of both MyCart
and MyCatalog
is MyApp
.
It also automatically calls dispose()
on CartModel
when the instance is no longer needed.
ChangeNotifierProvider 是一个向其后代提供 ChangeNotifier 实例的小部件。 它来自提供程序包。
我们已经知道将 ChangeNotifierProvider: 放在需要访问它的小部件之上的位置。 就 CartModel 而言,这意味着位于 MyCart 和 MyCatalog 之上。
您不想将 ChangeNotifierProvider 放置在高于必要的位置(因为您不想污染范围)。 但在我们的例子中,MyCart 和 MyCatalog 之上的唯一小部件是 MyApp。
当在CarModel中不再需要时,它会自动调用dispose()方法
个人理解:通过创建
ChangeNotifierProvider 将真正存放数据的ChangeNotifier提供给需要的widget
使用单个Provider:
main()=>runApp(
ChangeNotifierProvider(
create: (_)=>favorite(),
child: MyApp(),
)
);
多个Provider:
main()=>runApp(
MultiProvider(providers: [
ChangeNotifierProvider(create: (_)=>favorite()),
ChangeNotifierProvider(create: (_)=>food(),),
],
child: MyApp(),
)
);
使用数据
-
Consumer
Now that CartModel
is provided to widgets in our app through the ChangeNotifierProvider
declaration at the top, we can start using it.
现在通过ChangeNotifierProvider
在顶部的声明,CartModel
已经可以被提供给app中的widget了
return Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Total price: ${cart.totalPrice}');
},
);
- 必须指定要访问的模型的类型,如:Consumer<CartModel>
- Consumer中唯一必须的参数就是builder,Builder 是一个每当发生变化时就会被调用的函数
ChangeNotifier
。(换句话说,当您调用notifyListeners()
模型时,所有相应Consumer
小部件的所有构建器方法都会被调用。 - builder需要三个参数,
- context,每个build方法都会有上下文,目的是知道当前树的位置
- ChangeNotifier对应的实例,也是我们在builder函数中主要使用的对象
- child,目的是进行优化,如果builder下面有一颗庞大的子树,当模型发生改变的时候,我们并不希望重新build这颗子树,那么就可以将这颗子树放到Consumer的child中,在这里直接引入即可
class demo2Consumer extends StatelessWidget {
const demo2Consumer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer<favorite>(
builder: (context,instancs,child){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("喜欢1"),
SizedBox(width: 20,),
InkWell(
onTap: (){
print("添加到喜欢");
instancs.modify();
print("当前是否喜欢:${context.read<favorite>().isfavorite}");
},
child:instancs.isfavorite?
Icon(Icons.favorite,color: Colors.red,):
Icon(Icons.favorite_border_rounded,),
),
SizedBox(width: 20,),
Text("当前是否喜欢: ${context.read<favorite>().isfavorite}")
],
);
},
);
}
}
-
Provider.of
Provider.of<T>(context)
是 Provider
为我们提供的静态方法,当我们使用该方法去获取值的时候会返回查找到的最近的 T
类型的 provider
给我们,且也不会遍历整个组件树。
class demo1Provider extends StatelessWidget {
const demo1Provider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final demo=Provider.of<favorite>(context);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("喜欢2"),
SizedBox(width: 20,),
InkWell(
onTap: (){
demo.modify();
},
child: demo.isfavorite? Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
),
SizedBox(width: 20,),
Text("当前是否喜欢: ${context.read<favorite>().isfavorite}")
],
);
}
}
-
Selector
Selector相当于 Consumer 可以通过选择有限数量的值来过滤更新,并在它们没有更改时防止重建。
源码:
即从 A 类型的数据中选择 S 类型数据。然后,这个返回值被传递给父类构造函数中的 selector 参数。
class Selector<A, S> extends Selector0<S> {
/// {@macro provider.selector}
Selector({
Key? key,
required ValueWidgetBuilder<S> builder,
required S Function(BuildContext, A) selector,//传过来一个A,返回一个S
ShouldRebuild<S>? shouldRebuild,
Widget? child,
}) : super(
key: key,
shouldRebuild: shouldRebuild,
builder: builder,
selector: (context) => selector(context, Provider.of(context)),
child: child,
);
}
required S Function(BuildContext, A) selector
selector 是一个回调函数,它接受两个参数:一个是 BuildContext 类型的 context,另一个是泛型类型 A。这个回调函数的返回值是泛型类型 S
selector: (context) => selector(context, Provider.of(context)),
selector 函数被调用,并传入了 context 和 Provider.of(context) 两个参数。这个表达式的返回值是 selector(context, Provider.of(context)),其中Provider.of(context)),即从 A 类型的数据中选择 S 类型数据。然后,这个返回值被传递给父类构造函数中的 selector 参数,该selector
回调的任务是返回一个对象,该对象仅包含builder
所需的信息。
在 Selector 类的定义中,builder 参数的类型为 ValueWidgetBuilder<S>。
它是一个回调函数,它接受三个参数:BuildContext 类型的 context、泛型类型 S 的数据和一个可选的 Widget 类型的子部件。它的返回值是一个小部件,通常是根据 S 类型的数据构建的。
typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value, Widget? child);
例子:
class demo4Selector extends StatelessWidget {
const demo4Selector({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Selector<favorite,bool>(
selector:(context,favoritedemo)=>favoritedemo.isfavorite,
shouldRebuild: (pre,next)=>true,
builder: (context,demo1,child){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width:150,child:Text("喜欢Selectorbool"),),
SizedBox(width: 20,),
Text("当前是否喜欢: ${demo1}")
],
);
}, );
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
main()=>runApp(
MultiProvider(providers: [
ChangeNotifierProvider(create: (_)=>favorite()),
ChangeNotifierProvider(create: (_)=>food(),),
],
child: MyApp(),
)
);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "收藏",
home: HYHomePage(),
);
}
}
//消费者 Provider.of(context)
class HYHomePage extends StatelessWidget {
const HYHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("收藏示例"),
),
body: Center(
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
demo1Provider(),
SizedBox(height: 20,),
demo2Consumer(),
SizedBox(height: 20,),
demo3Selector(),
SizedBox(height: 20,),
demo4Selector(),
],
) ,
),
);
}
}
/*Provider.of<T>(context) 是 Provider 为我们提供的静态方法,
当我们使用该方法去获取值的时候会返回查找到的最近的 T 类型的 provider 给我们,且也不会遍历整个组件树。*/
class demo1Provider extends StatelessWidget {
const demo1Provider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final demo=Provider.of<favorite>(context);
final demo2=Provider.of<food>(context);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width:100,child: Text("喜欢Provider")),
SizedBox(width: 20,),
InkWell(
onTap: (){
demo.modify();
demo2.addfood("一只猫猫爪");
},
child: demo.isfavorite? Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
),
SizedBox(width: 20,),
Text("当前是否喜欢: ${context.read<favorite>().isfavorite}"),
SizedBox(width: 20,),
Text("${demo2.foods}"),
],
);
}
}
class demo2Consumer extends StatelessWidget {
const demo2Consumer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer2<favorite,food>(
builder: (context,instancs,foodinstanc,child){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width:100,child:Text("喜欢Consumer"),),
SizedBox(width: 20,),
InkWell(
onTap: (){
print("添加到喜欢");
instancs.modify();
foodinstanc.addfood("大芒果");
print("当前是否喜欢:${context.read<favorite>().isfavorite}");
},
child:instancs.isfavorite?
Icon(Icons.favorite,color: Colors.red,):
Icon(Icons.favorite_border_rounded,),
),
SizedBox(width: 20,),
Text("当前是否喜欢: ${context.read<favorite>().isfavorite}"),
SizedBox(width: 20,),
Text("${foodinstanc.foods}"),
],
);
},
);
}
}
class demo3Selector extends StatelessWidget {
const demo3Selector({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final demo22=Provider.of<food>(context);
return Selector<favorite,favorite>(
selector:(context,favoritedemo)=>favoritedemo,
shouldRebuild: (pre,next)=>true,
builder: (context,demo1,child){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width:100,child:Text("喜欢Selector"),),
SizedBox(width: 20,),
InkWell(
onTap: (){
demo1.modify();
demo22.addfood("孙悟空最喜欢的桃子");
},
child: demo1.isfavorite? Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
),
SizedBox(width: 20,),
Text("当前是否喜欢: ${demo1.isfavorite}"),
SizedBox(width: 20,),
Text("${Provider.of<food>(context).foods}"),
],
);
}, );
}
}
class demo4Selector extends StatelessWidget {
const demo4Selector({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Selector<favorite,bool>(
selector:(context,favoritedemo)=>favoritedemo.isfavorite,
shouldRebuild: (pre,next)=>true,
builder: (context,demo1,child){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width:150,child:Text("喜欢Selectorbool"),),
demo1?Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
SizedBox(width: 20,),
Text("当前是否喜欢: ${demo1}"),
SizedBox(width: 20,),
Text("${Provider.of<food>(context).foods}"),
],
);
}, );
}
}
//真正的数据存放处
class favorite extends ChangeNotifier{
bool isfavorite=false;
void modify(){
isfavorite=!isfavorite;
notifyListeners();
}
}
class food extends ChangeNotifier{
List<String> foods=["苹果","李子","桃子","橙子"];
void addfood(String food){
foods.add(food);
notifyListeners();
}
}
总结:
ChangeNotifier是用于存放数据的地方,ChangeNotifierProvider是用于将数据提供给需要的Widget的地方,而Consumer则是用于使用这些数据的地方。
当数据发生变化时,ChangeNotifier会通知依赖它的Consumer重新构建,并使用新的数据来更新UI。这种方式可以实现局部重建,提高应用的性能。