当前位置:网站首页>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 .” , and Riverpod Is in the Provider 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 libraries provider 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 Of ref To create counterProvider Conduct read To read State , Get int Value ;
  • Use another Consumer Of ref To create counterProvider Conduct watch , So as to read the... After each change int 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 into ProviderScope Inside ? Why didn't you see context? 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 a ProviderContainer .

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 needed Result read<Result> ;
  • ref.watch It will be needed ProviderSubscription<State> listen<State> ;
  • ref.refresh It will be needed Created 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 in Ref 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 frame State 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.dataResult.errormap 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 call element.readSelf(); , That is to return to requireState ( In fact, it is generally our generic State) .

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 and Element The visual sense .

Step by step :

  • structure Provider We passed in a Create function ;
  • Create The function will be ProviderElementBase Inside setState The call , Get one Reuslt;
  • Reuslt Internal requireState We can use read() When , Get what we defined Generic State 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 : ProviderScopeProviderContainerWidgetRef .

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 ProviderScopeProviderProviderElementBaseProviderContainerConsumerWidgetConsumerStatefulElement) 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 inside ConsumerStatefulElement Get to the top ProviderScope Shared within ProviderContainer ;
  • When we go through ref call read/watch when , It's really just through ConsumerStatefulElement To call ProviderContainer Internal read 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 in provider 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 Of getElement() Get or create to ProviderElementBase;

Here's to ProviderBase by Key , _StateReader by value Deposit in _stateReaders , It's just a way of “provider” It's deposited in ProviderContainer, That is, and ProviderScope Connect , Since then “provider” and ProviderScope 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 down ProviderContainer ;

  • ConsumerWidget Inside ConsumerStatefulElement adopt BuildContext Read ProviderContainer, And realize WidgetRef Interface ;

  • adopt WidgetRef Interface read(provider) Call to ProviderContainer Inside read;

  • ProviderContainer adopt read Methodical provider Create or get ProviderElementBase

  • ProviderElementBase Will execute provider Inside Create function , To get Result return State ;

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 is ProviderContainer Of refresh , Then it will finally pass _buildState To trigger setState(_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 ProviderElementStreamProviderElementFutureProviderElement 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't listened 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 This map 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 is FamilyProvider, We from ProviderFamily I got FamilyProvider, As ProviderBase to ref.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 .

原网站

版权声明
本文为[Cover code rice, no code]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202130658551258.html