Article series
Flutter Provider state management---introduction, class diagram analysis, basic use
Flutter Provider State Management---Analysis of the Use of Eight Providers
Flutter Provider State Management---Analysis of four types of consumer usage
Flutter Provider state management---MVVM architecture combat
Video series
Flutter Provider state management---introduction, class diagram analysis, basic use
Flutter Provider State Management---Analysis of the Use of Eight Providers
Flutter Provider State Management---Analysis of four types of consumer usage
Flutter Provider state management---MVVM architecture combat
Source code warehouse address
Preface
In the previous article, we gave Provider
and used corresponding cases to illustrate their differences. In this section, let’s talk about Provider
. If you want to optimize your project structure And their use difference.
Provider.of
Provider.of<T>(context)
is Provider
. When we use this method to get the value, it will return the nearest T
type 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 have defined a CountNotifier1
, and all the following sample codes will demonstrate around this model
import 'package:flutter/material.dart';
class CountNotifier1 with ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
Step 2: Application entry settings
return ChangeNotifierProvider(
create: (_) => CountNotifier1(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ConsumerExample(),
),
);
Step 3: Use Provider.of
Here, when reading the value and clicking the button +1, it is obtained and used Provider.of<T>()
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. It says that we are trying to Widget
tree. If you want to fix it, you can change listen
to false
. This problem actually Provider 4.0.2
, the most important Its default behavior is ture
, and 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();
operation result
Consumer
Consumber
just calls Widget
Prvoider.of
, and delegates its construction to the constructor, such as our common Builder
. If your Widget
relies on multiple models, then it also provides Consumer23456
for convenient calls. The case uses Consumer
to modify
Wrap components with Consumer
Which has a builder
constructor, when we body
changed after the re-run can be found below and use Provider.of
result of the same, but there is no need to use the image in Provider.of
that every time you have to write a bunch of repetitive code.
There are three attributes:
- context: current context
- Provider.of<T>(context): model object
- child: child component (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 Consumer
Optimization method 1: Adjust the position of Consumer as much as possible
We found in the above code that the Center
and Column
components are also Consumer
, but these two components do not need to update the state, and every time we build Widget
, we will rebuild the entire body
, so we optimize the code The structure looks 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, 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 will cause all refresh, then it will obviously cause performance degradation, you may think of using multiple Consumer
packages to refresh. The component is solved, but isn’t that Provider
cart before the horse? 06166ae2a19bd0 itself is the robust and code that repeats the code, so at this time we can use Consumer
provide us with the child
parameter, as follows:
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
class and Consumer
similar, just build
call Widget
provide finer control method, the simple point is, Selector
also a consumer, it allows you to define which properties can be prepared 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 I don’t need to rebuild the username , phone number and other components, then Selector
is used to solve this problem, let’s look at an example:
Step 1: Define the model
import 'package:flutter/material.dart';
class UserModel6 with ChangeNotifier {
String name = "Jimi";
int age = 18;
String phone = "18888888888";
void increaseAge() {
age++;
notifyListeners();
}
}
Step 2: Application entry settings
return ChangeNotifierProvider(
create: (_) => UserModel6(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SelectorExample(),
),
);
Step 3: Use Selector for finer control
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("改变年龄"),
),
),
),
),
);
}
}
operation result
InheritedContext
InheritedContext
is Provider
built-in extension BuildContext
, it does not save the reference of the component's own position in the tree, we see Provider.of<CountNotifier1>(context,listen: false)
in the above case, in fact, the of
method is to use Flutter
search the tree and find the Provider
subtype is CountNotifier1
.
Three ways:
- BuildContext.read:
BuildContext.read<CountNotifier1>()
can replaceProvider.of<CountNotifier1>(context,listen: false)
, it will findCountNotifier1
and return it. - BuildContext.watch:
BuildContext.watch<CountNotifier1>()
can replaceProvider.of<CountNotifier1>(context,listen: false)
, it looksread
no different, but withwatch
you do not need to useConsumer
. - BuildContext.select:
BuildContext.select<CountNotifier1>()
can replaceProvider.of<CountNotifier1>(context,listen: false)
, it lookswatch
is no different, but withselect
you do not need to useSelector
.
BuildContext.read
The results of the following two usage methods are the same
Use Provider.of<T>()
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
Use Consumer
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"),
),
/// Consumer 获取值
body: Center(
child: Consumer<CountNotifier2>(
builder: (_, countNotifier2, child) {
return 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"),
),
),
],
);
},
),
),
);
}
}
Use 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
provides us with a variety of ways to read values. Above we have used and analyzed the four categories of consumers. You can use the corresponding method according to your actual application scenarios.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。