DEV Community

Cover image for The InheritedWidget
Greg Perry
Greg Perry

Posted on

The InheritedWidget

Many are not using it right.

Here, copy this gist, inherited_state_mixin.dart, and you’ll have a mixin that provides you with a properly implemented InheritedWidget to be used with any State class. I’m seeing developers are not implementing their InheritedWidgets quite right and thus not utilizing them to their fullest. This mixin will make that a distant memory.

Show By Example

I’ve written up and stored an example app in the Github repository, inherited_state_mixin_example. Below is a video of that example app starting up. The little blue buttons along the bottom are labeled, ‘New Dogs’, ‘New Cats’, ‘New Birds’, and ‘New Foxes.’

Press a button, and three new images of that chosen animal are downloaded from a public API. Now, this involves an InheritedWidget. As you may know, when an InheritedWidget is called, again and again, any other widgets anywhere in the app that was ‘linked’ to this InheritedWidget as a dependency are also rebuilt — as if their setState() functions were themselves called. Now, that is powerful! Distributed rebuilds of selected widgets in an interface have always been the strongest reason to use InheritedWidgets in my opinion.

Let’s dive right in and show you how to implement this mixin. There are four points of interest in the screenshot below highlighted with my ‘little red arrows.’ We’ll work through them from top to bottom. First of course there’s the mixin, InheritedStateMixin. Slap this mixin onto your State class using the ‘with’ clause, and you’re on your way. Next arrow highlights the function, initInheritedState(). This is in the mixin and is necessary for things to work. It explicitly takes in two things:

  • The type of the InheritedWidget.
    That’s always needed when you’re working with InheritedWidgets.

  • A builder that passes a ‘child’ widget to the InheritedWidget.
    Of course, in the builder, you see the InheritedWidget being called.

Lastly, there’s the function, buildChild(). It’s to be used like any other build() function in a State object — its name is just different because the mixin you’ve attached to the State class is now using the build() function. If you haven’t already guessed, that buildChild() function returns the required ‘child’ widget that needs to be passed to an InheritedWidget. Now in the screenshot below, it may look a little strange returning only,widget.child!, but that’s how the example app is written — just think of it as any other build() function you’d use to implement your app’s interface. That interface now is passed as a ‘child’ widget to an InheritedWidget.

Lastly, in the screenshot below, is the InheritedWidget itself. In this case, it’s local to this library file with a leading underscore. That, of course, is not a requirement — any InheritedWidget anywhere could work for you.

inherit_bird.dart

It’s For The Birds

The class defined in the screenshot above is for the birds. You’ll find there are three additional classes very similar to this one — each with its own dedicated InheritedWidget. Each of them will further be ‘linked’ to yet another three State objects with a different role. These additional State objects are each responsible for displaying an image of a particular animal. In other words, three ‘bird image’ state objects will be dependent on this ‘Bird’ Inherited State object displayed above. When this State object above is called again to rebuild, its InheritedWidget is called again, and so its three ‘image’ State objects will be rebuilt again. This results in three new images of birds every time you press the ‘New Birds’ button. See how that works? You can get a sense of the process while viewing the video above.

Note, the images are coming from the public API's listed below. You can see in the video above that the ‘cat’ API is a lot slower than the others at times. It can have gif files, and they take longer to download, and I suspect it’s one of the more popular API’s.

https://shibe.online/api/birds
https://aws.random.cat/meow
https://dog.ceo/api/breeds/image/random/1
https://randomfox.ca/floof

State Your Inheritance

I see many instances where the InheritedWidget is being used wrong — the app’s interface has an embedded inheritedWidget and yet it’s not being utilized as well as it should. Now that's fine I guess if you’re only using the InhertiedWidget to access some ‘type of object’ previously instantiated up the Widge tree. This was likely the primary capability highlighted to you when you were first introduced to InheritedWidgets. However, that’s such a waste! All it’s doing is accessing some Map object somewhere in the Flutter framework that contains objects of a particular type. See below. Heck! You can implement that yourself! No, the real power is the efficiency InheritedWidgets offers you in repainting only separate parts of the app’s screen instead of building the whole interface all over again. Efficiency and performance should always be a consideration. After all, your app’s likely running on a mobile phone — not on a supercomputer.

used by Theme.**of**() etc.

The Flutter framework

() contains InheritedWidgets and is retrieved by type.

The [Map object]

Sure, working with Flutter, you can see it’s fast. However, in most of your apps, I suspect you’re calling the setState() function of some State object somewhere and it’s rebuilding the whole screen all over again. Again, it’s fast, but it’s expensive. It’s certainly not efficient. In this simple example app that we’re looking at today, when you press the ‘New Birds’ button, for example, only those three areas of the screen displaying birds are getting repainted. It’s not the whole screen getting rebuilt. It’s just those three areas. Now that’s efficient! That’s powerful! That’s using InheritedWidgets the right way.

Below is a quick example of what I’ve been seeing out there when an InheritedWidget is implemented. Now, mind you, this is not real. It’s a variation of an old example app changed for your benefit. In the example, InheritedData, is the InheritedWidget class. However, it’s merely wrapped around the rest of the app?? With every call of the InheritedWidget, because of its placement and the declarative nature of Flutter, the rest of the app is simply being rebuilt regardless of the boolean value in the InheritedWidget’s updateShouldNotify() function. Not very helpful at all in my opinion.

Again, I suspect it’s being done like that just so the InheritedWidget can provide you access to some data object up through the Widget tree. Simply put, there’s more to it than just placing an InhertiedWidget above what makes up the rest of your app. In fact, more specifically, it takes another StatefulWidget with its State object returning the InheritedWidget in its build() function! The ‘child’ widget passed to that InheritedWidget then contains the rest of your app — and so it’s immutable. You’ll see what I mean.

app_counter_inherited.dart

Another State In The Mix

Part of what makes up the mixin I’m presenting today is displayed in the screenshot below. It may be the most important part of this mixin frankly. There, in the screenshot, is the build() function you now don’t have to implement when you place this mixin with your State class. It’s calling a local instance variable called, _inheritedStatefulWidget. From the name, you’d guess correctly that it contains an instance of another StatefulWidget. That StatefulWidget is also displayed in the screenshot below. In turn, that StatefulWidget instantiates a private State object called, _InheritedState, right in its initializer list! It’s a final instance variable — and it’s immutable! This is setting the stage for working with the InheritedWidget properly.

inherited_state_mixin.dart

The next screenshot below is of the start of this mixin. You can see it’s in the initInheritedState() function where that StatefulWidget is instantiated and assigned to the ‘immutable’ instance variable, _inheritedStatefulWidget. You can also see the ‘type’ of InheritedWidget is represented by the capital ‘T’ using generics and is further passed along to that StatefulWidget. Finally, you see the function, buildChild(), that needs to be implemented when using this mixin. This is all coming together now.

inherited_state_mixin.dart

Let’s now return to the example app and continue demonstrating this mixin’s implementation before continuing with its inner workings.

Widgets within Widgets.

The screenshot below is of the examples app’s ‘home page.’ It does look a little odd with its series of cascading calls of ‘Inherit’ classes one after the other ending with a GridView widget. Note, this is just for demonstrational purposes. However, it does represent a typical Futter app in microcosm. After all, typical Flutter apps are just a series of Widgets called one after another with one calling another, are they not?

my_home_page.dart

Each one of those ‘Inherit’ StatefulWidgets has its State object counterpart containing an InheritedWidget. Note, the last StatefulWidget called in this arrangement is the InheritBird StatefulWidget, and we’ve already seen its State object displayed above beside the video. Now, the first thing executed in that screenshot is the expression,con.children, for the GridView’s parameter, children. This expression returns a List object containing those ‘image’ State objects I mentioned earlier. Three of each type of animal for a total of twelve StatefulWidgets will be contained in that List object. Each in that List object has access to its designated public API to get the appropriate animal image. Do you follow me so far?

In fact, let’s take another look at the ‘Bird’ State object below. Doing so, you now realize that its buildChild() function is returning the InheritCat StatefulWidget, and so on. You’ll find when you examine the code yourself that each of these ‘Inherit’ classes is stored in their own library Dart file.

inherit_bird.dart

Now a closer look at its InheritedWidget class, _BirdInherited, reveals a very simple class guaranteed to rebuild any dependencies it may have when it’s called again. That’s because its updateShouldNotify() function always returns true. This is the case with all the other InheritedWidgets as well. See below.

inherit_bird.dart

The State’s Image

Let’s quickly look now at the StatefulWidgets responsible for displaying the individual images and are the very widgets placed in the List object described above. The screenshot below conveys part of the routine used to produce three separate images of a group of animals or birds and randomly place them in the GridView widget. It is there where we’re introduced to the ‘Random’ classes.

home_controller.dart

Looking at the ‘Bird’ version of these classes, you can see it contains the API information necessary to download an image of a bird in this case. Note, that it also takes in an instance of the class, BirdController, by calling the State object’s add() function in its constructor. Keep that class in mind.

random_bird.dart

If you recall, back in at the ‘home page’, the InheritBird StatefulWidget class was being instantiated and associated with its State class, _InheritedBirdState. Below is a screenshot of that State class once again. When you first saw this State class, I directed you to the mixin, InheritedStateMixin, being initialized in that State object’s constructor using the function, initInheritedState(). However, did you notice the other class being instantiated there? One that even takes in that State object as a parameter? It’s the same class that is then later taken in by a ‘Random’ class object: add(BirdController()) . Hence, a link is made between a particular InheritedWidget and its three ‘image’ widgets.

inherit_bird.dart

This is possible with the use of a factory constructor in all four of these ‘Inherit’ Controller objects. In the BirdController for example (see below) using a factory constructor then supplies the same instance of this particular class to the corresponding ‘image’ State object, _RandomBirdState. The reason to make this connection is to form a ‘dependency’ between those ‘image’ widgets and their particular InheritedWidget.

bird_controller.dart

Again, the State object, _RandomBirdState, calls the public API. More specifically, the parent class that it extends, ImageAPIStateMVC, calls the public API. A screenshot of that parent class is below. The ‘Inherit’ Controller taken in with the add() function is then used to make this State object dependent on a particular InheritedWidget. And so, when that ‘Inherit ’ Controller then calls its newAnimals() function, the InheritedWidget is eventually called again — so then this State object is rebuilt again. I know, it’s a little tough to follow here, but run the example app a few times (stepping through the code with your favorite debugger), and you’ll come to see the process.

image_api.dart

Highlighted above is the function, widgetInherited(). It ‘connects’ this State object to an InheritedWidget. Most of you may realize such a connection traditionally involves the dependInheritedWidgetOfExactType() function, and it still does. However, now that’s all taken care of for you by the mixin! The next screenshot is in the mixin, and you can see for yourself its own widgetInherited() function has that very function being called inside.

inherited_state_mixin.dart

Another video below again demonstrates what happens when you press those buttons along the bottom of the screen. A screenshot of those buttons reveals a Controller object calls the appropriate function to update only small selected areas of the screen.

my_home_page.dart

The HomeController is the class object that contains those ‘new’ functions you see highlighted above. It’s displayed in the first screenshot below. Notice those ‘Inherit’ Controller objects with their factory constructors are being utilized once again. This time, they’re calling their ‘newAnimals’ functions. The second screenshot below contains their parent class, InheritController. Notice, the newAnimals() function calls something you’re familiar with by now, the setState() function. It’s calling a State object to rebuild. However, which State object is the question with using this mixin.

home_controller.dart
inherit_controller.dart

Well, that question is quickly answered with one last look at the screenshot of the State class, _InheritBirdState. Remember, the ‘Inherit’ Controller, BirdController, takes in that State object, and so now calling setState() in that Controller will call this State object’s setState() function. Easy peasy.

inherit_bird.dart

Inherit The State

You see, when a State object is using the mixin, InheritedStateMixin, not only has the State object’s build() function now been taken up and used by that mixin, but its setState() function as well. However, in the first screenshot below showing the mixin’s setState() function, you can see it doesn’t call the usual function found in the State class but instead calls the setState() function belonging to the ‘internal’ State object containing the InheritedWidget! Further, it does this through its StatefulWidget, _inheritedStatefulWidget, which has its own setState() function, and that can be seen in the second screenshot below.

inherited_state_mixin.dart
inherited_state_mixin.dart

And so, back in the State class, _InheritedState, we finally see where your interface is returned and displayed to the screen. It is passed to the InheritedWidget using that specified builder parameter, remember? Every time that build() function displayed below is called the ‘child’ widget is presented — unchanged. It has already been defined in a final parameter, child up in the StatefulWidget, remember? The thing is, of course, you’re able to efficiently make small changes within that child widget by calling the mixin’s widgetInherited() function here and there within and throughout that child widget. Perfect. Again, play with this example app in your favorite IDE. Do a walkthrough of the code with its debugger, and you’ll see.

inherited_state_mixin.dart

Pass The Buck

Finally, if you still want to use InheritedWidgets to contain properties or data to be accessed further down the Widget tree, you’re still free to do just that. See below. With that, of course, you can then test if the data has changed in value inside the InheritedWidget’s updateShouldNotify() function — returning true, for example, to update any dependencies if necessary.

app_counter_inherited.dart

Round and Round

By the way, you may be asking yourself where those progress circular indicators are coming from when the example app first starts up. I’ve presented them again in the video below. You’d be right if you guessed they’re CircularProgressIndicator() widgets. The question then is where did they come from? A mixin of course.

Remember, the screenshot of the ImageAPIStateMVC class? See below. It’s the class in the example app responsible for initially connecting and downloading images from those public API’s. Of course, any public API is a rather busy service — particularly the ‘cat’ one. It calls for an asynchronous operation to be performed (maybe even using a FutureBuilder widget). Yes, that’s right. I made another mixin called, FutureBuilderStateMixin. Attach this mixin to your State object, and you’ll have a built-in FutureBuilder. Implementation is pretty much similar to that of the InheritedStateMixin. Makes implementing FutureBuilders in a State class quick and reliable as well.

image_api.dart

You may have noticed in the screenshot that first introduced you to the widgetInherited() function (remember, it links that State object as a dependency to a particular InheritedWidget) that the traditional build() function had again been replaced by another function. This time, it was replaced by a function named, buildWidget(). A pretty descriptive name if I do say so myself. Well, that belongs to this other mixin. That screenshot is back again as the first screenshot below. The second screenshot below is that of the mixin, FutureBuilderStateMixin. And you guessed it, a FutureBuilder is implemented in its build() function. Anyway, that’s for another story.

image_apid.dart
futurebuild_mixin.dart

I’ve found this mixin to be just as versatile as the ‘InheritedWidget’ mixin. Of course, in my own custom framework, my State objects have both a FutureBuilder and an InheritedWidget built right in, but for now, here’s the gist for the second mixin as well: futurebuilder_mixin.dart. Maybe you will take both mixins here today and combine them into one for State objects. If you do, don’t forget to share with the rest of the Flutter community.

Cheers.

Greg Perry

Top comments (0)