Provider.of
Provider.of<T>(context)
It is Provider
a static method provided for us. When we use this method to get the value, it will return the nearest T
type found provider
to us, and it will not traverse the entire component tree. Let's look at the code below:
Step 1: Define the model
We define a CountNotifier1
model, and all the following sample codes will be demonstrated around this model
import 'package:flutter/material.dart';
class CountNotifier1 with ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
Step 2: Application entry setting
return ChangeNotifierProvider(
create: (_) => CountNotifier1(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ConsumerExample(),
),
);
Step 3: Use Provider.of
Here, the value is read and the button +1 is clicked Provider.of<T>()
to obtain and use it.
import 'package:flutter/material.dart';
import 'package:flutter_provider_example/consumer_example/count_notifier1.dart';
import 'package:provider/provider.dart';
class ConsumerExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ConsumerExample"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(Provider.of<CountNotifier1>(context).count.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 50
),
),
Padding(
padding: EdgeInsets.only(
top: 20
),
child: ElevatedButton(
onPressed: (){
Provider.of<CountNotifier1>(context).increment();
},
child: Text("点击加1"),
),
)
],
),
),
);
}
}
error log
When we run the code, an error will be prompted, which indicates that we are trying to listen to Widget
the value exposed by the provider from outside the tree. If we want to fix it, we can change it to listen
. false
This problem will actually Provider 4.0.2
appear later, the most important thing is its default The behavior is ture
, the error log is as follows:
======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.
To fix, write:
Provider.of<CountNotifier1>(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.
The context used was: ConsumerExample(dependencies: [_InheritedProviderScope<CountNotifier1?>])
'package:provider/src/provider.dart':
Failed assertion: line 276 pos 7: 'context.owner!.debugBuilding ||
listen == false ||
debugIsInInheritedProviderUpdate'
When the exception was thrown, this was the stack:
........
====================================================================================================
Set listen to false
Provider.of<CountNotifier1>(context, listen: false).increment();
Consumer
Consumber
It is just Widget
called in Prvoider.of
, and its construction is delegated to the constructor. For example Builder
, if you Widget
rely on multiple models, it also provides a convenient call. Next, we will use it to modify Consumer23456
the above caseConsumer
Wrap components with Consumer
There is a builder
constructor in it. When we change it to body
the following and rerun it, we can find that Provider.of
the result is the same as that used, but there is no need to Provider.of
write a large series of repetitive codes every time we use it.
There are three properties in it:
- context: the current context
- **Provider.of(context):** model object
- child: child component (the part that does not need to be refreshed)
body: Consumer( builder: (_, CountNotifier1 countNotifier1, child) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(countNotifier1.count.toString(), style: TextStyle( color: Colors.red, fontSize: 50 ), ), Padding( padding: EdgeInsets.only( top: 20 ), child: ElevatedButton( onPressed: (){ countNotifier1.increment(); }, child: Text("点击加1"), ), ) ], ), ); }, ),
Optimizing Consumers
Optimization Method 1: Adjust the position of Consumer as much as possible
We found in the above code
Center
thatColumn
the components are alsoConsumer
wrapped, but these two components do not need to update the state, and everyWidget
time we build, we will rebuild the wholebody
, so we optimize the code structure, it looks like Like this:body: Center( child: Consumer( builder: (_, CountNotifier1 countNotifier1, child) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( countNotifier1.count.toString(), style: TextStyle(color: Colors.red, fontSize: 50), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () { countNotifier1.increment(); }, child: Text("点击加1"), ), ), Container( child: Column( children: [ Text("更多组件1"), Text("更多组件2"), Text("更多组件3"), Text("更多组件4"), Text("更多组件5"), Text("更多组件6"), ], ), ) ], ), ); }, ) )
Optimization method 2: Use child for components that do not need to be refreshed but are wrapped by Consumer
For example, above we have more components 1-6 or even hundreds of components that do not need to refresh the state, but because you use the
Consumer
package to refresh all of them, it will obviously lead to a decrease in performance. You may think ofConsumer
components that need to be refreshed with multiple packages alone It's solved, but isn't this putting the cart before the horse? It is a robust and repetitive codeProvider
to solve the code , so at this time we can use the parameters provided for us , as follows:Consumer
child
body: Center( child: Consumer( builder: (_, CountNotifier1 countNotifier1, child) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( countNotifier1.count.toString(), style: TextStyle(color: Colors.red, fontSize: 50), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () { countNotifier1.increment(); }, child: Text("点击加1"), ), ), child! ], ), ); }, child: Container( child: Column( children: [ Text("更多组件1"), Text("更多组件2"), Text("更多组件3"), Text("更多组件4"), Text("更多组件5"), Text("更多组件6"), ], ), ), ) ),
Selector
Selector
Classes areConsumer
similar, but provide more fine-grained control whenbuild
callingWidget
methods. In simple terms,Selector
they are also a consumer, which allows you to prepare which properties to define from the model.Let's take an example:
For example, there are 50 attributes in the user model, but I only need to update the age, so I hope that there is no need to rebuild components such as user name and phone number
Selector
, so it is used to solve this problem. Let's take a look at the example:第一步:定义模型 import 'package:flutter/material.dart'; class UserModel6 with ChangeNotifier { String name = "Jimi"; int age = 18; String phone = "18888888888"; void increaseAge() { age++; notifyListeners(); } } 第二步:应用程序入口设置 return ChangeNotifierProvider( create: (_) => UserModel6(), child: MaterialApp( debugShowCheckedModeBanner: false, home: SelectorExample(), ), ); 第三步:使用Selector更精细的控制 import 'package:flutter/material.dart'; import 'package:flutter_provider_example/selector_example/user_model6.dart'; import 'package:provider/provider.dart'; class SelectorExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("SelectorExample"), ), body: Center( child: Selector<UserModel6, int>( selector: (_, userModel6) => userModel6.age, builder: (_, age, child) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(age.toString(), style: TextStyle( color: Colors.red, fontSize: 30 ) ), child! ], ); }, child: Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: (){ Provider.of<UserModel6>(context, listen: false).increaseAge(); }, child: Text("改变年龄"), ), ), ), ), ); } }
Three ways:
- BuildContext.read:
BuildContext.read<CountNotifier1>()
can be replacedProvider.of<CountNotifier1>(context,listen: false)
, it will findCountNotifier1
and return it. - BuildContext.watch:
BuildContext.watch<CountNotifier1>()
Can be replacedProvider.of<CountNotifier1>(context,listen: false)
, it looksread
the same, butwatch
you don't need to use itConsumer
. - BuildContext.select:
BuildContext.select<CountNotifier1>()
It can be replacedProvider.of<CountNotifier1>(context,listen: false)
, it lookswatch
the same as it, butselect
you don't need to use itSelector
. -
BuildContext.read
The following two ways of using the result are the same
Use Provider.of()
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart'; import 'package:provider/provider.dart'; class InheritedContextExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("InheritedContextExample"), ), /// Provider.of 获取值 body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(Provider.of<CountNotifier2>(context).count.toString(), style: TextStyle( color: Colors.red, fontSize: 50 ), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(), child: Text("点击加1"), ), ), ], ), ), ); } }
Use BuildContext.read
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart'; import 'package:provider/provider.dart'; class InheritedContextExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("InheritedContextExample"), ), /// read 获取值 body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(context.read<CountNotifier2>().count.toString(), style: TextStyle( color: Colors.red, fontSize: 50 ), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(), child: Text("点击加1"), ), ), ], ), ), ); } }
BuildContext.watch
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart'; import 'package:provider/provider.dart'; class InheritedContextExample extends StatelessWidget { @override Widget build(BuildContext context) { /// 重要 final countNotifier2 = context.watch<CountNotifier2>(); return Scaffold( appBar: AppBar( title: Text("InheritedContextExample"), ), /// watch body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(countNotifier2.count.toString(), style: TextStyle( color: Colors.red, fontSize: 50 ), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () => countNotifier2.increment(), child: Text("点击加1"), ), ), ], ), ), ); } }
BuildContext.select
Use Selector
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart'; import 'package:provider/provider.dart'; class InheritedContextExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("InheritedContextExample"), ), /// Selector body: Center( child: Selector<CountNotifier2, int>( selector: (_, countNotifier2) => countNotifier2.count, builder: (_, count, child) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(count.toString(), style: TextStyle( color: Colors.red, fontSize: 50 ), ), child! ], ); }, child: Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(), child: Text("点击加1"), ), ), ), ), ); } }
Use BuildContext.select
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart'; import 'package:provider/provider.dart'; class InheritedContextExample extends StatelessWidget { @override Widget build(BuildContext context) { /// 重要 final count = context.select((CountNotifier2 countNotifier2) => countNotifier2.count); return Scaffold( appBar: AppBar( title: Text("InheritedContextExample"), ), /// select body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(count.toString(), style: TextStyle( color: Colors.red, fontSize: 50 ), ), Padding( padding: EdgeInsets.only(top: 20), child: ElevatedButton( onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(), child: Text("点击加1"), ), ) ], ), ), ); } }
Summarize
Flutter
It provides us with a variety of ways to read values. Above we use and analyze and compare the four categories of consumers. You can use the corresponding method according to your actual application scenario.