preface

For those of you who read this article, a Provider template code generation plug-in is included!

Why did I write this plugin?

Short story, I am not going to write an article parsing Provider source code, must write this framework use sample ah, and then beep source ah! While writing the demo, I felt comfortable creating two or three new files, folders, and mandatory template code. It didn’t take me long. Then it took me hundreds of times longer…

I hope this plug-in can reduce the workload of guys using Provider. Plug-in inside the template code is after MY thoughtful, if you have a better template code, please post it in the comments, I think reasonable, will add to the plug-in.

About Provider source code, if you are not familiar with the design pattern or interface oriented programming, it seems to be quite meng force, basically: meng force tree meng fruit, meng force you and ME under the tree; Provider source code uses a large number of abstract classes, call the parent class constructor, inherit the implementation of assertions, many key function calls, point into the abstract class, must go back several layers to see what the implementation of this abstract class is, see a big ten! There are many traces of design patterns: Observer, Policy, appearance, command, visitor, template, iterator.

I will try my best to make the overall process clear. Related obscure processes will be combined with pictures and texts, and give corresponding small demos

ε=(´ο ‘*))) Alas, I feel emptied by the end of this article…

Whether you use providers or not, I believe that after you finish reading the refresh Mechanism section of this article, you will most likely be impressed by the shining wisdom of this framework.

use

Same old rule. Before we talk about how it works, let’s see how it works

The use of providers differs a little from the Handler and ThreadLocal uses I wrote about in the previous two articles

Provider is a state management framework, write its use may take more space, so the overall length of the article will be longer, please forgive me…

I really do not want to share the length of water praise ah, but also for the convenience of you can refer to the relevant knowledge in an article (please combine with the outline next to the nuggets), also convenient for me to modify and optimize the content of the article at any time…

The plug-in

  • Plug-in lot:provider_template
    • What bugs are encountered in the use, and I hope you can timely give me an issue
  • To install the Flutter Provider, go to the Android Studio Setting, select Plugins, and search for the Flutter Provider. The first one is the one marked with the red box on the page

  • Let’s see how it works

  • If you don’t like the naming, there’s a way to change it; Persistence is also supported
    • Revise it as needed

  • Alt + Enter: You can choose to wrap the Widget in three options (Consumer, Selector, and ChangeNotifierProvider). One click generates troublesome, repetitive, and frequently used widgets!

  • Quick code snippet tip: I wrote three, if the old brothers have other SAO operation, you need to mention PR ah
    • Just add provider_template to this file
    • Enter the provider prefix to prompt

writing

Why use builder in ChangeNotifierProvider? Instead of using child?

  • The context in provider.of (context, listen: false) must be of the ChangeNotifierProvider or its child Widget
  • Increment () method cannot be triggered if ProEasyCounterProvider cannot be found using ProEasyCounterPage context
  • How does it work? After reading the article, you made it
class ProEasyCounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => ProEasyCounterProvider(),
      builder: (context, child) => _buildPage(context),
    );
  }

  Widget _buildPage(BuildContext context) {
    final provider = context.read<ProEasyCounterProvider>();

    return Scaffold(
      appBar: AppBar(title: Text('the Provider - Easy example')),
      body: Center(
        child: Consumer<ProEasyCounterProvider>(
          builder: (context, provider, child) {
            return Text(
              'click on the${provider.count}Time ',
              style: TextStyle(fontSize: 30.0)); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => provider.increment(), child: Icon(Icons.add), ), ); }}class ProEasyCounterProvider extends ChangeNotifier {
  int count = 0;

  voidincrement() { count++; notifyListeners(); }}Copy the code

The method of getting the injected instance is a bit long; So I’m in _buildPage(…) In this method, I’m going to pull it out and assign it to a single variable for later use

Plug-in generation code

Plug-in generated code is divided into two modes: Default and High

The Default mode has two files: View and provider

Advanced mode has three files (High) : View, Provider, and state

All of you are veterans of Flutter and should be familiar with this structure. The State layer is maintained independently of the data layer

In the very complicated submission interface, I even divided the state layer into three structures: jump, submit and show. There are hundreds of variables in one module. It’s too difficult to maintain if you don’t divide them like this

Default: template code in default mode

  • view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'provider.dart';

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => CounterProvider(),
      builder: (context, child) => _buildPage(context),
    );
  }

  Widget _buildPage(BuildContext context) {
    final provider = context.read<CounterProvider>();

    returnContainer(); }}Copy the code
  • provider
import 'package:flutter/material.dart';

class CounterProvider extends ChangeNotifier {}Copy the code

High: template code in advanced mode

  • view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'provider.dart';

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => CounterProvider(),
      builder: (context, child) => _buildPage(context),
    );
  }

  Widget _buildPage(BuildContext context) {
    final provider = context.read<CounterProvider>();

    returnContainer(); }}Copy the code
  • provider
import 'package:flutter/material.dart';

import 'state.dart';

class CounterProvider extends ChangeNotifier {
  final state = CounterState();
}
Copy the code
  • state
class CounterState {

  CounterState() {
    // init some variables}}Copy the code

Front knowledge

The following is the source analysis content of Provider, if you are in a hurry, you can click a “like” (convenient for future reference, funny.jpg), and then have time to calm down and slowly look; I’m afraid you read fast food, read the refresh mechanism that piece, will directly scold the street, this write what play??

Provider refresh mechanism, the relevant process is quite around, I have tried my best to simplify countless we do not need to pay attention to the code, and then step by step with your ideas to go through the right process, the relevant class also gave a lot of instructions, but do not set up the source process mountain road 18 bends, around a ratio! If you don’t look and feel with your heart, you will be very upset…

I’ve helped you through the cone part, and I’ve drawn a detailed diagram of the process, and I’ve worked on it; If you want to know the inner workings of your Provider, now is your time!

Separate use of ChangeNotifier

ValueListenableBuilder and ValueNotifier can be used together. ValueListenableBuilder is also a StatefulWidget. The code is very simple and you can view it yourself

This is not table, here is the use of the most original ChangeNotifier

I’m sure you’ve written a lot of code on your Provider that inherits from ChangeNotifier, but do you know how to use ChangeNotifier alone to control interface changes?

I searched a lot of articles about how to use ChangeNotifier alone, but most of them are about how to use ChangeNotifierProvider in Provider. I was surprised to find a few articles about how to use ChangeNotifier alone. I wonder if there is a separate XxxWidgetBuild for this thing? But! I can’t find it anywhere. It’s cold!

It occurs to me that the TextEditingController in the TextField control uses a ChangeNotifier. I searched through the source code for all kinds of super abstract private variables, and finally found the key code to understand how the TextField uses ChangeNotifier, and why the TextEditingController changes its text value every time. And then the data in the TextField data box changes in time, and you actually end up using setState.

TextField process code will not be posted, if posted, will be quite large: I will write a minimum granularity ChangeNotifier separate demo

  • The TextEditingController actually inherits from ValueNotifier, so take a look at ValueNotifier
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
  ValueNotifier(this._value);
  @override
  T get value => _value;
  T _value;
  set value(T newValue) {
    if (_value == newValue)
      return;
    _value = newValue;
    notifyListeners();
  }

  @override
  String toString() => '${describeIdentity(this)}($value) ';
}
Copy the code

ValueNotifier encapsulates ChangeNotifier

It doesn’t make much difference here, so we’ll use a ChangeNotifier to write a controller effect similar to the one in TextField, where the contents of the control are automatically updated whenever the value in the controller changes

  • Make a controller using ChangeNotifier
class TestNotifierController extends ChangeNotifier {
  String _value = '0';

  String get value => _value;

  set value(String newValue) {
    if (_value == newValue) return; _value = newValue; notifyListeners(); }}Copy the code
  • Widgets with this controller
    • OK, that’s it. Change the controller’s data and the Widget will refresh automatically
    • I have compressed the granularity of the function to a very small size, so that you can read it more easily
class TestNotifierWidget extends StatefulWidget {
  const TestNotifierWidget({
    Key? key,
    this.controller,
  }) : super(key: key);

  final TestNotifierController? controller;

  @override
  _TestNotifierState createState() => _TestNotifierState();
}

class _TestNotifierState extends State<TestNotifierWidget> {
  @override
  void initState() {
    ///When a callback value change is added, the callback content is automatically triggeredwidget.controller? .addListener(_change);super.initState();
  }

  @override
  Widget build(BuildContext context) {
    returnText( widget.controller? .value ??'Initial value is null',
      style: TextStyle(fontSize: 30.0)); }///The triggered callback
  void_change() { setState(() {}); }}Copy the code
  • How to use this control
    • Using the code is simple enough: onPressed changes the controller numeric content, and the TestNotifierWidget automatically refreshes
class TestNotifierPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final controller = TestNotifierController();
    var count = 0;

    return Scaffold(
      appBar: AppBar(title: Text('ChangeNotifier User Demo ')),
      body: Center(
        child: TestNotifierWidget(controller: controller),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.value = 'Numerical variation:${(++count).toString()}'; }, child: Icon(Icons.add), ), ); }}Copy the code
  • Take a look at the renderings

Function Call()

Here says a small knowledge point, the source code inside a large number of the use of this skill, online search, rarely mention this, here a stroke

Every Function has a Call() method

  • The following two calls are equivalent in that they both call the test method
void main(){
    test();

    test.call();
}

void test(){
    print('test');
}
Copy the code

You might be thinking, what’s the use of this, I’ll write another.call?

Let’s take a look at the next little example, and see how this simplifies a lot of our code

  • The Widget is usually packaged with a CallBack
    • There are two custom click callback judgments here
    • If the Function is not implemented, the event will raise a null exception
class TestWidget extends StatelessWidget {
  const TestWidget({
    Key? key,
    this.onTap,
    this.onBack,
  }) : super(key: key);

  final VoidCallback? onTap;
    
  final VoidCallback? onBack;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        if(onTap ! =null) { onTap! (a); }if(onBack ! =null) { onBack! (a); } }, child: Container(), ); }}Copy the code
  • use.call()And then, how can I write that?
    • Can kill the troublesome if null operation!
class TestWidget extends StatelessWidget {
  const TestWidget({
    Key? key,
    this.onTap,
    this.onBack,
  }) : super(key: key);

  final VoidCallback? onTap;
    
  final VoidCallback? onBack;

  @override
  Widget build(BuildContext context) {
    returnGestureDetector( onTap: () { onTap? .call(); onBack? .call(); }, child: Container(), ); }}Copy the code

The refresh mechanism

Provider refresh mechanism is very important, as long as the Provider refresh mechanism is clear, this framework in front of you, will not be mysterious!

In fact, anyone who has seen the ChangeNotifier app knows that this is an observer mode, but the question is: where do you add the listener? Does the added listening logic have a complete initialization link? What is the listening logic? Why triggering the listening logic can cause the corresponding control to refresh?

  • The full link initialization above is a bit of a pain in the ass
    • Source code east a lang hammer west a bar, but also used a lot of abstract classes, want to directly locate the logic, that is impossible, you have to find the implementation of class assignment, to understand the inner workings
    • Do not understand the complete initialization link, the heart is equivalent to the diaphragm, know that he must be initialized, but do not know where he initialized, it is very uncomfortable
    • I will be related to the process again, I hope to help you
  • To understand Provider, one must understand the observer mode: The observer mode is very simple
    • Define a List type, generic as an abstract class, initialize the List
    • And then I’m going to add to this List an implementation instance of this abstract class
    • At some point, iterate through all instances of this List, firing a method on all instances
    • If you combine this idea with reflective annotations, you can greatly expand its use, such as EventBus on Android…

The total process

Inherit the ChangeNotifier class, is passed into the Provider through ChangeNotifierProvider, obviously ChangeNotifierProvider this class is very important, basically can be considered as the main entrance of the framework

The ChangeNotifierProvider backtrace is a very important example of a context class. Many of the key class initialations are related to this context class. Let’s start with this important process.

  • ChangeNotifierProvider
    • There is a _dispose callback, which is defined and the internal logic is to recycle the ChangeNotifier instance
    • This method is assigned to its parent class, ListenableProvider, and then backtraced
class ChangeNotifierProvider<T extends ChangeNotifier? >extends ListenableProvider<T> {
  ChangeNotifierProvider({
    Key? key,
    required Create<T> create,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super( key: key, create: create, dispose: _dispose, lazy: lazy, builder: builder, child: child, ); .static void _dispose(BuildContext context, ChangeNotifier? notifier) {
    notifier?.dispose();
  }
}
Copy the code
  • ListenableProvider
    • There is an _startListening callback, which is extremely important
class ListenableProvider<T extends Listenable? >extends InheritedProvider<T> {
  ListenableProvider({
    Key? key,
    required Create<T> create,
    Dispose<T>? dispose,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super( key: key, startListening: _startListening, create: create, dispose: dispose, lazy: lazy, builder: builder, child: child, ); .staticVoidCallback _startListening(InheritedContext e, Listenable? value,) { value? .addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}
Copy the code
  • InheritedProvider
    • This class is a logical entanglement point: I omitted a lot of code that was irrelevant to the main flow, which would have affected your focus so much that it would have been uncomfortable
    • Don’t need to see his parent here, he is the parent of the SingleChildStatelessWidget, this class is a wrapper for StatelessWidget class, can slightly under the optimized nesting problem, it doesn’t matter
    • Take a look at the _InheritedProviderScope class in the buildWithChild (think of the StatelessWidget’s build method) method to see its source code
class InheritedProvider<T> extends SingleChildStatelessWidget {
  InheritedProvider({
    Key? key,
    Create<T>? create,
    T Function(BuildContext context, T? value)? update,
    UpdateShouldNotify<T>? updateShouldNotify,
    void Function(T value)? debugCheckInvalidValueType,
    StartListening<T>? startListening,
    Dispose<T>? dispose,
    this.builder,
    bool? lazy,
    Widget? child,
  })  : _lazy = lazy,
        _delegate = _CreateInheritedProvider(
          create: create,
          update: update,
          updateShouldNotify: updateShouldNotify,
          debugCheckInvalidValueType: debugCheckInvalidValueType,
          startListening: startListening,
          dispose: dispose,
        ),
        super(key: key, child: child); .final _Delegate<T> _delegate;
  final bool? _lazy;
  finalTransitionBuilder? builder; .@override
  Widget buildWithChild(BuildContext context, Widget? child) {
    ...
    return _InheritedProviderScope<T>(
      owner: this,
      debugType: kDebugMode ? '$runtimeType' : ' ', child: builder ! =null
          ? Builder(
              builder: (context) => builder!(context, child),
            )
          : child!,
    );
  }
}
Copy the code
  • _InheritedProviderScope
    • This InheritedWidget overrides the createElement method, which is always called when building the Widget!
    • To the most important class soon, it is instantiated in the createElement method _InheritedProviderScopeElement class!
class _InheritedProviderScope<T> extends InheritedWidget {
  const _InheritedProviderScope({
    required this.owner,
    required this.debugType,
    required Widget child,
  }) : super(child: child);

  final InheritedProvider<T> owner;
  final String debugType;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }

  @override
  _InheritedProviderScopeElement<T> createElement() {
    return _InheritedProviderScopeElement<T>(this); }}Copy the code
  • _InheritedProviderScopeElement: approach the logic inside the province slightly, logic is too much, look at dizzy
    • To be clear, this class is extremely important! Take a look at what kind of abstract class InheritedContext implements!
    • InheritedContextinheritedBuildContextIn other words, here the author implementsBuildContextAll abstract methods
      • Yes, BuildContext is also an abstract class, so we can implement multiple different implementation classes
      • The internal system only needs a specific period to trigger the corresponding method
      • You can implement your own logic in the corresponding methods, greatly expanding the logic, how to say? A bit of a policy pattern, you can dynamically replace implementation classes
    • _InheritedProviderScopeElement be realized: InheritedContext and BuildContext; BuildContext has many methods that hook into the control lifecycle, such as reassemble, SetState trigger (build, performRebuild), as well as interesting mandatory dependency components refresh (markNeedsNotifyDependents: this is the author of the Provider in InheritedContext abstract method).
abstract class InheritedContext<T> extends BuildContext {
  T get value;

  void markNeedsNotifyDependents();

  bool get hasValue;
}

class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget); .@override
  void mount(Element? parent, dynamic newSlot) {
    ...
  }

  @override
  _InheritedProviderScope<T> get widget => super.widget as _InheritedProviderScope<T>;

  @override
  void reassemble() {
	...
  }

  @override
  void updateDependencies(Element dependent, Object? aspect) {
    ...
  }

  @override
  void notifyDependent(InheritedWidget oldWidget, Element dependent) {
    ...
  }

  @override
  void performRebuild() {
    ...
  }

  @override
  void update(_InheritedProviderScope<T> newWidget) {
    ...
  }

  @override
  void updated(InheritedWidget oldWidget) {
    ...
  }

  @override
  void didChangeDependencies() {
    ...
  }

  @override
  Widget build() {
    ...
  }

  @override
  void unmount() {
    ...
  }

  @override
  bool get hasValue => _delegateState.hasValue;

  @override
  void markNeedsNotifyDependents() {
    ...
  }

  bool _debugSetInheritedLock(bool value) {
    ...
  }

  @override
  T get value => _delegateState.value;

  @override
  InheritedWidget dependOnInheritedElement(
    InheritedElement ancestor, {
    Object? aspect,
  }) {
    ...
  }

  @override
  voiddebugFillProperties(DiagnosticPropertiesBuilder properties) { ... }}Copy the code

This is a five-step backtracking process, and if you don’t take a closer look at the logic in the related classes, you can easily get lost in the super method…

Through the above five steps back, we can conclude that the fact that _InheritedProviderScopeElement (implementation BuildContext) is instantiated, and he had at the time of initialization is invoked, the corresponding, the inside the corresponding period can also be normal trigger! So before looking at the source of a lot of my problems, easy to solve!

  • graphic
    • There are too many levels of backtracking and too many inheritances and implementations
    • After reading, the brain may not have what impression, so draw a flow chart here, you can refer to the comparison

Add to monitor

One of the things that’s really important about this whole refresh mechanism is, what happens inside the class that we pass in from Create?

class ProEasyCounterPage extends StatelessWidget {
  final provider = ProEasyCounterProvider();

  @override
  Widget build(BuildContext context) {
    returnChangeNotifierProvider( create: (BuildContext context) => provider, child: Container(), ); }}Copy the code

Without looking at the source code, I can tell that the passed XxxProvider instance must use its own addListener method!

But looking for the addListener method really makes me feel closed. Before, BECAUSE I didn’t comb through the general process, I was not clear about its initialization link. When I found the addListener method, I was very doubtful whether I found it correctly and whether there was an addListener method elsewhere. Later no way, on the Provider source download down (before the direct project inside the point Provider plug-in source code to see), global search addListener method, exclude all test classes used, and then concluded that I found the right, the whole add listening link is smooth!

Let’s take you over the source code as a whole

Guys, I’m gonna go around!!

The circulation

  • ChangeNotifierProvider
    • Create is a Function that returns an instance of the Inherited ChangeNotifier class
    • It’s important to remember where the create variable goes, where T is the key class that inherits the ChangeNotifier class
    • Added _dispose method, passed to the parent class
    • Create where super goes to its parent, so let’s go back to the parent
typedef Create<T> = T Function(BuildContext context);

class ChangeNotifierProvider<T extends ChangeNotifier? >extends ListenableProvider<T> {
  ChangeNotifierProvider({
    Key? key,
    required Create<T> create,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super( key: key, create: create, dispose: _dispose, lazy: lazy, builder: builder, child: child, ); .static void _dispose(BuildContext context, ChangeNotifier? notifier) {
    notifier?.dispose();
  }
}
Copy the code
  • ListenableProvider
    • Here we give the create instance super to the parent class
    • Add a _startListening method, also for the parent class
class ListenableProvider<T extends Listenable? >extends InheritedProvider<T> {
  ListenableProvider({
    Key? key,
    required Create<T> create,
    Dispose<T>? dispose,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super( key: key, startListening: _startListening, create: create, dispose: dispose, lazy: lazy, builder: builder, child: child, ); .staticVoidCallback _startListening(InheritedContext e, Listenable? value,) { value? .addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}
Copy the code
  • InheritedProvider
    • This place is a little different than usual
    • Create, dispose, and startListening are passed to _CreateInheritedProvider
    • Look at _CreateInheritedProvider
class InheritedProvider<T> extends SingleChildStatelessWidget {
  InheritedProvider({
    Key? key,
    Create<T>? create,
    T Function(BuildContext context, T? value)? update,
    UpdateShouldNotify<T>? updateShouldNotify,
    void Function(T value)? debugCheckInvalidValueType,
    StartListening<T>? startListening,
    Dispose<T>? dispose,
    this.builder,
    bool? lazy,
    Widget? child,
  })  : _lazy = lazy,
        _delegate = _CreateInheritedProvider(
          create: create,
          update: update,
          updateShouldNotify: updateShouldNotify,
          debugCheckInvalidValueType: debugCheckInvalidValueType,
          startListening: startListening,
          dispose: dispose,
        ),
        super(key: key, child: child); . }Copy the code
  • The process here

_CreateInheritedProvider

This place will be a very important back in the process, back to the _InheritedProviderScopeElement

Next time I need to use this class, I’ll just use this class

  • _CreateInheritedProvider instructions
    • _CreateInheritedProvider inherits the abstract class _Delegate and implements its createState abstract method
    • Arguably, the main logic must be createState method _CreateInheritedProviderState instances
    • Must see _CreateInheritedProvider instance, where call createState method, and then can continue to see _CreateInheritedProviderState logic
@immutable
abstract class _Delegate<T> {
  _DelegateState<T, _Delegate<T>> createState();

  void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
}

class _CreateInheritedProvider<T> extends _Delegate<T> {
  _CreateInheritedProvider({
    this.create,
    this.update,
    UpdateShouldNotify<T>? updateShouldNotify,
    this.debugCheckInvalidValueType,
    this.startListening,
    this.dispose,
  })  : assert(create ! =null|| update ! =null),
        _updateShouldNotify = updateShouldNotify;

  final Create<T>? create;
  final T Function(BuildContext context, T? value)? update;
  final UpdateShouldNotify<T>? _updateShouldNotify;
  final void Function(T value)? debugCheckInvalidValueType;
  final StartListening<T>? startListening;
  final Dispose<T>? dispose;

  @override
  _CreateInheritedProviderState<T> createState() =>
      _CreateInheritedProviderState();
}
Copy the code
  • We need to revisit the InheritedProvider class here
    • One important operation is done here, assigning the _CreateInheritedProvider instance to the _delegate
    • The owner of _InheritedProviderScope in the buildWithChild method accepts an instance of the InheritedProvider itself
    • When you combine these two things, look at the _InheritedProviderScope class
class InheritedProvider<T> extends SingleChildStatelessWidget {
  InheritedProvider({
    Key? key,
    Create<T>? create,
    T Function(BuildContext context, T? value)? update,
    UpdateShouldNotify<T>? updateShouldNotify,
    void Function(T value)? debugCheckInvalidValueType,
    StartListening<T>? startListening,
    Dispose<T>? dispose,
    this.builder,
    bool? lazy,
    Widget? child,
  })  : _lazy = lazy,
        _delegate = _CreateInheritedProvider(
          create: create,
          update: update,
          updateShouldNotify: updateShouldNotify,
          debugCheckInvalidValueType: debugCheckInvalidValueType,
          startListening: startListening,
          dispose: dispose,
        ),
        super(key: key, child: child);
    
  final _Delegate<T> _delegate;
  final bool?_lazy; .@override
  Widget buildWithChild(BuildContext context, Widget? child) {
	,,,
    return _InheritedProviderScope<T>(
      owner: this,
      debugType: kDebugMode ? '$runtimeType' : ' ', child: builder ! =null
          ? Builder(
              builder: (context) => builder!(context, child),
            )
          : child!,
    );
  }
}
Copy the code
  • _InheritedProviderScope
    • The createElement method passes in an instance of _InheritedProviderScope itself
    • The key in _InheritedProviderScopeElement class
class _InheritedProviderScope<T> extends InheritedWidget {
  const _InheritedProviderScope({
    required this.owner,
    required this.debugType,
    required Widget child,
  }) : super(child: child);

  final InheritedProvider<T> owner;
  final String debugType;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }

  @override
  _InheritedProviderScopeElement<T> createElement() {
    return _InheritedProviderScopeElement<T>(this); }}Copy the code
  • _InheritedProviderScopeElement class, I went directly to the key code
    • Do you feel like inheritedWidgets are like statefulwidgets? In fact, both of them end up inheriting widgets without encapsulating the Widget’s builder mode layer. The StatelessWidget encapsulates the builder mode layer, so there is only one layer
    • See the key code below! widget.owner._delegate.createState() … This place calls the createState() method of the _CreateInheritedProvider class
    • PerformRebuild: This callback will be triggered at setState or build time. A judgment is made here that only fires on the first build
    • This ensures that the createState method in the _CreateInheritedProvider class must be called; Then look at the method call _CreateInheritedProviderState class inside
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget); .@override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false; _delegateState = widget.owner._delegate.createState().. element =this;
    }
    super.performRebuild(); }... }Copy the code
  • The process here

_InheritedProviderScopeElement

  • _CreateInheritedProviderState: this class do a lot of things, the main body of a lot of logic in this processing
    • There is a lot of code in this class, only the code we need to pay attention to here, because a lot of code is omitted, from the following body code, the process is clear: Create, startListening, dispose are there
    • But these variables are attached to a delegate, what is the delegate? Take a look at the inherited abstract class _DelegateState
class _CreateInheritedProviderState<T> extends _DelegateState<T._CreateInheritedProvider<T>> {
  VoidCallback? _removeListener;
  bool _didInitValue = false;
  T? _value;
  _CreateInheritedProvider<T>? _previousWidget;

  @override
  T get value {
    ...

    if(! _didInitValue) { _didInitValue =true;
      if(delegate.create ! =null) {
        assert(debugSetInheritedLock(true));
        try{... _value = delegate.create! (element!) ; }finally{... }... }... } element! ._isNotifyDependentsEnabled =false; _removeListener ?? = delegate.startListening? .call(element! , _valueasT); element! ._isNotifyDependentsEnabled =true;
    assert(delegate.startListening == null|| _removeListener ! =null);
    return _value as T;
  }

  @override
  void dispose() {
    super.dispose(); _removeListener? .call();if(_didInitValue) { delegate.dispose? .call(element! , _valueasT); }}... }Copy the code
  • _DelegateState
    • The delegate is through _InheritedProviderScopeElement instance access to capturing the owner then _delegate variables
    • The _delegate variable is assigned to him by instantiating _CreateInheritedProvider in the InheritedProvider class, so go back to it if you don’t believe me
    • Good Gil around!!
abstract class _DelegateState<T.D extends _Delegate<T>> {
  _InheritedProviderScopeElement<T>? element;

  T get value;

  D getdelegate => element! .widget.owner._delegateas D;

  bool get hasValue;

  bool debugSetInheritedLock(bool value) {
    returnelement! ._debugSetInheritedLock(value); }bool willUpdateDelegate(D newDelegate) => false;

  void dispose() {}

  void debugFillProperties(DiagnosticPropertiesBuilder properties) {}

  void build({required bool isBuildFromExternalSources}) {}
}
Copy the code
  • element
    • Now, where is the element variable instantiated? Why do people use it so casually?! Aren’t you afraid it’s empty?
    • Take you directly to _InheritedProviderScopeElement looked inside, the above has been reviewed to the must instantiate the context class process
    • In the performRebuild callback, element is assigned the value element = this when the createState() method is called
    • So in _CreateInheritedProviderState class, element can use this variable, his value is not empty!
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget); .@override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false; _delegateState = widget.owner._delegate.createState().. element =this;
    }
    super.performRebuild(); }... }Copy the code

I don’t know if you have a clear picture of how this works

  • Let’s take a look at the initial link diagram of the 18 bends on this mountain road

_CreateInheritedProviderState

With the above analysis of the element and _delegate is not null, and _delegate can directly access _CreateInheritedProvider this basis, to look at _CreateInheritedProviderState code

  1. Get the process
    1. The create we pass in will be assigned directly to _value, which is now the same XxxProvider instance we passed in!
    2. The startListening callback is also called, with the context instance and the XxxProvider instance passed in as inputs. The callback is also called through.call.
  2. The dispose process
    1. When the startListening method is called, it returns a remove listening Function
    2. The Function that removes the listener is called when dispose is done, and the listener that was added to the XxxProvider is removed
    3. The Dispose method, passed in from the outside, is also executed here
    4. OK! Recycling is all done here!
class _CreateInheritedProviderState<T> extends _DelegateState<T._CreateInheritedProvider<T>> {
  VoidCallback? _removeListener;
  bool _didInitValue = false;
  T? _value;
  _CreateInheritedProvider<T>? _previousWidget;

  @override
  T get value {
    ...

    if(! _didInitValue) { _didInitValue =true;
      if(delegate.create ! =null) {
        assert(debugSetInheritedLock(true));
        try{... _value = delegate.create! (element!) ; }finally{... }... }... } element! ._isNotifyDependentsEnabled =false; _removeListener ?? = delegate.startListening? .call(element! , _valueasT); element! ._isNotifyDependentsEnabled =true;
    assert(delegate.startListening == null|| _removeListener ! =null);
    return _value as T;
  }

  @override
  void dispose() {
    super.dispose(); _removeListener? .call();if(_didInitValue) { delegate.dispose? .call(element! , _valueasT); }}... }Copy the code
  • The key is the startListening callback, so look at the logic
    • _startListening addListener ChangeNotifier is the Listenable implementation class, let’s just think of it as a visitor pattern, so this value is the XxxProvider we passed in from outside
    • Returns a VoidCallback Function that removes the listener logic
class ListenableProvider<T extends Listenable? >extends InheritedProvider<T> {
  ListenableProvider({
    Key? key,
    required Create<T> create,
    Dispose<T>? dispose,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super( key: key, startListening: _startListening, create: create, dispose: dispose, lazy: lazy, builder: builder, child: child, ); .staticVoidCallback _startListening(InheritedContext e, Listenable? value,) { value? .addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}
Copy the code

One last question!!

You need to call _startListening method, you must call _CreateInheritedProviderState class get the value

At which initialization entry do I use this get value?

  • Direct conclusions here, still in _InheritedProviderScopeElement this context class
    • Reassemble: Called when initialization logic for global state or hot overload
    • _delegateState is first assigned an initial value in the performRebuild callback
    • In reassemble callback, _delegateState calls value (_delegatestate.value)
    • So get value must be called at initialization, so the flow is smooth
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget);
    
  late_DelegateState<T, _Delegate<T>> _delegateState; .@override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false; _delegateState = widget.owner._delegate.createState().. element =this;
    }
    super.performRebuild();
  }

  @override
  void reassemble() {
    super.reassemble();

    final value = _delegateState.hasValue ? _delegateState.value : null;
    if (value isReassembleHandler) { value.reassemble(); }}... }Copy the code

conclusion

This completes the analysis of adding listeners, and the associated initialization and invocation links

  • You can complete the flow chart. Take a look

Refresh the logic

The refresh logic is also quite convoluted; This version compares all kinds of debug, all kinds of interrupt points in the framework, and finally makes the process clear! I suddenly feel like I’ve got two arteries!

The author in order to achieve this refresh logic, and the system API to do a lot of interaction, quite wonderful!

I will try my best to show you this wonderful operation!

The trigger

  • ListenableProvider
    • This place logic is simple, added InheritedContext markNeedsNotifyDependents in this context class method
    • That we used in external notifyListeners (), is bound to trigger InheritedContext markNeedsNotifyDependents method in the implementation class
class ListenableProvider<T extends Listenable? >extends InheritedProvider<T> {
  ListenableProvider({
    Key? key,
    required Create<T> create,
    Dispose<T>? dispose,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super( key: key, startListening: _startListening, create: create, dispose: dispose, lazy: lazy, builder: builder, child: child, ); .staticVoidCallback _startListening(InheritedContext e, Listenable? value,) { value? .addListener(e.markNeedsNotifyDependents);return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}
Copy the code
  • _InheritedProviderScopeElement: _InheritedProviderScopeElement is InheritedContext implementation class
    • Still want to come to this class, only retained and markNeedsNotifyDependents relevant code
    • MarkNeedsNotifyDependents callback function, on the whole: will force depends on the rebuilt T widget
    • It doesn’t make sense to be so general, but here’s how he managed to rely on the T widget for reconstruction! On second thought, the application of observer mode…
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget); .@override
  void markNeedsNotifyDependents() {
    if(! _isNotifyDependentsEnabled) {return;
    }

    markNeedsBuild();
    _shouldNotifyDependents = true; }... }Copy the code

The refresh process

Let’s go through the refresh process now!

  • markNeedsNotifyDependents
    • When we use a notifyListeners, we trigger this callback
    • MarkNeedsBuild () is called and _shouldNotifyDependents is set to true
    • The required action, take a look at markNeedsBuild() in action
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget);
    
  bool _shouldNotifyDependents = false; .@override
  void markNeedsNotifyDependents() {
    if(! _isNotifyDependentsEnabled) {return;
    }

    markNeedsBuild();
    _shouldNotifyDependents = true; }... }Copy the code
  • markNeedsBuild
    • _InheritedProviderScopeElement eventually inherited Element or an abstract class, markNeedsBuild () method is an Element
    • The Element class is an abstract class that implements the abstract methods in the BuildContext abstract class, which is important
    • This method has a lot of fancy code written, its main features: Element is called the performRebuild () method, and then trigger a ComponentElement build () method, eventually trigger _InheritedProviderScopeElement build method
    • _InheritedProviderScopeElement extends InheritedElement extends ProxyElement extends ComponentElement extends Element
abstract class Element extends DiagnosticableTree implements BuildContext {...void markNeedsBuild() {
    assert(_lifecycleState ! = _ElementLifecycle.defunct);if(_lifecycleState ! = _ElementLifecycle.active)return;
    assert(owner ! =null);
    assert(_lifecycleState == _ElementLifecycle.active);
    assert(() {
      if(owner! ._debugBuilding) {assert(owner! ._debugCurrentBuildTarget ! =null);
        assert(owner! ._debugStateLocked);if(_debugIsInScope(owner! ._debugCurrentBuildTarget!) )return true;
        if(! _debugAllowIgnoredCallsToMarkNeedsBuild) {final List<DiagnosticsNode> information = <DiagnosticsNode>[
            ErrorSummary('setState() or markNeedsBuild() called during build.'),
            ErrorDescription(
              'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
              'is already in the process of building widgets. A widget can be marked as '
              'needing to be built during the build phase only if one of its ancestors '
              'is currently building. This exception is allowed because the framework '
              'builds parent widgets before children, which means a dirty descendant '
              'will always be built. Otherwise, the framework might not visit this '
              'widget during this build phase.',
            ),
            describeElement(
              'The widget on which setState() or markNeedsBuild() was called was',)];if(owner! ._debugCurrentBuildTarget ! =null) information.add(owner! ._debugCurrentBuildTarget! .describeWidget('The widget which was currently being built when the offending call was made was'));
          throw FlutterError.fromParts(information);
        }
        assert(dirty); // can only get here if we're not in scope, but ignored calls are allowed, and our call would somehow be ignored (since we're already dirty)
      } else if(owner! ._debugStateLocked) {assert(! _debugAllowIgnoredCallsToMarkNeedsBuild);throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.'),
          ErrorDescription(
            'This ${widget.runtimeType} widget cannot be marked as needing to build '
            'because the framework is locked.',
          ),
          describeElement('The widget on which setState() or markNeedsBuild() was called was'),]); }return true; } ());if (dirty)
      return;
    _dirty = true; owner! .scheduleBuildFor(this); }... }Copy the code
  • build
    • If the subclass calls its parent method and the parent calls its own method, the overriding method of the subclass is triggered. To execute the superclass logic
    • The _shouldNotifyDependents is set to true, so the build internal logic executes the notifyClients(widget) method
    • Next, look at the notifyClients(Widget) method
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget);
    
  bool _shouldNotifyDependents = false; .@override
  Widget build() {
    if (widget.owner._lazy == false) {
      value; // this will force the value to be computed.
    }
    _delegateState.build(
      isBuildFromExternalSources: _isBuildFromExternalSources,
    );
    _isBuildFromExternalSources = false;
    if (_shouldNotifyDependents) {
      _shouldNotifyDependents = false;
      notifyClients(widget);
    }
    return super.build(); }... }Copy the code
  • NotifyClients: notifyClients() is an abstract method in the InheritedElement class, and notifyClients() is an abstract method in the ProxyElement class
    1. NotifyClients () is a very, very important methodA “for” loop iterates over all HashMap keys of type _dependents, whose key is an Element
      1. What is Element? It can be represented as an instance of a Widget at a specific location in the tree, and an Element can form a tree (think of every Container that has an Element, and then its child sets other widgets to form a tree).
      2. Element is a tree of widgets and their children. Element is the head node of the tree. The node at that particular location is instantiated, and operations on the instance node at that particular location affect its children
      3. The Widget’s createElement() method instantiates Element
    2. This Element iterates over the _dependents key to fetch an Element or Widget
    3. After fetching the relevant Element instance, she passes in the notifyDependent(oldWidget, dependent) method
    4. Next, we need to look at the notifyDependent(oldWidget, dependent) method logic
class InheritedElement extends ProxyElement {
  final Map<Element.Object?> _dependents = HashMap<Element.Object?> (); .@override
  void notifyClients(InheritedWidget oldWidget) {
    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    for (final Element dependent in _dependents.keys) {
      assert(() {
        // check that it really is our descendant
        Element? ancestor = dependent._parent;
        while(ancestor ! =this&& ancestor ! =null)
          ancestor = ancestor._parent;
        return ancestor == this; } ());// check that it really depends on us
      assert(dependent._dependencies! .contains(this)); notifyDependent(oldWidget, dependent); }}}Copy the code
  • notifyDependent
    • “If (dependencies is _Dependency)” adds a select extension to BuildContext, but I spent a lot of time there, Madeira!
    • Remove the above logic is simple, shouldNotify assignment to true, the last call dependent. DidChangeDependencies ()
    • Dependent remember what that was? Is an Element instance iterated from the parent class
    • This is where the super operation is directly removed, which is also recommended by the system. We can rewrite the notifyDependent method to customize the correlation logic. Because sometimes we need to call dependent. Optional didChangeDependencies ()!
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget); .@override
  void notifyDependent(InheritedWidget oldWidget, Element dependent) {
    final dependencies = getDependencies(dependent);

    if (kDebugMode) {
      ProviderBinding.debugInstance.providerDidChange(_debugId);
    }

    var shouldNotify = false;
    if(dependencies ! =null) {
      if (dependencies is _Dependency<T>) {
        ...
      } else {
        shouldNotify = true; }}if(shouldNotify) { dependent.didChangeDependencies(); }}... }Copy the code
  • didChangeDependencies
    • The logic for didChangeDependencies is pretty simple, it calls markNeedsBuild()
    • The Widget’s build method is eventually called
    • MarkNeedsBuild () involves too much internal logic, the bind class, and the drawing process.
abstract class Element extends DiagnosticableTree implements BuildContext {...@mustCallSuper
  void didChangeDependencies() {
    assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
    assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies')); markNeedsBuild(); }... }Copy the code

Now there is a super twist, this point is the hub of the whole refresh process!

What is the _dependents Element in the InheritedElement map? This is all about calling the markNeedsBuild() method on the key (Element) in the _dependents Map, and finally calling the build method on the Element’s Widget!

Does that make sense? Let’s go out on a wild guess and bet that this Widget is probably a Consumer refresh Widget.

But! But! How on earth did he add such a refresh Widget to an InheritedElement’s _dependents variable? ?

  • Above process diagram

BuildContext

Insert a small knowledge point, this knowledge and the following content related, here first introduced

What is BuildContext?

  • BuildContext
    • Each abstract method above the comment super much, I deleted (account for space), interested in their own source code to see
    • BuildContext is an abstract class, it’s an abstract class that has conventions, and if you want to implement this abstract class, you can implement different methods, but you should not deviate from the scope of functionality described in the abstract method annotations
abstract class BuildContext {
  Widget get widget;

  BuildOwner? get owner;

  bool get debugDoingBuild;

  RenderObject? findRenderObject();

  Size? get size;

  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });

  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });

  InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();

  T? findAncestorWidgetOfExactType<T extends Widget>();

  T? findAncestorStateOfType<T extends State>();

  T? findRootAncestorStateOfType<T extends State>();

  T? findAncestorRenderObjectOfType<T extends RenderObject>();

  void visitAncestorElements(bool Function(Element element) visitor);

  void visitChildElements(ElementVisitor visitor);

  DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});

  DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});

  List<DiagnosticsNode> describeMissingAncestor({ required Type expectedAncestorType });

  DiagnosticsNode describeOwnershipChain(String name);
}
Copy the code
  • StatelessWidget: Take a look at StatelessWidget’s implementation of BuildContext (same with StatefulWidget)
    • The code is super simple. The StatelessWidget abstracts the Build method with a BuildContext input
    • The createElement() method instantiates the StatelessElement class and passes in an instance of the StatelessWidget itself
    • StatelessElement implements the build method for ComponentElement: This method calls the build method in the widget and passes in an instance of itself. The flow passes through, where the StatelessWidget’s Build method is called and the BuildContext implementation class is passed in
    • There’s definitely an implementation of BuildContext in ComponentElement’s parent class, so look up there
abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key? key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}

class StatelessElement extends ComponentElement {
  StatelessElement(StatelessWidget widget) : super(widget);

  @override
  StatelessWidget get widget => super.widget as StatelessWidget;

  @override
  Widget build() => widget.build(this);

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true; rebuild(); }}Copy the code
  • ComponentElement
    • ComponentElement inherits Element, which abstracts the build method. StatelessElement implements this method
    • Look at the Element
abstract class ComponentElement extends Element {...@protectedWidget build(); . }Copy the code
  • Element
    • Element implements BuildContext here, so inherit its subclasses and pass its own instance directly to BuildContext
    • If nothing is done, BuildContext can be understood as: Each Widget has a corresponding Element(generated by createElement()), which is the BuildContext implementation class
abstract class Element extends DiagnosticableTree implements BuildContext {... }Copy the code
  • Widget
    • The Widget abstracts a createElement() method
    • Each Widget subclass should have its own Element
@immutable
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });

  final Key? key;
    
  @protected
  @factory
  ElementcreateElement(); . }Copy the code
  • graphic

  • A few more words about widgets and Elements

Do you know why so many articles say widgets are one-to-many for Elements?

StatelessElement createElement() => StatelessElement(this) Pass an instance of the widget’s own configuration information into XxxElemen(this), and XxxElement can use the widget’s configuration information to generate the corresponding Element instance

Did you notice that? Each Widget has a corresponding Element instance!

Suppose you write the following Widget

Widget _myWidget({Widget child}){
    return Container(width:30, height:30, child:child);
}
Copy the code
  • Let’s use it this way
_myWidget(
    child: Container(
        child: _myWidget(),
    )
)
Copy the code

There is only one Widget configuration, but two elements are generated!

There are still two Widget instances, but at the configuration level, the configuration information for both Widget instances is the same, so it’s one configuration…

Hence the notion that widgets are one-to-many to elements; Anyway, that’s how I understand it, just for your reference…

It may be that these simple examples are naturally generated in the minds of big wigs when they write articles, but for those who have no concept of beauty, it may become: a theorem or established concept

The magic of the Provider of ()

In order to connect the above process, we need a magic magician to come on stage, and we need to invite our king fried: provider.of ()!

How did he add the refresh component to the _dependents variable within the InheritedElement?

  • Provider.of() : Here’s all the logic for this method, with very little code and a lot of functionality!
    1. In the of method, the _inheritedElementOf(context) method retrieves the XxxElement that inherits the InheritedElement closest to the parent of the current Widget
    2. Above is through _inheritedElementOf (context) method of the context. GetElementForInheritedWidgetOfExactType () method to obtain; This method is used to retrieve the XxxElement instance of the Widget that inherits an InheritedElement closest to the Widget, as well as the data stored therein
    3. You might be thinking, what do I have with the XxxElement instance that InheritedElement? Think about it: after we get the XxxElement instance, we can insert the _dependents map variable in its InheritedElement parent. Ecstasy…
    4. How does it do that? It is through this: context. DependOnInheritedElement (inheritedElement)
static T of<T>(BuildContext context, {bool listen = true{...})final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {
        context.dependOnInheritedElement(inheritedElement);
    }
    return inheritedElement.value;
}


static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(BuildContext context) {
    ...

    _InheritedProviderScopeElement<T>? inheritedElement;

    if (context.widget is _InheritedProviderScope<T>) {
        context.visitAncestorElements((parent) {
            inheritedElement = parent.getElementForInheritedWidgetOfExactType<
                _InheritedProviderScope<T>>() as_InheritedProviderScopeElement<T>? ;return false;
        });
    } else {
        inheritedElement = context.getElementForInheritedWidgetOfExactType<
            _InheritedProviderScope<T>>() as_InheritedProviderScopeElement<T>? ; }if (inheritedElement == null) {
        throw ProviderNotFoundException(T, context.widget.runtimeType);
    }

    returninheritedElement! ; }Copy the code

context.getElementForInheritedWidgetOfExactType()

How does this API get the InheritedElement of the parent node?

  • Element: Since Element is the BuildContext implementation class, you can look at the logic directly at Element
    • GetElementForInheritedWidgetOfExactType returns are _inheritedWidgets variables
    • _inheritedWidgets key: is the Widget that needs to be found to inherit the InheritedWidget
    • _updateInheritance: The method is to assign the parent _inheritedWidgets object to the current Element’s _inheritedWidgets variable
    • Where do we give _inheritedWidgets the Map plug value
abstract class Element extends DiagnosticableTree implements BuildContext {...@mustCallSuper
  void mount(Element? parent, dynamic newSlot) {
    ...
    _updateInheritance();
  }
    
  Map<Type, InheritedElement>? _inheritedWidgets; .@override
  InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement? ancestor = _inheritedWidgets == null ? null: _inheritedWidgets! [T];return ancestor;
  }

  void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

 ...
}
Copy the code
  • InheritedElement
    • _inheritedWidgets! [widget.runtimeType] = this
    • [InheritedElement] inserts the _inheritedWidgets variable into the parent Element. [ProxyElement]
    • The parent node assigns the _inheritedWidgets variable level by level to the _inheritedWidgets variable of the child node Element
    • Find the InheritedElement in the parent node, which takes very little time. All you need to do is grab the value from the Map
class InheritedElement extends ProxyElement {...@override
  void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    final Map<Type, InheritedElement>? incomingWidgets = _parent? ._inheritedWidgets;if(incomingWidgets ! =null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>(); _inheritedWidgets! [widget.runtimeType] =this; }... }Copy the code

dependOnInheritedElement

  • BuildContext is a dependOnInheritedElement method that we’ve already talked about
  • Direct look at the most important code ancestor. UpdateDependencies (this aspect) : The XxxElement that we pass in InheritedElement is passed to the updateDependencies method, and it also passes the Element instance of the current Widget to the updateDependencies method
abstract class Element extends DiagnosticableTree implements BuildContext {...@override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
    assert(ancestor ! =null); _dependencies ?? = HashSet<InheritedElement>(); _dependencies! .add(ancestor); ancestor.updateDependencies(this, aspect);
    returnancestor.widget; }... }Copy the code
  • UpdateDependencies: The process runs completely
    • The updateDependencies method calls the setDependencies method
    • The setDependencies method assigns the Element instance of the child Widget to the _dependents variable of the class InheritedElement
class InheritedElement extends ProxyElement {...@protected
  void setDependencies(Element dependent, Object? value) {
    _dependents[dependent] = value;
  }
      
  @protected
  void updateDependencies(Element dependent, Object? aspect) {
    setDependencies(dependent, null); }... }Copy the code
  • Take a look at the diagram: this graph has been tuned for a long time, without planning, the lines can easily cross, vomiting blood…

Custom Builder

As a result of the above analysis, the Provider widget is refreshed at a certain point, which is no longer a mystery…

Let’s build a custom Builder!

  • The custom EasyBuilder control can refresh as well as the Consumer
class EasyBuilder<T> extends StatelessWidget {
  const EasyBuilder(
    this.builder, {
    Key? key,
  }) : super(key: key);

  final Widget Function() builder;

  @override
  Widget build(BuildContext context) {
    Provider.of<T>(context);
    returnbuilder(); }}Copy the code

Write down the complete usage

  • view
class CustomBuilderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => CustomBuilderProvider(),
      builder: (context, child) => _buildPage(context),
    );
  }

  Widget _buildPage(BuildContext context) {
    final provider = context.read<CustomBuilderProvider>();

    return Scaffold(
      appBar: AppBar(title: Text('Provider- Custom Builder Example ')),
      body: Center(
        child: EasyBuilder<CustomBuilderProvider>(
          () => Text(
            'click on the${provider.count}Time ',
            style: TextStyle(fontSize: 30.0), ), ), ), floatingActionButton: FloatingActionButton( onPressed: () => provider.increment(), child: Icon(Icons.add), ), ); }}///Custom Builder
class EasyBuilder<T> extends StatelessWidget {
  const EasyBuilder(
    this.builder, {
    Key? key,
  }) : super(key: key);

  final Widget Function() builder;

  @override
  Widget build(BuildContext context) {
    Provider.of<T>(context);
    returnbuilder(); }}Copy the code
  • provider
class CustomBuilderProvider extends ChangeNotifier {
  int count = 0;

  voidincrement() { count++; notifyListeners(); }}Copy the code
  • rendering

conclusion

The refresh mechanism of the Provider is complete

✿ Remember, (° °) Blue ✿

If there write not proper, please every big guy give advice ~. ~

MultiProvider

The ChangeNotifierProvider class is very important. It is basically the main entry to the framework

  • Here, you may have a question??
    • That’s not right!
    • Instead of writing a global Provider in the main entrance, use a MultiProvider.
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(builder: (BuildContext context, Widget? child) {
      return MultiProvider(child: child, providers: [
        // The Provider created by MultiProvider is globalChangeNotifierProvider.value(value: ProSpanOneProvider()), ]); }); }}Copy the code
  • Here’s the MultiProvider code.
    • Source code so easy, can optimize some layout Nested problems, interested in:
    • If you look at the source code, you can see that MultiProvider is definitely not the main entry, it is just a place to put the Provider on top of the top Widget
class MultiProvider extends Nested {
  MultiProvider({
    Key? key,
    required List<SingleChildWidget> providers,
    Widget? child,
    TransitionBuilder? builder,
  }) : super( key: key, children: providers, child: builder ! =null
              ? Builder(
                  builder: (context) => builder(context, child),
                )
              : child,
        );
}
Copy the code
  • The above is not the main entrance, the children with the ChangeNotifierProvider. Value, to look at the source
    • ChangeNotifierProvider. The value is ChangeNotifierProvider named constructor, In fact ChangeNotifierProvider. The value is to ChangeNotifierProvider using an optimized
class ChangeNotifierProvider<T extends ChangeNotifier? >extends ListenableProvider<T> {
  ChangeNotifierProvider({
    Key? key,
    required Create<T> create,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super(
          key: key,
          create: create,
          dispose: _dispose,
          lazy: lazy,
          builder: builder,
          child: child,
        );

  ChangeNotifierProvider.value({
    Key? key,
    required T value,
    TransitionBuilder? builder,
    Widget? child,
  }) : super.value(
          key: key,
          builder: builder,
          value: value,
          child: child,
        );

  static void _dispose(BuildContext context, ChangeNotifier? notifier) {
    notifier?.dispose();
  }
}
Copy the code
  • Why say ChangeNotifierProvider. The value is a over the use of ChangeNotifierProvider optimization?
    • So if you look at the following two ways, they’re actually equivalent
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(builder: (BuildContext context, Widget? child) {
      return MultiProvider(child: child, providers: [
        / / simplified version
        ChangeNotifierProvider.value(value: ProSpanOneProvider()),
          
        // The effect is the same as aboveChangeNotifierProvider(create: (context) => ProSpanOneProvider()), ]); }); }}Copy the code
  • conclusion
    • So the ChangeNotifierProvider class is very important. It’s basically the main entrance to the Provider
    • A lot of initialization is done by starting with the instantiation of the class

Consumer

Consumer should be our daily, very very commonly used a control, his source code is very simple, the structure is also very clear

Consumer2, Consumer3, Consumer4, Consumer5, Consumer6; I was stunned…

In my humble opinion, you don’t have to, because it makes the Builder parameter very confusing; With Consumer2 to Consumer6, you can use provider.of (context) directly, which may make it easier for later users to read the code more clearly. And with Consumer2 and the like, you have to write the corresponding generic in Consumer and the corresponding parameters in builder, which is about the same as if I were writing provider.of (context) directly…

Consumer2 to Consumer6 are just a few more provider. of(context)…

  • Consumer
    • The structure is very clear, inherited SingleChildStatelessWidget, rewrite the buildWithChild method, returned to the builder function in it
    • Note that this does an operation to pass the child to its parent; And buildWithChild will pass out a child and pass it to the Builder method
class Consumer<T> extends SingleChildStatelessWidget {
  Consumer({
    Key? key,
    required this.builder,
    Widget? child,
  }) : super(key: key, child: child);

  final Widget Function(BuildContext context, T value, Widget? child,) builder;

  @override
  Widget buildWithChild(BuildContext context, Widget? child) {
    returnbuilder( context, Provider.of<T>(context), child, ); }}Copy the code
  • SingleChildStatelessWidget
    • Here a buildWithChild method is abstracted and then called in the Build method
    • Here, we’re passing the context and the child that we passed externally, to the buildWithChild method
    • Ok, the Consumer logic is simple, and that’s about it!
abstract class SingleChildStatelessWidget extends StatelessWidget implements SingleChildWidget {
  const SingleChildStatelessWidget({Key? key, Widget? child})
      : _child = child,
        super(key: key);

  final Widget? _child;

  Widget buildWithChild(BuildContext context, Widget? child);

  @override
  Widget build(BuildContext context) => buildWithChild(context, _child);

  @override
  SingleChildStatelessElement createElement() {
    return SingleChildStatelessElement(this); }}Copy the code

Selector

The Provider also has an important refresh component, the conditional refresh component Selector, so let’s see

  • use
    • I’m using a three-tier structure here, decoupling the state layer
    • Better at complex modules
class ProHighCounterPage extends StatelessWidget {
  final provider = ProHighCounterProvider();

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => provider,
      child: _buildSelector(),
    );
  }

  Widget _buildSelector() {
    return Scaffold(
      appBar: AppBar(title: Text('the Provider - Extended example')),
      body: Center(
        child: Selector(
          shouldRebuild: (previous, next) {
            return true;
          },
          selector: (context, provider) => provider,
          builder: (_, __, ___) {
            return Text('click on the${provider.state.count}Time ',
                style: TextStyle(fontSize: 30.0)); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => provider.increment(), child: Icon(Icons.add), ), ); }}class ProHighCounterProvider extends ChangeNotifier {
  final state = ProExtendedCounterState();

  voidincrement() { state.count++; notifyListeners(); }}class ProExtendedCounterState {
  late int count;

  ProExtendedCounterState() {
    count = 0; }}Copy the code
  • Selector
    • It looks like Selector0 is the key, so let’s look at Selector0
class Selector<A.S> extends Selector0<S> {
  Selector({
    Key? key,
    required ValueWidgetBuilder<S> builder,
    required S Function(BuildContext, A) selector,
    ShouldRebuild<S>? shouldRebuild,
    Widget? child,
  }) : super(
          key: key,
          shouldRebuild: shouldRebuild,
          builder: builder,
          selector: (context) => selector(context, Provider.of(context)),
          child: child,
        );
}
Copy the code

Selector0: Main logic in _Selector0State, the following three values are true to cause the Builder method to perform the refresh operation

  1. oldWidget ! = Widget: If the selector’s parent is refreshed, the Builder is refreshed as well
  2. widget. _shouldRebuild ! = null && widget. _shouldRebuild! (value as T, selected) : shouldRebuild callback is implemented and returns true
  3. The selector callback returns XxxProvider, which is completely changed (e.g., re-instantiated assignment); And the shouldRebuild callback is not implemented
class Selector0<T> extends SingleChildStatefulWidget {
  Selector0({
    Key? key,
    required this.builder,
    required this.selector,
    ShouldRebuild<T>? shouldRebuild,
    Widget? child,
  })  : _shouldRebuild = shouldRebuild,
        super(key: key, child: child);

  final ValueWidgetBuilder<T> builder;

  final T Function(BuildContext) selector;

  final ShouldRebuild<T>? _shouldRebuild;

  @override
  _Selector0State<T> createState() => _Selector0State<T>();
}

class _Selector0State<T> extends SingleChildState<Selector0<T>> {
  T? value;
  Widget? cache;
  Widget? oldWidget;

  @override
  Widget buildWithChild(BuildContext context, Widget? child) {
    final selected = widget.selector(context);

    finalshouldInvalidateCache = oldWidget ! = widget || (widget._shouldRebuild ! =null&& widget._shouldRebuild! (valueas T, selected)) ||
        (widget._shouldRebuild == null&&!const DeepCollectionEquality().equals(value, selected));
    if (shouldInvalidateCache) {
      value = selected;
      oldWidget = widget;
      cache = widget.builder(
        context,
        selected,
        child,
      );
    }
    returncache! ; }@override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<T>('value', value)); }}Copy the code

Hand rub a state management framework

After seeing the principle of Provider, do you feel that you have thousands of gaps in your heart and thousands of talents that cannot be released? Let’s let it all out!

To apply what we learn, let’s refresh the mechanism according to Provider, hand rub a state management framework…

Hand rubbing the frame is called EasyP (Bloc and GetX should follow; EasyC, EasyX, easy…) , take the first letter of Provider

Hand rub state frame

The hand-rubbing framework simplifies a lot, but retains the original Provider refresh mechanism!

  • ChangeNotifierEasyP: The ChangeNotifierProvider analogy
    • Much of the code has been streamlined, preserving only the essence of the provider’s refresh mechanism
    • I will not explain the code, the above refresh mechanism if you understand, the following code is easy to understand; If you do not understand, I explain the following code is useless ah…
class ChangeNotifierEasyP<T extends ChangeNotifier> extends StatelessWidget {
  ChangeNotifierEasyP({
    Key? key,
    required this.create,
    this.builder,
    this.child,
  }) : super(key: key);

  final T Function(BuildContext context) create;

  final Widget Function(BuildContext context)? builder;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    assert( builder ! =null|| child ! =null.'$runtimeType  must specify a child',);returnEasyPInherited( create: create, child: builder ! =null
          ? Builder(builder: (context) => builder!(context))
          : child!,
    );
  }
}

class EasyPInherited<T extends ChangeNotifier> extends InheritedWidget {
  EasyPInherited({
    Key? key,
    required Widget child,
    required this.create,
  }) : super(key: key, child: child);

  final T Function(BuildContext context) create;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => false;

  @override
  InheritedElement createElement() => EasyPInheritedElement(this);
}

class EasyPInheritedElement<T extends ChangeNotifier> extends InheritedElement {
  EasyPInheritedElement(EasyPInherited<T> widget) : super(widget);

  bool _firstBuild = true;
  bool _shouldNotify = false;
  late T _value;
  late void Function() _callBack;

  T get value => _value;

  @override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false;
      _value = (widget as EasyPInherited<T>).create(this);

      _value.addListener(_callBack = () {
        // Handles the refresh logic, where notifyClients cannot be called directly
        // causes owner! ._DEBUgCurrentBuildTarget is null, triggering the predicate condition and cannot be executed backwards
        _shouldNotify = true;
        markNeedsBuild();
      });
    }

    super.performRebuild();
  }

  @override
  Widget build() {
    if (_shouldNotify) {
      _shouldNotify = false;
      notifyClients(widget);
    }
    return super.build();
  }

  @override
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    // Refresh the added listener child Element, not the super
    dependent.markNeedsBuild();
    // super.notifyDependent(oldWidget, dependent);
  }

  @override
  void unmount() {
    _value.removeListener(_callBack);
    _value.dispose();
    super.unmount(); }}Copy the code
  • EasyP: Provider class analogous to Provider
class EasyP {
  /// Get the EasyP instance
  /// [[email protected]] [[email protected]
  static T of<T extends ChangeNotifier>(BuildContext context) {
    return _getInheritedElement<T>(context).value;
  }

  /// Register listening controls
  static T register<T extends ChangeNotifier>(BuildContext context) {
    var element = _getInheritedElement<T>(context);
    context.dependOnInheritedElement(element);
    return element.value;
  }

  /// Gets the InheritedElement closest to the current Element<T>The components of the
  static EasyPInheritedElement<T>
      _getInheritedElement<T extends ChangeNotifier>(BuildContext context) {
    var inheritedElement = context
            .getElementForInheritedWidgetOfExactType<EasyPInherited<T>>()
        asEasyPInheritedElement<T>? ;if (inheritedElement == null) {
      throw EasyPNotFoundException(T);
    }

    returninheritedElement; }}class EasyPNotFoundException implements Exception {
  EasyPNotFoundException(this.valueType);

  final Type valueType;

  @override
  String toString() => 'Error: Could not find the EasyP<$valueType> ';
}
Copy the code
  • Build: A complete build class will do
class EasyPBuilder<T extends ChangeNotifier> extends StatelessWidget {
  const EasyPBuilder(
    this.builder, {
    Key? key,
  }) : super(key: key);

  final Widget Function() builder;

  @override
  Widget build(BuildContext context) {
    EasyP.register<T>(context);
    returnbuilder(); }}Copy the code

Once you’re done, these three classes can perform the same partial refresh functionality as the Provider!

The refresh mechanism is exactly the same and there is absolutely no bluffing!

Here’s how to use it.

use

The usage is basically the same as Provider…

  • view
class CounterEasyPPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierEasyP(
      create: (BuildContext context) => CounterEasyP(),
      builder: (context) => _buildPage(context),
    );
  }

  Widget _buildPage(BuildContext context) {
    final easyP = EasyP.of<CounterEasyP>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Custom State Management Framework -EasyP Paradigm')),
      body: Center(
        child: EasyPBuilder<CounterEasyP>(() {
          return Text(
            'click on the${easyP.count}Time ',
            style: TextStyle(fontSize: 30.0)); }), ), floatingActionButton: FloatingActionButton( onPressed: () => easyP.increment(), child: Icon(Icons.add), ), ); }}Copy the code
  • easyP
class CounterEasyP extends ChangeNotifier {
  int count = 0;

  voidincrement() { count++; notifyListeners(); }}Copy the code
  • Effect:Experience the
    • If the page doesn’t open, you may need to clear your browser cache

Global EasyP

  • Global is also possible, directly put the ChangeNotifierEasyP class set in the main entrance, the code will not paste, to show you the effect picture

conclusion

If there are beautiful boy company, do not want to use a third party state management framework, you can refer to the Provider refresh mechanism, lift a state management framework out! I have lifted a minimalist version above, painting dragon painting tiger is difficult to draw bone, above I roughly put his skeleton whole; Use your ingenuity if you need it, copy and fill him with flesh and blood…

If you look at the refresh mechanism of the Provider, you will see that the Provider state framework consumes very little resources. It only uses the ChangeNotifier, which is the basic Callback. How many resources does this use? The refresh logic is all about calling the inherent APIS of Flutte’s framework layer… So don’t worry about it at all, how much resources it will take up is almost negligible!

The last

A secret

Writing the whole article, I suddenly feel that I have mastered a martial arts secret book! I know how to write high-end, elegant, sophisticated projects!

I’m going to teach you right now…

  • Be sure to use the idea of interface oriented programming first!
    • If you want to be very profound, profound themselves are difficult to understand, that direct abuse of this kind of thought is stable!
  • Use a variety of design patterns, don’t talk to me about easy to use, old man write code, is a design pattern shuttle, whether it is appropriate or not, all the above
    • Be sure to use both command mode and visitor mode to make your function input parameters highly extensible and difficult to read by others and yourself
    • If else internal logic is discarded directly
    • Whether the internal state is closed or not, the state mode directly forces the closed loop
    • Use less for, more List traversal, in case people don’t understand your carefully, be sure to note: iterator mode
    • Appearance mode, generally is to do a layer of appearance, let’s directly make two layers, three layers of appearance class! Proxy mode five layer proxy class start!
    • Whether objects or variables are used only once or not, we cache them all to carry the idea of the meta-sharing pattern to the end
    • Transformation is unpredictable is the bridge mode, generally two dimensions bridge, we directly 9 dimensions, as the saying goes, 99 81 difficult, not around you, is around yourself! Hair or life, only one can live!
    • All classes and classes must not be strongly coupled, there must be an intermediary class bridge, others want to spray you; You confidently back, light say: “Dimeter law, understand.”
  • Most importantly, use the Framework layer’s callbacks
    • Whether we understand the system callback or not, we code it in there, pretending to understand it
    • The most critical time, the system abstract class to inherit, write more of their own abstract methods, do not write notes, or later to understand their own, how to do?

That’s just a joke

Do not enter Provider, Provider related ideas with a degree of relaxation, he abstracted the class, in fact, in many different implementation classes, greatly increased the expansion; And the methods it abstracts from the system context classes it inherits are annotated in great detail.

From the Provider source code, can see the Provider author is absolutely a master, must have enough understanding of the framework layer, to write such a wonderful refresh mechanism!

This is an excellent framework!

Why am I writing these jokes? ε=(´ο ‘*))) Alas, one practises hands, another scratches his head…

The relevant address

  • The Github address of Demo in this article is flutter_use
  • Web:Cnad666. Making. IO/flutter_use…
    • If the provider function button is not visible, you may need to clear your browser cache
  • Windows:Windows platform installation package
    • Password: xdd666

series

  • Handler those things (swastika graphic)
  • ThreadLocal’s Fantastic Ideas (swastika)