InheritedWidget & FutureBuilder
now inside one State object.
--
State objects are a vital part of Flutter. They retain state. However, four years ago when I began working with Flutter, I found them wanting. Thus came along the concept of a ‘State Object Controller’ in the package, mvc_pattern. In that package, all your State objects can have an optional ‘Controller’ object that takes in that State object and takes on all its capabilities. It’s my most popular Flutter package to date.
However, four years later, I felt there was room for improvement. I mean, InheritedWidgets are cool! They trigger the spontaneous rebuilds of ‘dependent’ child widgets distributed throughout your app — it’s a powerful capability. FutureBuilder widgets are also cool! They’re Flutter’s approach to working with asynchronous operations — operations that likely have to be completed before your app can proceed. I use InheriteWidget’s and FutureBuilder widgets all the time with State objects. One day, I said to myself, ‘How about combining them together to be readily available in various combinations?’ ‘It would make for a powerful State object!’, I said to myself.
Guess what I did.
I Like Screenshots. Tap Caption For Gists.
As always, I prefer using screenshots in my articles over gists to show concepts rather than just show code. I find them easier to work with frankly. However, you can click or tap on their captions to see the code in a gist or in Github. Tap or click on the screenshots themselves to zoom in on them for a closer look.
No Moving Pictures, No Social Media
There will be gif files in this article demonstrating aspects of the topic at hand. However, it’s said viewing such gif files is not possible when reading this article on platforms like Instagram or Facebook. They may come out as static pictures or simply blank placeholders. Please, be aware of this and maybe read this article on medium.com or on their own app.
Let’s begin.
The example app presented in this article can be found in the repository, inherited_statemvc_example. When you run it, it presents you with a GridView widget displaying the images from four public APIs (see below). This app is to produce a somewhat random batch of photo images depicting birds, cats, dogs, and foxes. Of course, being individual network operations, they all will take time to complete the downloading of such images. Some will take more time than others. The ‘cats’ API, for example, appears to be particularly popular that offers not only pictures but gif files as well. Hence, its performance is slower than the others in many instances.
https://shibe.online/api/birds
https://aws.random.cat/meow
https://dog.ceo/api/breeds/image/random/1
https://randomfox.ca/floof
Below are three gif files. The last one from left to right is the example app’s complete startup process. You can see there is a number of circular progress indicators spinning round and round. Since this app is running on an Android emulator, it’s the CircularProgressIndicator widget that’s producing these material design circular progress indicators. Of course, when running on an iOS phone, the example app then uses the CupertinoActivityIndicator widget from the Cupertino Library to produce the iOS-style activity indicators instead. After all, Flutter is a cross-platform SDK!
The first two gif files are merely the startup process broken up to highlight the two separate instances where the example app must ‘wait’ before proceeding. The first gif depicts one State object while the second has twelve State objects. Each has its own individual asynchronous operation going on, and so each has its own little FututureBuilder widget built-in ‘waiting’ for its particular operation to complete. Now, that’s cool!
Admittedly, I’m cheating when it comes to the first one. It’s merely for demonstration purposes — there’s no real asynchronous operation going on there. It’s just a duration of 10 seconds counting down before the app can proceed. However, in a real production scenario, this could easily be databases opening up, web services being called, servers being logged into, etc. Such operations can now easily be triggered in these State objects that then quietly wait for completion. Let’s see what this looks like.
Remember now, if you’ve been a long-time follower of my articles, you know I always separate the app’s Interface from its data source and event handling in my projects. That’s why the mvc_pattern package has these ‘State Object Controllers’ after all. A State object deals with the app’s Interface (within its build() function) while its accompanying Controller deals with the ‘business rules’ and any event handling. The benefits of using such a design pattern have been touted for literally decades and so will not be cited here. Just know that ‘the brains’ of this simple app will most likely be found in a State object’s Controller.
With that, this simple example app is no exception. The first State object you encounter in this example app is called, _MyAppState, and it extends the class, AppStateMVC, from the mvc_pattern package. It happens to have a Controller, unimaginatively named, AppController, which extends the class, ControllerMVC. Because of this, the controller has the option to implement an initAsync() function that runs any asynchronous operations that need to be completed before the app can proceed. Again, I simulated this using a Future.delayed() function counting off 10 seconds. It’s a function I soon found I readily used all the time and couldn’t live without. *grin*
After ten seconds, the app proceeds to start up twelve more individual State objects nestled inside a GridView. This is conveyed in the first screenshot below. Now, I know what you’re thinking… ‘What’s with the cascading ‘Inherit’ widgets??’ Further, the GridView’s ‘children’ property looks peculiar as well. See below. Well, when it comes to whipping up an example app to demonstrate concepts and features, sometimes it’s not pretty. You’ve just got to get it out the door. I’ll admit it— I’ve cut corners in this example app for time. However, not so much when it came to the argument: con.children
That’s a common practice in my apps. Again, I follow the design pattern MVC where the responsibilities typically found in apps are delegated to one of three separate codebases. As such, their lines of communication are specifically defined. The graphics below represent this. And so, in the screenshot above, we see the Interface (the View) is ‘talking to’ the Event Handler (the Controller) when it comes to determining what’s to be displayed in the GridView (i.e. con.children
). That’s because, in most instances, the Controller would then access a data source (the Model) to retrieve such items as depicted in the first graphic below. In this case, however, being such a simple example app, there’s no distinct and separate data source, and it’s more like a combining of the Controller and the Model as depicted in the second graphic below.
This separation of responsibilities encourages better modular development and allows for easier scalability. I mean, today we have a GridView full of dogs, cats, birds, and foxes. Next week, it could be full of astronauts, famous artists, and sports heroes without any change to the code in the screenshot above. That’s a powerful capability during development and maintenance.
I wasn’t going to tout the benefits of this design pattern, but there you are.
Thus, the first screenshot below reveals that argument is a getter called, children, and it calls an internal function that (again, not pretty) ensure twelve images are produced for the GridView — three images for each type of animal: Three dogs, three cats, three birds, and three foxes.
Three of the four of these ‘Animal Image’ widgets are listed below. Each is only really unique by the URI details needed to access their respectable API’s. Each extends yet another State class called, ImageAPIStateMVC. It’s a class written specifically for this example app and, in turn, extends the class, StateMVC. It’s this parent class, StateMVC, that we’ll concentrate on for now.
You see, it’s the StateMVC class that now has a FutureBuilder widget built-in, and so when these individual ‘animal’ widgets start calling on their public API’s, the class, StateMVC, now uses the mixin, FutureBuilderStateMixin, to implement that FutureBuilder and display the appropriate circular progress indicator.
In the first screenshot below, the long list after the with clause would suggest I indeed found Flutter’s State class lacking. Indeed, it now has the additional functionality offered by the mixin, FutureBuilderStateMixin. If you look closely at this mixin in the second screenshot below, you can see it’s available to any State class and not just the StateMVC class. Copy and paste it into your own projects! I’m always sharing with the Flutter community! I only ask you’d mention my name in the copy.
Last week the abstract class, StateMVC, had only the function, build, to be implemented. Like the State class it extends, if you wanted to use it, you had to implement the build() function. Makes sense. Now with this mixin installed, you have the option to use the buildWidget() function instead. Do you like the name I chose there? So original in my name-calling, aren’t I? *grin*
You can see in the second screenshot below that the mixin instead implements the build() function to introduce the FutureBuilder widget. It then calls the buildWidget() function later on in its internal function, _futureBuilder. The function, initAsync, is called to process any asynchronous operations at that point returning a boolean true when completed. True, I could let the developer choose any data type using generics, but my mantra has always been, ‘Keep it Simple; Keep it Flutter.’
Granted, looking at the first screenshot above, I didn’t need to insert the abstract function, buildWidget, into the class, StateMVC. It’s already implemented in its mixin and so is unnecessary there. However, when supplying such a package to the general public as it were, it’s sometimes necessary to dish out some redundancy so, as in this case, a developer can readily see that the function, buildWidget, is available to them. Thus, with this arrangement, you could either override the build() function and not use the FutureBuilder feature at all, or you can instead implement the buildWidget() function and leave the original build() function to the mixin to use. See how that works? You’d then place any asynchronous operations into the initAsync() function and return true when those operations are completed successfully or not.
In fact, you could use the buildWidget() function and yet not implement its accompanying initAsync() function. Perfectly fine. That just means the App would proceed as usual and just fall out of the FuturBuilder. That’s because, as you see in the first screenshot below, the default value for the initAsync() function is true. It’s just like overriding the build() function itself. What it is, in fact, is more options, and you know I like options.
Again, the first screenshot below is of the function, _futureBuilder, and it does reveal that the function, buildWidget, is indeed called when any and all asynchronous operations are completed in the initAsync() function and all goes well. However, what if it doesn’t go well? Again, back when I first started working with Flutter, I found a State object could do with some readily available error handling and so now has an onError() function in the class, StateMVC. The handling of its asynchronous operations is no exception. In this case, error handling is in the form of the function, onAsyncError, and can be seen in the second screenshot of the _futureBuilder() function below. It’s called if the snapshot.hasError
is true.
The _futureBuilder() function must return a Widget. The first screenshot below conveys the last bit of this function, and if there isn’t an error, again the appropriate circular progress indicator is returned. Otherwise, if there is an error, the App terminates in development with the ‘Red Screen of you-know-what’ by default. By the way, in the second screenshot below, a StateMVC object implements its initAsync() function only to call any and all of its Controllers and their own initAsync() functions in a try..catch statement — in case there are errors. Many asynchronous operations have no relation to the app’s interface, you see, and are more suited in the State object’s Controller with the rest of the app’s ‘business rules.’ See how that works?
Let’s move on now to the State object’s InheritedWidget! So you’ve started up this simple example app, and now you're staring at a bunch of cute little pictures of birds, dogs, cats, and foxes. Now what? Well, let’s go back to the app’s home page and note in the code there is a list of TextButtons to be displayed along the bottom of the screen. They’re all passed to the Scaffold Widget using the parameter, persisentFooterButtons. See below.
In the gif file above, you can see that pressing the ‘new’ TextButtons, in turn, will update the appropriate images. Three StatefulWidgets are being updated with every button selected. Again, the cat API, for whatever reason, is slower than the others. Regardless, all twelve of these StatefulWidgets can be updated with a tap of a button. Now know this: There aren’t three separate setState() functions being called with every update — only one. This is all being done with InheritedWidgets and their ‘dependent’ child widgets are being spontaneously updated. I feel this is the most powerful attribute of the InheritedWidget.
Again, as you see in the screenshot above, the State object’s Controller comes into play and handles the event when a TextButton is pressed. The appropriate function is called to produce new pictures of the animals in question (i.e. pressing the button, New Dogs, and the function, con.newDogs(),
is called resulting in three new images of dogs). Note, only three of the twelve widgets are ever updated in each instance. Most of the screen is left alone resulting in better performance.
Let’s take a look inside the Controller itself now and see what makes up those ‘new’ functions. The first screenshot below presents something very interesting. A common function named, newAnimals, is called in each of those functions but from a different Controller depending on the specific animal. Continuing further, you’ll discover all these ‘animal’ Controllers extend a common parent class called, InheritController. A look at that class in the second screenshot below shows the newAnimals() function involves calling this Controller’s first State object it had registered with inside this app. The function, buildInherited, is then called in that State object. The plot thickens!
By the way, looking at the individual ‘animal’ Controllers, you’ll find they all use a factory constructor allowing you to call their newAnimals() function, for example, without instantiating a brand new instance of that class. I find the role of a State Object Controller is best served with only one instance of that class at all times in most projects. Three of the four Controllers are listed below.
Finally, that State object’s buildInherited() function is displayed in the screenshot below. You may be surprised by what’s in there. It’s just your typical setState() function being called there with an empty VoidCallback function passed as an argument. Now, what’s that all about?? Of course, as you know, this means that the State object will be calling its build() function soon after — rebuilding a Widget.
Now, the most important thing you should remember about InheritedWidgets is this: When a particular InheritedWidget is called again and allowed to notify its ‘dependent’ child widgets, all those child widgets’ State objects are rebuilt. In other words, their build() functions are called again as if you individually called their setState() functions. That one State object will be calling its build() function soon after alright, but not to rebuild a Widget. It’s going to call its InheritedWidget again. I’ll show you.
Let’s step through the example app again and hope I haven’t lost you already. Remember those cascading ‘Inherit’ widgets back on the home page? They are StatefulWidgets that contain individual InheritedWidgets. When their accompanying State objects call the setState() functions, they each call individual InheritedWidgets — and then the magic happens.
These StatefulWidgets serve much like a Proxy widget — merely ‘passing along’ an already inflated ‘child’ Widget through to the Widget tree. In this simple app, most of them happen to be passing each other. In the second screenshot, you can see they all have the required parameter, child, used to pass each other and finally the GridView widget through to the Widget tree. Remember that child widget parameter. It’s highlighted again shortly.
It’s in the first screenshot below where things start getting interesting. It’s the State object for the InheritBird StatefulWidget, but it’s not extending the expected StateMVC class, but a new class called, InheritedStateMVC. Man! I’m really getting good at making up names for new classes, aren’t I? *grin* In the first screenshot, you can also see the private InheritedWidget, _BirdInherited. The second screenshot displays the new class, InheritedStateMVC.
InheritedStateMVC is a new abstract class I’ve introduced to the package library, mvc_pattern. Incorporating an InheritedWidget was a little tricky compared to the FutureBuilder widget. In the end, I decided it called for a brand new class that extended the class, StateMVC, the explicitly declared the InheritedWidget type using generics. See above.
In the first screenshot below, you’ll notice that this new class uses the buildWidget() function and thus leaves the build() function to work with the built-in FutureBuilder widget. It’s in the buildWidget() function where the InheritedWidget is called. In the second screenshot below, we’re looking again at the State object for the InheritBird Statefulwidget. Notice it registers with the controller, BirdController, as well as takes in a builder function that returns the InhertiedWidget, _BirdInherited. It’s in this new class where yet another new function is introduced called, buildChild. Again, I keep coming up with these amazing new function names, don’t I? *grin*
As you see in the second screenshot below, it returns that required parameter, child, that I wanted you to keep in mind. It’s a widget already inflated and just passed down to the InheritedWidget. In the second screenshot below, the buildChild() function passes on, in turn, that child widget to the builder function assigned the parameter, inheritedWidget. See how this works? Thus, the stage is set for the InheritedWidget to now take in ‘dependent’ child widgets allowing for spontaneous and distributed rebuilds throughout the app. So how is that done in this simple example app?
Well, let’s step back a bit and examine the example app. On the interface side, the StatefulWidgets that display the animal pictures all extend the State class, ImageAPIStateMVC. We have the RandomBird StatefulWidget, for example, in the first screenshot below. The second screenshot displays the buildWidget() function used by the ImageAPIStateMVC class. Of course, this tells you it too is using the built-in FutureBuilder widget, but it’s the function, widgetInherited, called by the State object’s accompanying Controller object that’s the focus here.
So the controller, BirdController, has now been now registered with two separate State objects during the running on this example app. In the first screenshot above, it’s registered with the State class, _RandomBirdState, but before that, it registered with the State class, _InheritBirdState, that contains the InheritedWidget, _BirdInherited. Finally, it’s the controller, BirdController, itself that’s calling the function, widgetInherited, in the second screenshot above. See it coming together now?
The next step in all this is for the InheritedWidget to collect its ‘dependent’ child widgets. That’s where the function, widgetInherited, comes in. Now I suspect, when you were first introduced to Flutter’s InheritedWidget, you were told when making an InheritedWidget, you then have ready access to it and its properties pretty much anywhere in your app like the Theme and MediaQuery widget because they too are InheritedWidgets. That’s very true but this was never the primary appeal for me personally. Time and time again you were told to create a static function called, of, in your InheritedWidget so you can then have ready access it anywhere in your app:
final myInheritedWidget = MyInheritedWidget.of(context);
// Exactly like funcitons MediaQuery.of and Theme.of
// this gets an existing InheritedWidget of the specified type.static MyInheritedWidget of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
But what if you don’t what your InheritedWidgets to be so readily accessible? What if you don’t really need them to store properties or values to be accessed anywhere in your app? Instead, you want them to allow for the dynamic rebuild of distributed and, at times, seemingly unrelated State objects throughout your app. That’s the real power of InheritedWidgets!
That’s what the function, widgetInherited, does. It also calls the StatefulElement function, dependOnInheritedWidgetOfExactType, and registers or links that StatefulWidget as a ‘dependent’ child widget to the specified InheritedWidget found by type. A screenshot of the widgetInherited() in this new InheritedStateMVC class is displayed below.
Now in our little example app, it’s the Controller and not that State object that calls its widgetInherited() function, and so what happens in that version of the function? Well, it just calls its State object’s version. That’s all. See below. Every SOC (State Object Controller) has the field property,_stateMVC
, pointing to its current State object, and is free to call that State object’s functions instead.
There’s a version of this function in the original StateMVC class as well — it’s displayed in the second screenshot below. Things are a little more involved in that version. Firstly, it has to seek out an InheritedStateMVC object already mounted on the Widget tree, but in the end, it uses the function actually called in the original dependOnInheritedWidgetOfExactType() function named, dependOnInheritedElement, to register a ‘dependent’ child widget to an InheritedWidget.
Now, I just mislead you a little bit there. True, in our little example app, it’s the Controller and not that State object that calls the widgetInherited() function. However, the widgetInherited() function displayed in the first screenshot above is from the original parent class, ControllerMVC. It calls its ‘current’ State object. However, again in this app, all these ‘animal’ Controllers extend a common parent class called, InheritController, and its version calls the buildInherited() from the first State object the Controller had registered with inside this app, remember?
_states.first.widgetInherited(context);
As it is, I’m just using two characteristics of Object-Oriented Programming, Polymorphism, and Inheritance, for the class, InheritController. Its parent class, ControllerMVC, and itself are listed below for your comparison. Note, I decided not to override the buildInherited() function in the subclass, but instead provide a new function, newAnimals. Although, it would likely make sense to do so. What do you think?
So to review, to rebuild the three dependent child widgets (linked to an InheritedWidget by their respective ‘animal’ controllers), all you do is rebuild their respective ‘Inherit’ State object by calling that State object’s setState() function, and that’s it! Calling the State object’s setState() function calls its InheritedWidget again, and all the updateShouldNotify() functions for all these InheritedWidgets return true in this simple example app (see below) resulting in three ‘dependent’ child widgets to be rebuilt with every tap of a button. Run the example app in your favorite IDE’s debugger, and see for yourself.
And so, depicted in the first screenshot below, we’re in the InheritedStateMVC class. Its buildInherited() function merely calls its setState() function as its got an InheritedWidget to call in its build() function. While in the StateMVC class, must seek out an InheritedStateMVC object already mounted to the Widget tree, and then that State object’s setState() function is eventually called.
Well, let’s wrap this up. If anything, come away with this: When a particular InheritedWidget is called again in a running app and allowed to notify its ‘dependent’ child widgets, all those child widgets’ State objects are rebuilt.
Cheers.