A Better Flutter App #7
Supply lifecycle event handling with ease.
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
An Eventful Lifecycle
Android developers would be familiar with and perhaps even miss the lifecycle event functions listed along the first column in the graphic below. The second column is the Flutter platform’s equivalent. In this article, I’ll introduce their implementation and use as it pertains to the starter example app.
Using the MVC framework, mvc_application, the starter app binds with the device platform’s lifecycle linking a number of event handlers — one of which involves the function called, didChangeAppLifecycleState(). As for the starter app, such a function proves helpful in stopping and starting Flutter’s Timer object that periodically displays in red a word-pair inside the Counter app. This will be demonstrated in this article possibly giving you an idea of how to properly work with Timer objects. Note the screenshot below of the didChangeAppLifecycleState() function. It conveys all the possible ‘lifecycle events’ that can occur and thus are represented by the parameter, state.
When this app starts up, Flutter’s Timer object is eventually instantiated in the controller, WordPairsTimer. It’s there in the controller’s initTimer() function (see second screenshot below) where the Timer object is then assigned to the instance variable, timer.
All this is done, in turn, inside the controller, CounterController, when the ‘Counter app’ screen is first displayed. In the first screenshot below, you can see the ‘CounterController’ controller is assigned to the screen’s State object, _ConterPageState. Note, this State object is of type, StateMVC, and assigning the controller to it will allow that controller to then bind to the device platform’s event handling. Further on in that screenshot, the State object’s initState() function is displayed to you merely for demonstration purposes.
You see, it is within that initState() function that the State object calls the initState() function of any and all controllers assign to it. Thus, in the second screenshot, the Timer object is instantiated in the CounterController’s initState() function, but not before, the ‘timer controller’, WordPairsTimer, is also assigned to the State object, _ConterPageState. Again, doing so will bind that controller to the app’s lifecycle events as well.
By the way, below is a screenshot of a StateMVC object’s initState() function. You can readily see it’s there where the initState() function of every controller assigned to it is called. It is also there, in fact, where every StateMVC is bound to the host device’s lifecycle events using the WidgetsBinding class. In turn, controllers work through their assigned StateMVC objects to recognize the device’s lifecycle events. With me so far?
A Time and Place for a Timer
The Timer object displays a new word-pair every two seconds — but only when viewed in the Counter app. There are two other apps in the starter example app. For efficiency, the Timer object is stopped when the user moves off the Counter app and is only started again when the user returns back.
In observing the gif file below, note it begins in the Counter app, but then cycles through the other two apps before returning back. In this case, the State object, _CounterPageState, will call its deactivate() function when we move off the Counter app. Because it is of the type, StateMVC, the State object will cycle through any and all of its ‘attached’ controller objects and call their corresponding deactivate() functions as well. As you see below, the WordPairsTimer controller cancels its Timer object when ‘deactivated.’ An appropriate action as the Timer would continue and would unnecessarily take up precious CPU cycles.
Going Full Circle In Cycle
Coming back to the Counter app, in a very consistent and systematic way, the ‘Contacts app’ will call its controller, ContactsController, own deactivate function then soon after that, the Counter app screen is rebuilt. Therefore, the State object, _CounterPageState, is rebuilt and calls its initState() function. Of course, this means the attached controller, CounterController, own initState() function is called repeating the whole process and a list of red word-pairs is displayed once again. See how that works?
Time To Pause Then Resume
When another app, for example, is selected and the starter app is now in the background, it’s best to again stop the Timer object and its periodic processing. That’s demonstrated somewhat in the gif file below. Know that even a tap on the Android phone’s stop button causing the app to shrink back into the background will pass the value, AppLifecycleState.paused, to the didChangeAppLifecycleState() function. The cancel() function is then called as a result. See below. The Timer‘s periodic process has been stopped.
However, a tap on the app bringing it back to the forefront will call the didChangeAppLifecycleState() function once again. This time it’s the value, AppLifecycleState.resumed, that’s passed calling the initTimer() function and initiating another Timer object to resume. Finally, a tap on the stop button and swiping the app away will once again ‘cancel’ the Timer object before the whole app is terminated. A good practice when dealing with Timer objects.
Let’s leave it at that for now. Event handling is yet another crucial functionality every production-worthy Flutter app should have in hand. The MVC framework allows for this with ease.