DEV Community

Cover image for Little More Efficient
Greg Perry
Greg Perry

Posted on • Originally published at andrious.Medium

Little More Efficient

A Little More Framework for the Flutter Framework makes for better apps

Part of the ‘Little More’ Series, this article will highlight the ability of updating only those parts of your app’s interface that have changed. Flutter’s InheritedWidget provides the means, but many apps continue to have their whole interface rebuilt. This only degrades performance.

Depend On It

Using Flutter, for example, you can have three widgets each displaying an image of a animal. Each widget ‘depends’ on the same InheritedWidget. In Flutter, if that InheritedWidget is called again, all three of those animal widgets are rebuilt and will display a new image. Only those areas of the screen are updated. Tap on the video below and see for yourself.

The first screenshot below displays the example app, builtin_inherittedwidget_example. It’s a StateX object (see state_extended package) assigned as a ‘dependency’ to a particular InheritedWidget using the function, dependOnInheritedWidget(). The second screenshot below is the code for the footer buttons that are pressed one after the other in the accompanying video. Each ‘onPressed’ function is also highlighted with a red arrow.

Note, how the arrows highlight the separation of responsibilities in the code. The variables ‘_con’ and ‘con’ in each screenshot performs the ‘event-handling’ and ‘logic’ involved in the code. The rest of the code is only concerned with the interface (see Little More Control).

image_api.dart

home_page.dart

In this example app, four StateX objects have the named parameter, useInherited, set to true (see screenshots below). That means each will use their built-in InheritedWidget. Each represents a particular animal, and each will manipulate three additional StateX objects that display an image of that animal.

inherit_bird.dart  and  inherit_cat.dart

inherit_dog.dart  and  inherit_fox.dart

The first screenshot below displays the built-in InheritedWidget. It’s called, StateXInheritedWidget. Any ‘dependent’ Widgets will be rebuilt when that InheritedWidget is called again. In Flutter, when you call an InheritedWidget again, its corresponding InheritedElement object will eventually call the function, notifyClients(). See the second screenshot below. There, the InheritedWidget’s dependencies are notified in turn, and they are rebuilt.

part06_inherited_widget_state_mixin.dart

framework.dart

06_inherited_widget_state_mixin.dart

Note, if the property, useInherited, is set to false, the builder() function is instead called with every rebuild (the third screenshot above). Things work like Flutter’s original State class calling the build() function. The resulting child widget is returned completely foregoing the built-in InheritedWidget.

Make A Splash

As an aside, I thought I’d introduce a simple little splash screen, a little kitten, to the example app. See the video below. Using Fluttery, you’re given three ways to introduce a Splash Screen. See the second screenshot below. The first parameter, splashScreen, takes precedence, but instead of instantiating a widget right there in the initialization, you have the option to instantiate a widget either with the ‘on’ method or with the ‘inline’ method. Note, the ‘inline’ version takes last precedence.

A Splash Screen in Fluttery remains displayed until all the start up operations are completed. The three ‘cat’ image widgets in the example app now also display the little kitten. Cute, no? It takes time to complete such a network call to download an image; best to have something ‘spinning’ while you wait.

main.dart

app_view.dart

The Future Is Now

Each StateX object in this example app has a built-in FutureBuilder as well. How many times have I seen apps instantiate Widgets and Classes even before the runApp() function is called?! An example of this is displayed below. They’re instantiated and set up nowhere near where they’ll be used, but since there’s no logical place to perform such operations, the developer has no other choice! Not good.

Code before the runApp() function

You need a logical place so such tasks. They have to be completed before a particular screen is displayed, and in the StateX class, such a place is in the function, initAsync().

The first screenshot below is the example app’s first initAsync() function. For demonstration purposes, the app is delayed ten seconds so you can appreciate how cute the little kitten is at startup. Again, this ‘logic’ is found in a Controller class and nowhere near the interface. Nice.

app_controller.dart

image_api_controller.dart

The example app relies on four public API’s to retrieve images. The first screenshot below, for example, gives you the API used to retrieve bird images. They’re not the fastest Web services in the World. They are prone to lag and complete failure frankly, and so as you see in the second screenshot above, the process is pretty involving. However, it’s necessary, and so it too is tucked away in a initAsync() function found in another Controller class, ImageAPIController.

The ‘image’ Widget has the class type, ImageAPIStateX, and it takes in this Controller (second screenshot below). The Controller, ImageAPIController, performs the actual data retrieval (the image download) while the StatefuleWidget worries about how that data is displayed. Very nice.

network_bird.dart

image_api.dart

Controlling Events

Again, the four animal-type InheritedWidgets are inserted into the Widget tree in the app’s home screen. See the first screenshot below. Each with a unique SOC (State Object Controller). For example, the InheritBird class has the controller, BirdController. See the second screenshot below.

home_page.dart

inherit_bird.dart

Using Fluttery, each Controller object will have a reference to its assigned StateX object. Doing so provides the means to assign dependency as well as call the built-in InheritedWidget again. See the second screenshot below.

firstState?.dependOnInheritedWidget(context);

firstState?.notifyClients(context)

bird_controller.dart

inherit_controller.dart

That very same Controller is assigned to the corresponding ‘Image’ widget that displays an animal image. For example, in the first screenshot below, you see the class, NetworkBird. *It displays a bird image — assigned the appropriate API information. The State class, *_NetworkBirdState, continues on the second screenshot where the Controller object, BirdController, then registers this Widget (really it registers its StatefulElement object, context) as a dependency to the InheritedWidget in the State object, InheritBird. Easy Peasy.

network_bird.dart

image_api.dart

See below, and you’ll find each type of animal has its own Controller:

bird_controller.dart  and  cat_controller.dart

dog_controller.dart  and  fox_controller.dart

Take the time to download the example app, and review the process yourself. Note, how the whole process is broken down into parts and delegated to separate classes. This promotes modular development and code reuse and consequently more efficient and more effective implementation and maintenance.

TL;DR

I’ll continue with a deeper dive into the inner workings of Flutter‘s StatefulWidget, State class, Futurebuilder, and InheritedWidgets. Nothing new here. Merely implementing capabilities already found in Flutter as well as following some ‘good programming practices’ every developer should know.

Control Your Future

Once the initAsync() function is called and creates a Future for a StateX object, in most cases, you don’t want to create that Future again. These are synchronous operations that need to be completed once before the StateX object can display its interface.

Such operations would include establishing connections to Web services and or the opening of databases. Opening such connections again and again with every interface change would be grossly inefficient. Unless, of course, you’re calling an particular public API to retrieve a new animal image.

In the first screenshot below, you can see the StateX’s runInitAsync() function is used to determine if its corresponding initAsync() function is to be called again or not. The runInitAsync() function is implemented in the second screenshot below and only returns false when appropriate.

I’ve placed a menu option in this example app to demonstrate how the images would simply not change at all if the runInitAsync() functions involved always returned false. Please, examine the video.

part05_futurebuilder_state_mixin.dart

image_api.dart

Going To Dark Side

Again, there are situations where the runInitAsync() function should return false. Rebuilding the app’s whole interface to convey it in dark mode would be one of them. In the screenshot below, when the darkMode() function is called from the app’s menu, not only is the app’s theme toggled (the whole interface rebuilt with the call,App.setState()), but a particular property created for the example app is set to false:

runInitAsync = false;

That property you’ve already seen in the implemented runInitAsync() function displayed above. The first video below runs things properly, but the second video demonstrates what happens if you don’t ever return false in the runAsync() function. Again in most cases, you don’t want to create the Future again (the database is already open). However, if you always return true in the example app, you’ll inappropriately lose your animal images when changing back to dark mode.

return appController.runFuture && appController.runInitAsync

app_controller.dart

Let’s continue down the example app’s popup menu and see how one can now interfere with those built-in InheritedWidgets.

There’s No Inheritance

Deselect the menu option, Use InheritedWidget, and you’ll have a rather boring example app on your hands. Remember, it’s the four StateX objects listed below that represent the four types of animals displayed. They use their built-in InheritedWidget to take in three additional widgets as ‘dependencies’ to then display those animals in a grid.

In the video below, you can see when the ‘New Cats’ button is pressed, three new cat images appear (the API is providing duplicate images, but you know what I mean). As the video continues, you can see the app is no longer working. The menu option, Use InheritedWidget,is deselected — the named parameter, useInherited, is therefore set to false.

inherit_bird.dart  and  inherit_cat.dart

inherit_dog.dart  and  inherit_fox.dart

Note, you can still just tap on individual images and a new image appears. That’s because that’s a separate process. The onTap() method merely calls the setState() function of the associated State object. See the second screenshot below. In the video above, that would be the State class, _NetworkCatState. The setState() method is called, but not before its initAsync() function will load a new image. That’s because the global flag, runInitAsync, was set to true (the first screenshot below). See how this works?

image_api.dart

image_api_controller.dart

Going Home

You may have noticed with every menu option selection, the whole screen gets refreshed with a grid display of new animal images. It’s a necessary step in Flutter as that portion of the widget tree is being rebuilt. The app’s home page is a StatefulWidget called, HomePage, and the build() function for its State object gets called with the first two menu option selections.

As you see in the second screenshot below, it’s the controller called,HomeController,that calls that build() function. It’s the controller assigned to the State class,_HomePageState(first screenshot below). Note, State Object Controllers tend to utilize a factory constructor allowing for commands like the one listed below. You’re not instantiating a new instance of the class but working instead with the original instance:

HomeController().setState((){});

home_page.dart

app_controller.dart

Turn To Black

The menu option, Dark Mode, involves the Fluttery Framework’s App object. With Fluttery, you can reassign your app’s overall color theme using the following command:

App.themeData = darkMode;

This command appears in the darkMode() method (second screenshot below) and is called when you select the menu option, Dark Mode. See the first screenshot below. Using Fluttery, the whole interface is then rebuilt with the call,App.setState().Let’s see how that works next.

app_menu.dart

app_controller.dart

I have the video changing the app to dark mode listed below again. In the first screenshot below, the app’s first State object, AppState,takes in the StatefulWidget, HomePage,as the app’s home page. When the AppState’s build() function is called again (second screenshot below), the MaterialApp widget that makes up this app (running in an Android emulator) is created again. This time, with a ‘dark mode’ theme. Yes, as the video would suggest, this all runs in a flash. Flutter’s widget tree is efficiently fast.

app_view.dart

app_state.dart

Recreate State

Lastly, you must be aware that State objects, though recreated in ScrollViews and such lists, are only recreated in Flutter if its StatefulWidget counterpart is called with a ‘new’ key value.

You can see, in the screenshots below, how I guarantee a State class is recreated with strategically placed UniqueKey() functions.

app_statefulwidget.dart

home_page.dart

Top comments (0)