“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022”

InheritedWidget is an important functional component of Flutter. It provides a way to share data from top to bottom in the Widget tree. For example, we share data in the InheritedWidget in the root Widget of our application. The data is available in any child Widget of the application! The application Theme and current language Locale in Flutter share information using inheritedWidgets.

InheritedWidget

didChangeDependencies

When we introduced the life cycle of StatefulWidget earlier, we mentioned the didChangeDependencies callback method of the State object, which is called by the Flutter framework when a dependency changes. This “dependency” refers to whether the child Widget uses the InheritedWidget’s data in the parent Widget! If yes, there is a dependency relationship, otherwise there is no dependency relationship. This mechanism allows child components to update themselves by making changes in the InheritedWidget they depend on!

No dependency

class InheritedDemo extends StatefulWidget {
  @override
  _InheritedDemoState createState() => _InheritedDemoState();
}

class _InheritedDemoState extends State<InheritedDemo> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Container(child: TextA(count: _count), width: 60, height: 30, alignment: Alignment.center,),
        ElevatedButton(onPressed: () {
          setState(() {
            _count++;
          });
        }, child: constIcon(Icons.add)) ], ); }}class TextA extends StatelessWidget {
  final int? count;
  TextA({this.count});

  @override
  Widget build(BuildContext context) {
    returnTextB(count: count); }}class TextB extends StatefulWidget {

  final int? count;
  TextB({this.count});

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    returnTextBState(); }}class TextBState extends State<TextB> {

  @override
  void didChangeDependencies() {
    print('didChangeDependencies executes');
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    print('Build executed');
    return Text('${widget.count}'); }}Copy the code

When we click on the InheritedDemo, we change the value of Text on the interface, but the TextA does not display Text. Instead, we pass count to TextB, which then displays Text. At this point, we click the button to see the effect:

We find that the didChangeDependencies method executes once after the project runs and before the build executes, and then we click the button and it doesn’t execute again; That is, there is no dependency between TextB and InheritedDemo;

Dependent relationship

Create an InheritedData InheritedWidget with the following implementation:

  • InheritedDataNeed to integrate fromInheritedWidget;
  • A constructorMust be added torequired Widget childMust be followedsuper(child: child);
  • The child widgetsthroughofMethod to get shared data;
  • Must be rewrittenupdateShouldNotifyMethod, method returnstrueOf the current shared dataclickCountChanges will be notified when they occurThe child widgetsThe dependenceclickCounttheWidgetAt this time,didChangeDependenciesandbuildMethod is called); Otherwise, no notification will be given (only the call will be madebuildMethods);

We modify the InheritedDemo code in the original code as follows:

Uses InheritedData in its build method, puts the Row in the InheritedData child property, and assigns _count to the clickCount property of the InheritedData;

Change the TextB code as follows:

Finally, in the build method of TextB, we reference the InheritedData to assign a value to the Text.

The final running and printing results are as follows:

The didChangeDependencies method executes, indicating that a dependency exists;

What do you do in didChangeDependencies

In general, this method is rarely overridden in child widgets because the Flutter framework also calls the Build () method to rebuild the tree after a dependency change. However, if you need to perform expensive or time-consuming operations after a dependency change, such as a network request, it is best to do so in the didChangeDependencies method. This avoids the need to perform these time-consuming operations every time you build().

Learn more about the InheritedWidget

What if we point to data that references InheritedData in TextB, but don’t want to call TextBState’s didChangeDependencies method when InheritedData changes?

We can modify the of method on InheritedData as follows:

static InheritedData of(BuildContext context) {
  finalInheritedData? result = context.getElementForInheritedWidgetOfExactType<InheritedData>()! .widgetasInheritedData? ;assert(result ! =null.'No InheritedData found in context');
  returnresult! ; }Copy the code

In the of method, we get an InheritedData object instead of:

final InheritedData? result = context.dependOnInheritedWidgetOfExactType<InheritedData>();
Copy the code

Is amended as:

finalInheritedData? result = context.getElementForInheritedWidgetOfExactType<InheritedData>()! .widgetasInheritedData? ;Copy the code

The other calls are not modified, so let’s look at the result:

So what’s the difference between these two methods? Let’s compare the source of the two methods:

@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
  return ancestor;
}
Copy the code
@override
InheritedWidget dependOnInheritedWidgetOfExactType({ Object aspect }) {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
  // The extra part
  if(ancestor ! =null) {
    return dependOnInheritedElement(ancestor, aspect: aspect) as T;
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}
Copy the code

We found dependOnInheritedWidgetOfExactType method call dependOnInheritedElement method, this method source code is as follows:

@override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor ! =null); _dependencies ?? = HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
Copy the code

A dependOnInheritedElement method is used to register a dependency! So when call dependOnInheritedWidgetOfExactType method, InheritedWidget and depend on its child widgets relationships will registration is completed, after when InheritedWidget change, will update component dependencies, That is, the didChangeDependencies method and build method that call the child widgets.

It is important to note that although after will call way to getElementForInheritedWidgetOfExactType method, didChangeDependencies method won’t perform, but the build method still will carry out! This is because when we click the button, the setState method of _InheritedDemoState is called, at which point the entire page is rebuilt. Since TextA and TextB don’t have any caching, they will be rebuilt, so the build methods of TextA and TextB will execute.