当前位置:网站首页>Flutter riverpod is comprehensively and deeply analyzed. Why is it officially recommended?
Flutter riverpod is comprehensively and deeply analyzed. Why is it officially recommended?
2022-07-07 07:30:00 【Cover code rice, no code】
With Flutter The development of , Over the years Flutter The state management framework on is as follows “ Spring up ” Like an endless stream , and The most officially recommended state management framework in the past year is undoubtedly Riverpod
, Even more than Provider
, in fact Riverpod
Officials also call themselves “Provider
, But different ”.
Provider
In its own words, it is “InheritedWidget
Encapsulation , But simpler and more reusable .” , andRiverpod
Is in theProvider
New possibilities are reconstructed on the basis of .
The comparison of the state management framework in the past year can be seen 《2021 Year of Flutter State management : How to choose ?》 , This article mainly takes you to dissect RiverPod
How to realize the inside of , Understand how it works , And how to do better than Provider
Less templates and no dependencies BuildContext
.
Preface
if Riverpod
What is the most obvious feature , That is, external independence BuildContext
( In fact, it is another form of dependence ), Because you don't rely on BuildContext
, Therefore, it can be relatively simple to achieve the following effects :
That is to say Riverpod
Medium Provider
You can write it as a whole at will , And don't rely on BuildContext
To write the business logic we need .
️ Make a statement in advance , Here and later
Provider
, And third party librariesprovider
It doesn't matter. .
that Riverpod
How to realize it internally ? Next, let's explore Riverpod
Implementation principle of .
Riverpod
The implementation of is relatively complex , So I still look down patiently , Because this article is a step-by-step analysis , So if the process of watching is a little confused, you can ignore it first , After reading the whole article and then turning back, it may become clearer .
from ProviderScope Start
stay Flutter As long as state management is used in , It must be unavoidable InheritedWidget
, Riverpod It's the same in it , stay Riverpod There will be one ProviderScope
, Generally, you only need to register a top-level ProviderScope
.
If for InheritedWidget There is still doubt. , You can watch me dig gold :《 Fully understand State And Provider》
Let's start with an example , As shown in the figure below , Is a simple example of the official , You can see here :
- Nest a top
ProviderScope
; - Created a global
StateProvider
; - Use
ConsumerWidget
Ofref
To createcounterProvider
Conductread
To read State , Getint
Value ; - Use another
Consumer
Ofref
To createcounterProvider
Conductwatch
, So as to read the... After each changeint
value ;
A very simple example , You can see that there is no of(context)
, And the overall situation counterProvider
The data in , You can go through ref
Conduct read/watch, And read and update correctly .
So how is this achieved ?
counterProvider
How is it injected intoProviderScope
Inside ? Why didn't you seecontext
? With these questions, we continue to explore .
First let's look at ProviderScope
, It is the only top InheritedWidget
, therefore counterProvider
It must be stored here :
stay RiverPod in ,
ProviderScope
The biggest function is to provide aProviderContainer
.
More specifically , It is through internal nesting UncontrolledProviderScope
Provide , So here we can know :ProviderScope
State sharing can be provided down , Because there's one inside it InheritedWidget
, And the main thing to share is ProviderContainer
This class .
So first you can guess : We define various Providers, Like the one above counterProvider
, Are saved to ProviderContainer
in , Then share down .
In fact, the official for
ProviderContainer
The definition of : It's used to keep all kinds of Providers Of State , And support override Some special Providers act .
ProviderContainer
Here's a new class , It's called ProviderContainer
, In fact, it is generally used RiverPod You don't need to know it , Because you can't directly operate and use it , But you use RiverPod Every behavior of involves its implementation , for example :
ref.read
It will be neededResult read<Result>
;ref.watch
It will be neededProviderSubscription<State> listen<State>
;ref.refresh
It will be neededCreated refresh<Created>
Even all kinds of Provider
The saving and reading of are also related to it , So it acts as a pair of various Provider
Internal management class of , Realized RiverPod Some key logic in .
“Provider” and “Element”
Then we know ProviderScope
Share down ProviderContainer
after ,Provider
How does it work ? Why? ref.watch
/ ref.read
You can read it Provider
In the value of the ?
Continue with the previous code , It just defines StateProvider
, And used ref.watch
, Why can I read the inside state
value ?
First StateProvider
It's a special one Provider
, There is another one inside it called _NotifierProvider
It has realized a layer of transformation , So let's start with the most basic Provider
Class as the analysis object .
Basically all kinds of similar Provider
All are ProviderBase
Subclasses of , So let's first analyze ProviderBase
.
stay RiverPod Inside , Every ProviderBase
All subclasses of have their corresponding ProviderElementBase
Subclass implementation , For example, the previous code uses StateProvider
yes ProviderBase
And so on , It also has corresponding StateProviderElement
yes ProviderElementBase
Subclasses of ;
therefore RiverPod There is basically every “Provider” There will be one of their own “Element” .
️ there “Element” No Flutter Three trees in the concept
Element
, It is RiverPod inRef
Subclass of object .Ref
Mainly provide RiverPod Internal “Provider” Interface for interaction between , And provide some abstract life cycle methods , So it is RiverPod Unique in “Element” Company .
that “Provider” and “Element” What is the role of ?
First , In the above example, we structure StateProvider
When it came in (ref) => 0
, In fact, that is Create<State, StateProviderRef<State>>
function , Let's start from this Create
Function as an entry to explore .
Create<T, R extends Ref> = T Function(R ref)
RiverPod In the building “Provider” Will pass in a Create
function , In this function, we will write some required business logic , such as counterProvider
Inside ()=> 0
Is to return a int
by 0 Value , More importantly, it is decided State
The type of .
If you add <int>
It's even more obvious , In fact, what we have been saying before State
It's a generic , And we define “Provider” You need to define this generic State
The type of , Like here int
.
Return to ordinary Provider
Call to , We introduced Create
function , In fact, in the ProviderElementBase
Is called to execute .
As shown in the figure above , In short, when ProviderElementBase
perform “setState” when , Will call Create
function , Thus, the execution obtains the generic type we defined State
, obtain Result
Then notify and update UI.
️ there “setState” Neither Flutter Framework Inside
setState
, It is RiverPod The first one within oneself “setState” function , and Flutter In the frameState
irrelevant .
So each “Provider” Will have their own “Element” , And build “Provider” Time is imported Create
The function will be in the “Element” Pass inside setState
Calls to perform .
“Element” Inside setState
Mainly through new newState To get a RiverPod Inside Result
object , And then through _notifyListeners
Go get Result
Update to watch
The place of .
Result
The function of is mainly through Result.data
、Result.error
、 map
and requireState
Wait to provide the implementation results , Generally, the status is through requireState
obtain , Specific in RiverPod Embodied in :
We call
read()
when , In fact, they all callelement.readSelf();
, That is to return torequireState
( In fact, it is generally our genericState
) .
Is it a bit messy ?
Simply understand is : build “Provider” after , “Element” Will execute setState(_provider.create(this));
Call our incoming Create
function , And put “Element” Do it yourself ref
Incoming in , So we use ref
In fact, that is ProviderElementBase
.
therefore RiverPod There is a reason for the name in , there “Provider” and “Element” The relationship is very Flutter in
Widget
andElement
The visual sense .
Step by step :
- structure Provider We passed in a
Create
function ; Create
The function will beProviderElementBase
InsidesetState
The call , Get oneReuslt
;Reuslt
InternalrequireState
We can useread()
When , Get what we defined GenericState
Value .
WidgetRef
So much has been said before , But I still didn't say StateProvider
How and ProviderScope
Relate together , That is to say “Provider” How and ProviderContainer
Relate together , With what ref.read
You can read State
?
So in the previous code , We use ConsumerWidget
and Consumer
It's all the same thing , And this ref
That's what we've been talking about “Element” , Or rather, ProviderElementBase
.
You can see it in the source code , ConsumerWidget
The logic of is mainly in ConsumerStatefulElement
, and ConsumerStatefulElement
Inherited StatefulElement
, And implemented WidgetRef
Interface .
You can see many familiar figures in front of the above code : ProviderScope
、ProviderContainer
、 WidgetRef
.
First let's look at ProviderScope.containerOf(this)
, Finally we see the familiar BuildContext
Is there any , This method is actually what we used to use of(context)
, But it was put ConsumerStatefulElement
Use , Used to get ProviderScope
Share down ProviderContainer
.
So we see that ,ConsumerWidget
Inside ConsumerStatefulElement
Got it ProviderContainer
, therefore ConsumerStatefulElement
You can call ProviderContainer
Of read/watch .
And then look back ,ConsumerStatefulElement
Realized WidgetRef
Interface , therefore What we use WidgetRef
Namely ConsumerStatefulElement
In itself
That is to say ref.read
Is to perform ConsumerStatefulElement
Of read
, So as to execute to ProviderContainer
Of read
.
So we can sum up : BuildContext
yes Element
, then Element
It's realized again WidgetRef
, So at this time WidgetRef
Namely BuildContext
An alternative .
Don't put Flutter Of
Element
and RiverPod Inside “ProviderElementBase
” Confused. .
therefore WidgetRef
This interface becomes Element
The abstraction of , Replaced the BuildContext
, So this is Riverpod Of “ magic ” One of .
read
So we have sorted it out before ProviderScope
、 Provider
、 ProviderElementBase
、 ProviderContainer
、 ConsumerWidget
(ConsumerStatefulElement
) and WidgetRef
Etc , Then at last we can start to figure it out read
The whole working chain .
We sort out and know After the concept and function of , combination ref.read
To do a process analysis , The whole is :
ConsumerWidget
It'll go through the insideConsumerStatefulElement
Get to the topProviderScope
Shared withinProviderContainer
;- When we go through
ref
callread
/watch
when , It's really just throughConsumerStatefulElement
To callProviderContainer
Internalread
function ;
Then finally ProviderContainer
Internal read
How the function reads State
?
This should be combined with what we have also introduced ProviderElementBase
, in fact ProviderContainer
In execution read
Function is called readProviderElement
.
readProviderElement
As the name suggests, it is through Provider
To get the corresponding Element
, for example :
ref.read(counterProvider),
In general read/watch In short, it's from ProviderContainer
In the use proivder
do key To obtain ProviderElementBase
This “Element”, There is another new object in this process that needs a brief introduction , Namely :_StateReader
.
readProviderElement
One of the keys is to get _StateReader
, stay ProviderContainer
There's one in it _stateReaders
Internal variables , It is used for caching _StateReader
Of Map .
So in ProviderContainer
Inside :
- 1、 First of all, according to
read
When it came inprovider
Build to get a_StateReader
; - 2、 With
provider
by key ,_StateReader
by value Deposit in_stateReaders
This Map, And back to_StateReader
; - 3、 adopt
_StateReader
OfgetElement()
Get or create toProviderElementBase
;
Here's to
ProviderBase
by Key ,_StateReader
by value Deposit in_stateReaders
, It's just a way of “provider” It's deposited inProviderContainer
, That is, andProviderScope
Connect , Since then “provider” andProviderScope
Just bind together .
It's useless to use it on the surface BuildContext
And redundant nesting , let Provider
and ProviderScope
Connect . In addition, you can see here , stay ref.read
when , How to use provider
Build or get ProviderElementBase
.
obtain ProviderElementBase
Remember that we introduced “Provider” and “Element” The part of ?ProviderElementBase
Would call setState
To execute our incoming Create
function , obtain Result
return State
.
You can see , What we got here ProviderElementBase
after return element.readSelf()
, It's actually back requireState
.
Since the whole RiverPod The simplest ref.read
The whole process is completed :
ProviderScope
Share downProviderContainer
;ConsumerWidget
InsideConsumerStatefulElement
adoptBuildContext
ReadProviderContainer
, And realizeWidgetRef
Interface ;adopt
WidgetRef
Interfaceread(provider)
Call toProviderContainer
Insideread
;ProviderContainer
adoptread
Methodicalprovider
Create or getProviderElementBase
ProviderElementBase
Will executeprovider
InsideCreate
function , To getResult
returnState
;
Other watch
,refresh
Similar process , It's just that some specific internal implementation logic is more complex , For example, when refreshing :
adopt
ref.refresh
Method , In fact, the trigger isProviderContainer
Ofrefresh
, Then it will finally pass_buildState
To triggersetState(_provider.create(this))
Implementation .
And from this process analysis , Also see RiverPod How to use without exposure BuildContext
Realize the logic of full line Association .
Additional analysis
The above basically introduces the whole call process , Here is an additional introduction to how to implement some common calls , For example Riverpod There will be many “Element” , such as ProviderElement
、StreamProviderElement
、 FutureProviderElement
Such as these ProviderElementBase
Subclasses of .
We have found that they are not Flutter Inside Element
, It is Riverpod Inside State Company , Used for processing Provider
The state of , such as FutureProviderElement
Is in the ProviderElementBase
Provide a AsyncValue<State>
, Mainly in the FutureProvider
Use in .
AsyncValue
stay RiverPod In normal circumstances create The method definition is as follows :
And in the FutureProvider
Next is one more _listenFuture
, This Function After execution value
It would be AsyncValue<State>
Of State type .
from _listenFuture
In terms of implementation , The inside will be on this future()
perform , Will enter first AsyncValue<State>.loading()
after , according to Future
The result of return decides to return AsyncValue<State>.data
perhaps AsyncValue<State>.error
.
So, for example read
/ watch
when , Returned generics requireState
In fact, it has become AsyncValue<State>
.
And for the AsyncValue
Officials have done some extension
, stay AsyncValueX
On , Among them, acquisition AsyncData
Of data
\ asData
and T value outside , It mainly provides the construction methods of different states mentioned above , such as when
Method :
autoDispose & family
stay Riverpod There should also be a very common one called autoDispose
and family
Static variables for , Almost every Provider
There are , What is it used for ?
for instance , In the previous code, we have a FutureProvider
, We used in autoDispose
:
Actually FutureProvider.autoDispose
Mainly AutoDisposeFutureProvider
, And so on, basically every Provider
All have their own autoDispose
Realization ,family
It's the same thing .
If it's normal Provider
Is inherited AlwaysAliveProviderBase
, that AutoDisposeProvider
Is inherited from AutoDisposeProviderBase
:
It can be seen from the name :
AlwaysAliveProviderBase
Is an active ;AutoDisposeProviderBase
Nature just doesn'tlistened
Destroy when ;
The inside _listeners
、_subscribers
、_dependents
It's all empty time , Of course it has another maintainState
The control state of , By default, it is false
When , You can perform destruction .
Simple understanding is to use “ Incinerate immediately after completion ” .
For example, we introduced the call read
When , Will be called mayNeedDispose
Try to destroy :
Destruction is also called
element.dispose()
And from the_stateReaders
Thismap
Remove and so on .
alike family
The corresponding is ProviderFamily
, Its function is : Use additional parameters to build provider , That is to add a parameter .
For example, the default is :
final tagThemeProvider = Provider<TagTheme>
Can become
final tagThemeProvider2 = Provider.family<TagTheme, Color>
Then you can use additional parameters , stay read
/watch
When :
final questionsCountProvider = Provider.autoDispose((ref) {
return ref
.watch(tagThemeProvider2(Colors.red));
});
The reason why this function can be realized , It depends on its implementation ProviderFamily
, The comparison is average Provider
default create
,ProviderFamily
Yes. :
You can see create
Is the new one Provider
, That is to say family
I'm actually Provider
nesting Provider
.
So from the above example , We used to pass ref.watch(tagThemeProvider);
That's all right. , Because of our tagThemeProvider
The direct is ProviderBase
.
But if you use ref.watch(tagThemeProvider2);
You'll see an error message
The argument type 'ProviderFamily<TagTheme, Color>' can't be assigned to the parameter type 'ProviderListenable<dynamic>'.
Yes , Because this is Provider
nesting Provider
, We first get yes ProviderFamily<TagTheme, Color>
, So we need to change to ref.watch(tagThemeProvider2(Colors.red));
.
adopt tagThemeProvider2(Colors.red)
Execution once becomes what we need ProviderBase
.
that tagThemeProvider2
This ProviderFamily
Why is it implemented like this ? ProviderFamily
Obviously, there is no such constructor .
This involves Dart Characteristics of language , If you are interested, you can see : juejin.cn/post/696836…
First of all, here is a ProviderFamily<TagTheme, Color>
, stay Dart All function types in are Function
Subtypes of , So functions inherently have call
Method .
We execute tagThemeProvider2(Colors.red)
In fact, it's execution ProviderFamily
have to call
Method , Thus, the create
Method , obtain FamilyProvider<State>
,FamilyProvider
That is to say ProviderBase
Subclasses of .
️ Pay attention to some mistakes here , One is
ProviderFamily
, One isFamilyProvider
, We fromProviderFamily
I gotFamilyProvider
, AsProviderBase
toref.watch
.
Last
I haven't written such a long source code analysis for a long time , Unconsciously, it was written in the middle of the night and the early morning , In fact, relatively speaking , Whole Riverpod More complicated , So reading is more troublesome , But it will be relatively more convenient to use , especially period BuildContext
The limitation of , But it also brings ConsumerWidget
Dependence , All the pros and cons can only depend on your own needs , But the whole Riverpod It must be an excellent framework , It's worth a try .
边栏推荐
- Bi she - college student part-time platform system based on SSM
- My ideal software tester development status
- FullGC问题分析及解决办法总结
- transform-origin属性详解
- How do I get the last part of a string- How to get the last part of a string?
- 1089: highest order of factorial
- 计算机服务中缺失MySQL服务
- Readonly read only
- 弹性布局(一)
- Abnova membrane protein lipoprotein technology and category display
猜你喜欢
Special behavior of main function in import statement
Model application of time series analysis - stock price prediction
毕设-基于SSM大学生兼职平台系统
Simple example of ros2 planning system plansys2
Kuboard can't send email and nail alarm problem is solved
Esxi attaching mobile (Mechanical) hard disk detailed tutorial
Sqlmap tutorial (IV) practical skills three: bypass the firewall
Pass parent component to child component: props
transform-origin属性详解
云备份项目
随机推荐
一、Go知识查缺补漏+实战课程笔记 | 青训营笔记
机器人技术创新与实践旧版本大纲
Pass parent component to child component: props
Modify the jupyter notebook file path
A concurrent rule verification implementation
软件验收测试
【云原生】内存数据库如何发挥内存优势
Initial experience of teambiion network disk (Alibaba cloud network disk)
抽絲剝繭C語言(高階)指針的進階
Communication of components
修改Jupyter Notebook文件路径
The currently released SKU (sales specification) information contains words that are suspected to have nothing to do with baby
MIPS uclibc cross compile ffmpeg, support g711a encoding and decoding
Stack Title: nesting depth of valid parentheses
Freeswitch dials extension number source code tracking
Flexible layout (II)
组件的通信
Flexible layout (I)
Jesd204b clock network
Redis data migration