StateX class topic
The State Class Extended
Extend the capabilities of Flutter’s own State class. This class allows you to use a State Object Controller (SOC) to reliably call the State object's setState() function from outside its class — a very powerful capability!
This class provides the initAsync() function to perform any asynchronous operations before displaying its contents. It also provides the dependOnInheritedWidget() function to assign a widget as a 'dependency' to the StateX's built-in InheritedWidget. It then uses its notifyClients() function to spontaneously rebuilt only those widgets that have changed.
Code | State Pattern | Sync | Sync | Interface? | Inherit |
- The State Object Controller separates the interface (i.e. the State object's build() function) from everything else: StateXController
- A function to perform any necessary asynchronous operations before displaying the interface: initAsync()
- A means to update only particular widgets in the interface and not the whole screen improving performance: dependOnInheritedWidget() , didChangeDependencies() , notifyClient() , state() , updateShouldNotify()
- A function that runs if any error occurs. Allows you to 'clean up' and fail gracefully: onError()
Control Your Code
All your ‘mutable’ code should go into your State Object Controller. It would contain the ‘business logic’ involved in a given app as well as address any event handling. Controllers are not new to Flutter. Two popular widgets that use a controller, for example, is the TextField widget and the SingleChildScrollView widget.However, unlike those two widgets, a StateX object can have any number of controllers. This promotes more modular development where each controller is dedicated to a particular responsibility independent of other controllers. This further relieves any 'controller bloat' common when involving a controller (see State Object Controller).
State Pattern
A common pattern seen when implementing a controller for use by a StateX object will have the controller object instantiated right in the State object and then referenced here and there in the State object’s build() function. Thus providing the data and the event handling. For example, the first screenshot below is of the 'counter example app' that accompanies the Flutter Framework package. The second screenshot shows how a number of controllers can be added.(A controller and its use are highlighted by red arrows.)
_MyHomePageState | Page1State |
---|
Sync The State
There's now a means to deal with asynchronous operations in the State object before rendering its interface. In other packages, the approach has been to execute such operations even before the app begins---a disjointed approach. The operation is not done by the actual State object (the actual screen) where it's relevant or required. However, Flutter has always had the FutureBuilder widget to make this possible. A FutureBuilder widget is built into the StateX class, and has its initAsync() function to then perform such asynchronous operations. As a result, when such a State object is called, it can wait for its asynchronous operations to complete before proceeding. As easy as that.
Below are three gif files. The first one depicts what a user will see more often than not when starting up an app written with the Fluttery Framework. There's always a remote database to open or web services to connect to and this takes a little time. You don't want your user staring at a blank screen. They'll think the app is frozen! Fluttery displays a spinner indicating the app is indeed running. The second gif file depicts twelve separate StateX objects waiting to continue. Each has its own individual asynchronous operation loading a animal graphic from some REST api somewhere. The last gif file shows the whole startup process for this particular app. It carry's on and shows you they are indeed individual operations ending with a different picture.
In this case, since they're running on an Android emulator, those spinners are from the CircularProgressIndicator widget. If they were running on an iOS phone, the CupertinoActivityIndicator widget would produce the iOS-style activity indicators instead. Flutter is a cross-platform SDK after all.image_api_controller.dart | working_memory_app.dart |
---|
Control The Sync
Those three screens above are from yet another example app supplied with the Flutter Framework. Yes, there are twelve separate StateX objects on that one screen each loading an animal image. Actually its their own individual State Object Controller that's performing the asynchronous download. Above, is a screenshot of that controller's own initAsync() function. While each controller is calling its _loadImage() function, its associated StateX object will quietly wait with its indicator spinning away on the screen. Very nice.
The screenshot on the right is another initAsync() function from another app altogether. It demonstrates there can be a number of asynchronous operations performed before an app can continue. This particular app involves the authentication of the user for example. If not already logged in a login screen will appear. This function is in another controller and that controller calls yet another controller to run its own initAsync(). See how you're able to separate distinct asynchronous operations each prefixed with an await operator and returning a boolean value to the variable, init. It's all easy to read and all in the right location to be implemented.
It's suggested you implement such operations in a Controller, and not directly in a StateX object. Besides, the StateX object's own initAsync() function is already implemented: It's calling all the initAsync() functions from its associated State Object Controllers. When they're all complete, only then does the StateX object call its build() function. Since most asynchronous operations have no direct relation to an app’s interface, you’ll likely have your asynchronous stuff running in a State Object Controller anyway with the rest of the app’s business logic. See how that works?
There can be individual controllers running their own initAsync() function. Very clean. Very modular.
Which Interface?
As you know, Google touts Flutter as a cross-platform SDK. It even offers two types of interfaces to be displayed to users when running a Flutter app: the Material interface design and the Cupertino interface design.
This suggests then that you're able to create an app using either interface or both. For example, you'd run the Cupertino version of the interface if the app is running on a iOS phone , and run the Material version if the app is running on an Android, or on Windows or on the Web or on Linux. However, right out of the box, Flutter doesn't give you any place to make such a distinction. It only gives you one function: the build() function. However, the Fluttery Framework already uses that function. It uses that funtion to give you a separate place for those two types of interfaces:
Like the State class, the StateX class is an abstract class. However, when you extend the StateX class, you don't have to implement the one build() function. You have to implement two functions:
app_state.dart |
---|
Flutter produces one codebase for multiple platforms. It's likely the very reason you've turned to Flutter to make your next app is because of this ability. If you only want to use the Cupertino interface, for example, then direct the buildAndroid() function to call the buildiOS() function. Otherwise, build both. As you see from the video, with the Fluttery Framework, you can even have to user choose their favorite interface at runtime.
Inherit The State
You've may have been introduced to the InheritedWidget, and how it allows you to repel down the widget tree any piece of data you've designated. However, a more intriguing feature is whenever you call an already instantiated InheritedWidget, any widgets assigned as its dependents are rebuilt (their build() functions run again)---as if their setState() functions were explicitly called.Now that allows for improved performance with the refresh of only specific areas of the interface.
Even the efficiency of the humble 'counter app' is greatly improved. Instead of refreshing the whole screen (including the StatelessWidget with its 'You have pushed the button this many times') , when the 'Use the built-in InheritedWidget' switch is on, only the lone widget displaying the current count is then ever rebuilt. The rest of the screen would now be left alone with every press of that button. Granted this is a very simple interface and possibly a bad example, but look how easy this is implemented in the screenshot below.counter_app.dart |
---|
The state() function found only in the Fluttery Framework will allow for this immediate improvement in efficiency. When it comes to interfaces, the less that's rebuilt, the better. You're app is running on a mobile phone and not a Cray Supercomputer after all.
Back to the app with its grid of animal pictures, you can see above when the 'new dogs' text button is pressed, only the three 'dog pictures' are downloaded again. What your seeing are only three portions of the screen being updated---only three widgets being rebuilt. If the whole screen was rebuilt, all the pictures would change. Not very effective.
counter_app.dart |
---|
The screenshot above depicts one of the three widgets being assigned as a dependent to a State object's InheritedWidget using the dependOnInheritedWidget() function. In fact, the State object's controller with its own dependOnInheritedWidget() function is actually used.
Classes
-
AppStateX<
T extends StatefulWidget> Get started StateX class AppStateX class - The StateX object at the 'app level.' Used to effect the whole app by being the 'root' of first State object instantiated.
-
StateX<
T extends StatefulWidget> Get started StateX class Using FutureBuilder Error handling Testing Event handling - The extension of the State class.
- Uuid StateX class State Object Controller
- Shamelessly extracted from the author of Scoped Model plugin, Who maybe took from the Flutter source code. I'm not telling!
Mixins
- AsyncOps StateX class State Object Controller
- Supply the Async API
- FutureBuilderStateMixin StateX class Using FutureBuilder
- Supply a FutureBuilder to a State object.
- ImplNotifyListenersChangeNotifierMixin StateX class State Object Controller
- Allows you to use a ChangeNotifier as a Mixin
- InheritedWidgetStateMixin StateX class
- Supplies an InheritedWidget to a State class
- RecordExceptionMixin StateX class Error handling
- Record an exception for review by the developer
- RootState StateX class State Object Controller
- Supply access to the 'Root' State object.
- StateListener StateX class State Object Controller Event handling
- Responsible for the event handling in all the Controllers and State objects.
- StateXonErrorMixin StateX class Error handling
- Supply an 'error handler' routine if something goes wrong. It need not be implemented, but it's their for your consideration.