Little More Life Cycle Handling
Greg Perry
Posted on July 26, 2024
A Little More Framework for the Flutter Framework makes for better apps
As part of the ‘Little More’ Series, this article will review the Life cycle events that should be considered when running your app. Events that are made readily available to you when using the [Fluttery Framework] package.
Like its iOS and Android counterparts (see above), Flutter StatefulWidgets have their own life cycle. Below is a graphical depiction of that Life cycle as it’s currently understood. Follow the arrows, and you’re following the ‘sequence of function calls’ when certain circumstances occur. Using the Fluttery Framework, your State object will always have a corresponding function to address any such event — as best as it‘s able anyway.
You see, Flutter is a cross-platform solution — the Flutter engine runs on top of a completely different operating system depending on the device. As such, there are two separate life cycles involved when it comes to Flutter (denoted in the graphic by the dotted line). Unfortunately, as of this writing, these two life cycles do not acknowledge each other at all.
For example, Flutter will not call a StatefulWidget’s deactivate() and dispose() functions whenever a user has ‘unfocused’ your app with some hand gesture — only to terminate it soon after. For those developers working on the mobile platforms in particular, this has become an issue.
With Fluttery Framework, however, an effort is made to call the deactivate() and dispose() functions of all the app’s StatefulWidgets whenever such a scenario occurs (See second screenshot below). Unfortunately, as of this writing, it comes down to the particular operating system involved whether they’re called or not:
“AppLifeCycleState.detached is not called when app is closed quickly”
The video below has the example app, widgetsbinding_observer_example, running on a mobile phone emulator. This app is peppered with print() functions so to convey when particular events are triggered and in which State object. In the video, the app loses focus right away but then is returned to the foreground — only to lose focus again before being terminated. Without intervention, that screen’s deactivate() and dispose() functions would never have been called. The graphic is again presented below for you to compare with the adjacent print() function calls.
Rule #2 To Keep In Mind
Regardless of platform, remember to close databases and other time-critical resources in the **hiddenAppLifeCycleState() method only to open them again in the method, **resumeAppLifeCycleState(), if and when the user returns to your app.
This can get really complicated, but the Fluttery Framework is here to help you.
Another quick example is demonstrated in the video below. Page 1 and Page 2 are two StatefulWidgets called one after the other. The user then returns from Page 2 to Page 1. Again, the example app with its many print() functions details all the events that occur (see below). In this case, you can see when Page2 was closed, the functions, deactivate() and dispose(), are called in quick succession.
Better To Deactivate Than Dispose
Remember this: You’ll have no idea when a State object’s dispose() is called — if ever. This function is called by the Flutter engine as part of its ‘garbage collection’ process. It’s to the engine’s discretion when the dispose() function is called (or never called under certain memory constraints), and so, if your StatefulWidgets have any time-critical resources being freed and such, it would be more reliable to do so in their deactivate() functions and not in their dispose() functions.
Rule #1 To Keep In Mind
Regardless of platform, if no longer required by the closing StatefulWidget, remember to close any databases and any time-critical resources in its **deactivate() method only to open them again in its method, **activate(), if the running StatefulWidget is not being closed but instead is being moved around the Widget tree (e.g. Reordering items in a ListView widget).
The Series of Events
Let’s quickly go through the event functions calls listed above and suggest what they represent. As you know, when a State object is first created, it is these two functions below that are called. The first State object is called, _AppState.
The Fluttery Framework is ‘aware’ a router is involved in this example app, and so the ‘home’ screen with its State object, _Page1State, is started up and will soon call its initState() function, but not before its event function, didPush(), is called firsr. This allows you to run code ‘when a StatefulWidget is just opened by a Router.’
The next two print() functions are displayed below. They tells us ‘Page 1’ is now being displayed with a zero count.
Because this was run on a mobile phone emulator, the painting and sizing of the app’s interface triggers the didChangeMetrics() in the two StatefulWidgets that make up the app so far. Note, how they’re called in the order they were instantiated — with the help of the Fluttery Framework.
The next line tells Page 1 that the next screen to be displayed has been selected while the second line tells Page 2 it came about because of a router. Note, there’s no reinventing the wheel to achieve this. Fluttery is just implementing what’s already there in Flutter to note these events.
The next three lines tells you ‘Page 2’ is now displayed with a count of zero.
In the next screenshot below, both the first screen and second screen are notified when the user has returned back to Page 1. Great stuff!
Consequently, Page 2 is about to be removed from memory. Therefore, the next two lines appear. Again, the deactivate() is a better place to ‘clean up things’ before returning to the previous screen. The dispose() function just happens to be called right after deactivate() frankly. Under different circumstances beyond your control, it could just as easily never be called.
Timing is Everything
Playing around with the example app, you’ll discover that Page 5 has a Timer displaying ‘Word Pairs’ introduced in the Flutter website’s codelab called, “First Flutter app”. See below.
Look how the Timer object is handled. It’s turned on and turned off when appropriate. You have the ready-means to handle all your running resources effectively and efficiently using Fluttery. Moving on from Page 5 to the next Pages without addressing the Timer object, or an active stream or some other free-running process is just sloppy! In a sense, you’ve just implemented a memory leak otherwise!
The second screenshot is the very code called when moving from Page 5 to Page 6 and back. Highlighted by the first red arrow is the Timer being turned off while the second red arrow highlights the Timer being turned back on. The first screenshot lists the print statements conveying the event functions involved in the video — its last portion showing the app closing is again displayed further on below.
You can see how the ‘Operating System Events’ are covered by the Fluttery Framework. You can see this by comparing the print statements to the life cycle graphic below. Note, being on a phone emulator running on a laptop, Fluttery even recognizes the memory constraint that occurs by calling the function, didHaveMemoryPressure().
Look how all the Operating System Event functions in all the State objects currently running in the app all fire in turn and in the order they were instantiated. The host device is up to something, and all the running State objects should be aware of it.
The example app, [widgetsbinding_observer_example], has nine separate screens called one after the other. Note, how the State objects and their accompanying controllers (Business Logic Components like in the BLoC architecture) are triggered in the order they were instantiated.
Download the example app, [widgetsbinding_observer_example], and see how Fluttery is working for you to make your Flutter app more aware of the platform device it runs on. It’s a necessity if you want a better app.
Cheers.
Posted on July 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 30, 2024