A Better Flutter App #8

Effectively using an InheritedWidget for separate and spontaneous rebuilds

One of a series of articles detailing a comprehensive starter app. This app is generated by a template offered by the package, app_template, which uses the underlying framework, mvc_application, based on the MVC design pattern.

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, Facebook, etc. They may come out as static pictures or simply blank placeholder boxes. Please, be aware of this and maybe read this article on medium.com

Let’s begin.

Other Stories by Greg Perry

Individual Change

You can see in the build() function below, the CounterController controller’s class property, wordPair, is being displayed. In the gif file, you readily see it corresponds with the red word-pairs changing every few seconds. What if I told you the whole screen is not being repainted (rebuilt) with every word-pair change? In fact, what if I told you only the property, con.wordPair, is being rebuilt? It’s the only Widget where its build() function is being called. Further, what if I told you it’s only being called when a new word-pair is generated?! A wonderful bit of efficiency! The rest of the screen is left alone and unchanged. It’s an isolated and separate rebuild of one small Widget located on the screen, and I’ll show you how this is implemented by the framework and used in the starter app example.

Get A Pair

Back in the initTimer() function where the Timer is first created, a duration and a callback function is provided. The duration in the starter app is explicitly set to two-second intervals when the app is first started. The callback function, if not explicitly assigned from a parameter, is assigned the function, _getWordPair(). At the end of every interval, the Timer object will call this function generating a word-pair. And so with every newly generated word-pair, you can see in the second screenshot below it is then assigned as a String object to the static variable, dataObject.

Built It And They Will Too

However, know this. When that static variable, dataObject, is assigned a value, the underlying framework’s InheritedWidget is having its setState() function called. Doing this will cause every and all of that InhertiedWidget’s ‘dependencies’ to call their own build() functions — resulting in separate rebuilds of those Widgets as well. A powerful feature of Flutter! As it happens, in this framework, any and all SetState widgets are dependant on this InheritedWidget by default. Therefore, with every assignment of a value to the Static variable, dataObject, any and all SetState widgets used by the app will also be rebuilt. Follow me so far?

Thus, in the next scheduled Flutter rebuild cycle the getter, wordPair, will retrieve its value, but not before its StatelessWidget, SetState, has been rebuilt. You see, that widget's InheritedWidget has been rebuilt, and so its builder parameter would have been passed a new value in the dynamic variable, obj. Looking at the second screenshot below, you can see if the dynamic variable is a String object (a word-pair), its value is returned within a Text widget…and there you have it.

A Stateless State Set

And so, as you watch the word-pairs go by in the gif file below, know that the build() function of the StatelessWidget below is being called with every new value assigned to the static variable, dataObject. Easy peasy. That one little getter, wordPair, thus displays a new value every time — leaving the rest of the screen unchanged. Even the Counter app that hosts this getter is less efficient! Every tap of that button causes the whole screen to be rebuilt! A waste!

Again, as you see in the first screenshot below, when that static variable, dataObject, is assigned a value, the underlying framework’s InheritedWidget is having its setState() function called: _InheritedMVC.setState((){}). However, let’s not confine ourselves to the SetState class that only is rebuilt when some static variable is assigned a value. The framework can be more reflexible than that and take full advantage of Flutter’s InheriedWidget! Do you see what’s highlighted in the second screenshot below? What’s highlighted is unbelievably powerful. I’ll replace the SetState class in the starter app and instead implement those two functions: inheritWidget() and setStatesInherited().

Inherit The Widget

In this alternative approach, let’s take out the SetState class altogether for the getter, wordPair, and replace it with a StatelessWidget called, _WordPair. Indeed, in the second screenshot, this StatelessWidget returns the Text widget with assumingly the next generated word-pair assigned to the class property, con._wordPair. Note, an inheritWidget() function is called just before the Text widget is returned. To make any Widget a ‘dependent’ of the framework’s InheritedWidget you need only pass its context to this function. It’s essentially the very same thing as what you may be familiar with when working with InheritedWidgets and instead issuing the following command: context.dependOnInheritedWidgetOfExactType<_InheritedWidget>();

Of course, the underlying framework takes care of all this for you.

Inherit The State

Ok, so what else is required. Well, the SetState approach required some static variable to explicitly be assigned a value causing the framework’s InheritedWidget to then be rebuilt. Well, what if you just want to explicitly call the framework’s InheritedWidget to rebuild yourself? —you just want its build() function to run again when you want to? I mean, it’s your app! You’re responsible for calling the InheritedWidget and consequently all its dependent Widgets’ build() functions. How would you do it in this scenario?

In the screenshot below, you can see the _getWordPair() function has been modified a little bit. I’ve introduced four more lines of code. One assigns the newly generated word-pair to the instance variable, _wordPair. That property, if you recall, is accessed by the new StatelessWidget, _WordPair. Next, the ‘App State Object’ calls its function, inheritedNeedsBuild, to notify the framework’s InheritedWidget to call its setState() function and rebuild. Hence, all its ‘dependencies’ will also rebuild. Note, as of this writing the function, setStatesInherited, was recently deprecated in favor of the more familiar name, inheritedNeedsBuild (akin to the markNeedsBuild() function). It was placed there anyway as it’ll still work for now. Now, what of that fourth line? The one currently commented out? Well, it’s just another variation.

word_pair_timer.dart

Pass the Build

In the first screenshot below, that fourth line is now uncommented. It reveals it’s the same inhertedNeedsBuild() function but with the instance variable, _wordPair, passed to it. Now looking in the second screenshot at the further modified StatelessWidget, _WordPair, you’ll discover the passed variable was assigned to that static variable, dataObject. Thus, this is another means to have that static variable come into play if you wish. Bit of overkill in this simple example, but you do have that option.

Same Inherited Behavior

All this results in the same behavior. In this case, the InheritedWidget is explicitly called to rebuild using the function, inheritedNeedsBuild. Any and all Widgets with the function call, inheritedWidget(context), in their build() function will then be rebuilt. That’s the most powerful aspect of Flutter’s InheritedWidget!

For example, just imagine some financial app with a very, very busy screen — its data elements flashing and constantly changing here and there. Rebuilding and refreshing the whole screen would be madness! However, with the way an InheritedWidget is implemented in this framework, there’d be no problem at all. Now, would this be another crucial capability of a typical Flutter app? Well, it would certainly make that Counter app more efficient, I can tell you.

shutterstock

Cheers.

Part of the ‘A Better Flutter App’ Series

→ Other Stories by Greg Perry

Freelance Developer