StateX By Example



This content originally appeared on DEV Community and was authored by Greg Perry

Learn StateX using its example app.

StateX is a class found in the state_extended package. It merely ‘extends’ the capabilities of Flutter’s State class with the addition of a Controller class, a built-in FutureBuilder, and a built-in InheritedWidget. This package was recently accepted by Flutter Gems under the category, Flutter Framework. In this article, a review of what it can do for you will involve a walk through its accompanying example app.

Index of Topics

  • From The Start
  • First Class
  • A State of Control
  • Build Better
  • The Three Count
  • A Separate State
  • Inherited Performance
  • Build Once And Forget It
  • Less Is More
  • Note The Change
  • It’s a Do-over
  • A New State
  • Keep Control
  • Keep Count
  • Make A New Start
  • Learn by Example
  • A Singular Approach
  • It’s All Timing
  • Adaptive Abstraction
  • Unknown Potential
  • The Many Constructs
  • Handle Your Errors
  • A Better View
  • Trouble From The Start
  • To Catch An Error
  • To Err Is Programming
  • To Build Or Not To Build
  • Quick Reference
    • StateX Class
    • StateXController Class
    • Initialization Handlers
    • Event Handling
    • Error Handler

Presented in this article will be a series of screenshots conveying the example app’s source code. Each screenshot will be accompanied by one or more paragraphs explaining the function and features on display. Further, the screenshots’ captions will be linked to the very source code in its Github repository.

Let’s begin.

Other Stories by Greg Perry

From The Start

The first set of screenshots below starts with the main () function. Not a hard-and-fast rule, but you’ll notice I use ‘export files’ to keep the number of import statements used to a more manageable nmber. Instead of reams of import statements at the start of every Dart file, I followed the Dart team’s approach to organizing a library package. You can see the next two screenshots export the very files highlighted with red arrows in the first screenshot below. As a result, there’s only one import statement required for the main () function (Tap the screenshots for a closer look).

main.dart

view.dart

controller.dart

First Class

You can see it’s the MyApp widget that’s called in the runApp() function. That class is presented in the first screenshot below — and note only three import statements. Further down the library file, you’re introduced to the State class, _MyAppState (second screenshot below). Here, we encounter the first class from the state_extended package, AppStateX.

my_app.dart

my_app.dart

part04_app_statex.dart.dart

A State of Control

In the second screenshot above, you can see how SOC’s are assigned to a State object. Generally, the State object worries about the interface, while those ‘State Object Controllers’ worry about everything else. In the last screenshot above, you can see that the AppStateX class even worries about Error Handling as well as activates its built-in InheritedWidget (more on that later). The AppStateX class is an extension of another class from the state_extended package called StateX.

Build Better

Of course, the AppStateX class has a build() function, but you’re discouraged from using that function in your apps — not directly anyway. However, in this example app, I’ll use it to assign boolean values to Flutter’s development tools. The first screenshot below shows you how that’s done. The video demonstrates those tools. Further note in the first screenshot, the return statement calls AppStateX’s own build() function. In turn, that eventually calls the builder() function (second screenshot) where additional ‘development tool’ values are taken in by the MaterialApp widget.

You see, you’re to ignore the build() function in a StateX object class. You would use the builder() function instead to take full advantage of StateX’s functions and features. If you do use the build() function, it’s then just like another State class.

my_app

my_app

The Three Count

The Page1 widget is presented to the user when the app starts up. The second screenshot below conveys the Controller objects instantiated for its State object, Page1State. It’s the third screenshot that displays not one but three counters——each conceived in a different way.

my_app.dart

page_01.dart

A Separate State

In the video below, the counter is incremented three times. Note, the Text widget and the setBuilder() function will increment, but the third counter residing in a separate StatefulWidget does not. Perfectly normal.

page_01.dart

part06_inherited_widget_state_mixin.dart

As you know, in Flutter, a State object’s setState() function will have its build() function called again in the next frame cycle. The video above demonstrates this. The first screenshot above is the example app’s code in the builder() function that’s run again and again with every press of the button. The second screenshot above is the inner workings of the StateX object. You can see it’s there where the builder() function is called. Since the _useInherited variable is set to false, the builder() function is called every time. However, if the variable is set to true, the builder() function is not called ever again! Let’s see how that works below.

Inherited Performance

In the video below, things have changed. When you ‘turn on’ the built-in InheritedWidget (I’ll explain how that’s done shortly), the _useInherited variable is set to true in the StateX object. The first screenshot below highlights what that means.

part06_inherited_widget_state_mixin.dart

page_01.dart

Build Once And Forget It

The builder() function is only called once and is then wrapped in the built-in InheritedWidget. That means that Text widget will never be called and rebuilt again — it doesn’t increment with every push of the button anymore. You see, the whole screen is no longer rebuilt again and again. Do you follow? However, the other two counters do increment. That’s because they are both dependencies of the StateX object’s built-in InheritedWidget. Only those widgets are rebuilt in the interface making for better performance. This is nothing new in Flutter.

In the second screenshot below, we see the setBuilder() function is very much like the Builder widget supplies a BuildContext parameter and returns a widget. This function is from the Mixin found in the state_extended package called InheritedWidgetStateMixin. We’ve been looking at its inner workings for some time now with its buildF() function (first screenshot below), but now we’ll examine its setBuilder() function.

part06_inherited_widget_state_mixin.dart

part04_app_statex.dart

part04_app_statex.dart

In the second screenshot above, we’re presented with the setBuilder() function. Now you see why the counter is incremented whether the useInherited getter returns true or false — the builder() function is called in either case. Looking at the third screenshot, if set to true, the builder() function is wrapped in the StatelessWidget, StateDependentWidget. It is that widget that becomes a ‘dependent’ of the built-in InheritedWidget using the line:

stateMixin?.dependOnInheritedWidget(context)

I’d say the most powerful feature of Flutter’s InheritedWidget is that whenever it’s called again, its dependency widgets will be rebuilt (their build() functions called again). Nothing new here. It’s all Flutter.

The third and final counter is also incremented. As you see in the first screenshot below, it’s the one encased in a StatefulWidget. Its State object is made an dependent on Page1State’s built-in InheritedWidget as well. In the third screenshot, it’s the Controller object’s buildThisState() function that assigns this dependency.

page_01.dart

page_01.dart

Less Is More

Let’s look at another example. Viewing the video below, know it is only the red ‘word pairs’ being rebuilt in set intervals — it’s the only portion of the screen changing every few seconds. That is the only portion of the screen being repainted. The rest of the screen is left untouched. The less the Widget tree is rebuilt, the better the overall performance. In the screenshot below, you see it’s the getter, wordPair, that supplies these word-pairs. I’ll show how this works next.

page_01.dart

In the first screenshot below, the getter uses the SetBuilder widget. This widget is also from the state_extended package. It involves the App State object’s dataObj property. It’s of type object, and so you can pass ‘anything’ down the Widget tree. In the case of this example app, it’s a String object that is passed down. See the second screenshot below.

word_pair_timer.dart

my_app.dart

The SetBuilder widget rebuilds when that String object changes in value. In the first screenshot below, that occurs when a new word-pair is assigned. Finally, the setter, dataObject, calls the AppStateX’s InheritedWidget with the notifyClients () function. Doing so, will rebuild any and all SetBuilder widgets — in fact, any widget set as a dependency to the AppStateX’s InheritedWidget. Makes for an efficient interface.

word_pair_timer.dart

part17_app_state_mixin.dart

Note The Change

Finally, the video below demonstrates what happens with the three counters when selecting the Switch, ‘Use built-in ChangNotifier.’ All three counters now increment. It’s easy enough to understand the first two. We’ve returned to rebuilding the whole interface again. Both the Text widget and setBuilder() function will rebuild, but why would the third counter in its separate StatefulWidget? It shouldn’t increment — it’s no longer dependent on an InheritedWidget?! That’s because it’s become a Listener. State Object Controllers have a built-in ChangeNotifier.

controller.dart

part12_rebuild_controller_states_mixin.dart

In the first screenshot above, you can see the Controller’s buildThisState() function will also include Page1’s State object as a ‘State Listener’ if the variable, useChangeNotifier, is set to true. That’s exactly what happens in the video above.

rebuild = addStateListener(state);

In the second screenshot above, you can see the State object’s setState() function is called when that Listener is notified. They’re notified through the Controller which had added them. This is done in the Controller’s version of the setState() function. Note, in the first screenshot below, its setState() function also includes notifying any Listeners:

notifyListeners();

The next two screenshots trace back to Flutter’s own ChangeNotifierclass calling its notifyListeners() function and ‘rebuilding’ any and all State objects assigned. See how that works? Just another way of accessing and rebuilding dispersed and remote State objects — via a State Object Controller.

part02_statex_controller.dart

part12_rebuild_controller_states_mixin.dart

part12_rebuild_controller_states_mixin.dart

It’s a Do-over

To activate its built-in InheritedWidget, a StateX object must be instantiated with the formal parameter, useInherited, set to true. Notice in the video below, there’s a distinct ‘refresh’ of the screen when the Switch, Use built-in InheritedWidget, is tapped on. That’s because the App’s whole interface is rebuilt again. Note, it’s also rebuilt again with every change of those Development tools settings introduced earlier. The App’s interface is rebuilt when demonstrating much of this example app’s functionality. That means the MaterialApp widget in this example app is being called again and again (see the second screenshot below). Let’s how that’s done.

page_01.dart

my_app.dart

In the first screenshot below, selecting the Switch calls the Controller’s method, onChangedInherited(). Two things need to happen to then activate the StateX object’s built-in InheritedWidget: the whole app interface must be rebuilt and that particular StateX object must be recreated.

page_01.dart

controller.dart

A New State

In Flutter, State objects are recreated when their Statefulwidget is assigned a ‘new’ Key value. In the second screenshot above, that’s accomplished with the line, page1Key = null;. Again, nothing new here. This is how Flutter works. The second thing to do is simply rebuild the App’s State object:

appState?.setState((){});

Every Controller has a reference for the App’s State object. In the first screenshot below, we see the App’s State object’s builder() function called again. Again, changing any Development tool settings introduced earlier requires the line, appState?.setState((){});.

my_app.dart

page_01.dart

controller.dart

In the second screenshot above is the StatefulWidget, Page1. Note, if not explicitly provided one, its Key value is supplied by the screen’s Controller, Controller().page1Key. Remember, however, it was set to null with a tap of a Switch. This means the Key value stored in the class property, _page1Key, has now changed to a completely new value. In Flutter, this causes the State object to be recreated. Being recreated, the value passed to this new State object is now set to true: Controller().useInherited

Keep Control

If you haven’t noticed by now, each State Object Controller class in the example app uses a factory constructor to instantiate only one instance of each class. Three of those classes are conveyed in the screenshots below.

Again, not a hard-and-fast rule, but I’ve found keeping only one instance of the State Object Controllers has proven well-suited for their role: A class object representing the ‘ongoing state’ of the app in a singular instance. An example of this, of course, is the running count that’s displayed. Its ‘interface settings’ is also maintained by a Controller. Further advantages in using this Singleton Pattern will be explained shortly.

controller.dart

word_pair_timer.dart

example_app_controller.dart

Keep Count

Since it’s a complete recreation of the State object and its counter property, count, we will have to save the count before the switch. Of course, the Controller object will take care of all of this. In the first screenshot below, using its stateInit(State state) function, a controller can determine the StateX object it’s currently working with, and in this case, supply the count to the new screen. Otherwise, after the switch, you would just see zeros. Makes sense?

Make A New Start

Note, the video below further demonstrates this ‘rebuilding of the App’ with the line, appState?.setState((){});.In the video, you see the ‘debug banner’ is turned off and then changing the app’s whole theme from Material3 to Material2. Both required a new call to the App’s MaterialApp widget.

controller.dart

page_01.dart

Learn by Example

This is a ‘demonstration’ app as well. And so, as you see in the second screenshot above, the Page1 State object’s initState () function has many examples of how this state_extended package empowers you to make versatile and adaptive code by giving you more than one way to retrieve a State object that makes up your App from anywhere at anytime.

A Singular Approach

By the way, another reason for the Singleton Pattern used by instantiating all the State Object Controllers with a factory constructor is to take advantage of the versability and readability that’s achieved. You can then make any number of construction calls necessary to perform specific tasks throughout the app. An example of this is next.

It’s All Timing

As you many know, it was the WordPairsTimer controller that was providing those red ‘word pairs’ at set intervals using its getter, wordPair. In the first screenshot below, you see this controller follows the Singleton pattern. In the second screenshot below, it’s instantiated as one of the controllers to work with the first screen, Page1State, displaying the word-pairs.

However, this App has a Drawer widget, and whenever it opens, I want to turn off the Timer that’s changing the word-pairs every 5 seconds. The ‘easiest’ means to do this is presented in the third screenshot below — just call the constructor again. The Singleton pattern approach makes this possible.

word_pair_timer.dart

page_01.dart

my_app.dart

Adaptive Abstraction

Those construction calls for the WordPairsTimer controller right there in the first page was more for the benefit of the reader. In truth, I would normally only reference the State object’s controller, con, throughout that interface. This would then incorporate a layer of abstraction to the app. You can see an example of this in the first screenshot below. The interface has no business knowing there’s a word-pair timer running somewhere, and is turned off just before the Drawer is opened. See what I changed I made in the first screenshot below to achieve this.

my_app.dart

example_app_controller.dart

controller.dart

The second screenshot above is of the App’s State object controller, ExampleAppController. You see the new onOpenDrawer() and onCloseDrawer() functions. They, in turn, call their counterparts found in the Controller class. Finally, we see those functions, in the third screenshot above, calling the original methods found in the WordPairsTimer controller. See how that works?

Unknown Potential

Back on the interface side, there’s no hint of what’s going on when the Drawer callback functions fire (see first screenshot below). The WordPair Timer process could be modified down the road or removed outright without changing one bit of the interface. It’s the use of these State Object Controllers that provides this layer of abstraction and makes your code that much more adaptive and its maintenance that much easier.

The Many Constructs

The second screenshot below conveys another example of using multiple constructor calls from the same class. We’re back setting the built-in InheritedWidget on or off with the controller property, useInherited, for the Page 1 screen. Note the readability. Flutter doesn’t allow dynamic instance variables in the class. The initializer list can only initialize instance variables before the constructor body runs. and so this a more.

my_app.dart

page_01.dart

Handle Your Errors

You hope for no errors in your app. However, your app will error. You have to prepare for this fact. The StateX package allows you to prepare for this fact whether they’re unexpected errors or anticipated errors at times. Maybe there’s a case where intermittent errors are known to occur in your app — a network drop is known to occur, for example. In certain cases, a process is known to fail. This package gives the means to acknowledge such errors and shut down gracefully if not actually overcome the error and continue running.

As an example, in the video below, the app intentionally throws an anticipated error with every push of the counter button (see screenshot below). This example app logs many of the events that occur while running, and you can see the error is recognized in the second screenshot. An onError() method fires in a series of State objects all the way back to the App’s State object, _MyAppState. One of those methods actually recovers from this Exception by incrementing the count regardless.

page_01.dart

In truth, it is the App’s State object, _MyAppState, that catches the error. It then begins a series of onError() function calls. It first calls the State object where the error likely originated. When such a State object is found, its Controllers’ onError() methods are called first before calling its onError() method. It is the App’s State object that finally records and logs the error.

Back to our example, the third screenshot below highlights when an onError() method in one of the Controllers recognizes the error and increments the counter regardless. A very simple example, but you get the idea.

part04_app_statex.dart

part04_app_statex.dart

controller.dart

A Better View

This is not a built-in feature of the state_extended package, but a further demonstration of how to seamlessly implement a feature using a State Object Controller. Instead of the ‘Red Screen of Doom’ that comes with errors while developing in Flutter, how about a more useful, more pleasant-looking error screen? The BuildErrorWidget class integrates such a screen by merely introducing it as a controller to the App’s State object. In the video below, the class allows you to control its use and returns to the original error screen when appropriate while developing, while testing, and while running in production.

The video below demonstrates this with an error explitcity thrown when going to Page 2 in the example app. You’re able to manually ‘trip errors’ in this example app to demonstrate this package’s error handling. In the video, you see the traditional ‘Red Screen of Doom.’ However, when the ‘Custom Error Screen’ is switched on, the BuildErrorWidget class will assign a custom builder to Flutter’s ErrorWidget.builder callback. Now, when such an error occurs, a more helpful error screen is displayed when the intended widget fails to build. Very nice.

my_app.dart

build_error_widget.dart

Trouble From The Start

The video below throws an error right at the start of the app — in the AppStateX’s initAsync() function. It’s usually at startup when things have trouble — more of an effort may be needed to start successfully. In the video, the example app starts up without incident. However, the app is repeatedly restarted with specific settings switched on to show what the package can do for you if and when your app encounters an error right at startup.

The ‘Error initAsync() at Startup’ switch is first turned on. When the app starts up again, you are greeted by the good ol’ ‘Red Screen of Boom!’ Nothing left for you to do but try the app again. Happily, you’re able to get into the app again because it actually ‘recovers’ this time. A snackbar appears with the message, ‘An error at startup, but was caught.’ Going back to the Settings screen, you’ll notice the ‘Catch Error’ option is set, Yes. However, in the video, it is set back to ‘No’ so to again demonstrate the custom error screen with another restart. Again, you can’t continue as the app has once again failed at startup, and you’re presented with an error screen. That’s ok. That’s because the app will catch the error the next time it starts up. The error is overcome once again. Let’s see how.

This video emulates, for example, when the network is down at startup or when it fails to access some critical web service at startup. The app can’t continue — unless it can. That’s where the catchAsyncError() function comes in (see the second screenshot below). The catchAsyncError() function below looks out for an error message containing the string, ‘Error in App’s initAsync()!’ It then acts accordingly. If this function returns false, the error is not successfully caught, it has failed to recover, and so an error screen is presented. Note, when not caught, the property, catchInitAppAsyncError, is set to true for next time. Otherwise, the app will never start up successfully. Smart thinking.

part05_futurebuilder_state_mixin.dart

example_app_conroller.dart

The first screenshot is of state_extended package itself. It’s a snapshot of the build() function called by the StateX class. Highlighted is the catchAsyncError() function call. Your initAsync() function has thrown an error, but you’re given an opportunity to possibly recover from the error with the catchAsyncError() function.

Of course, always returning true and freely ignoring errors would not be advisable. Such decisions will very likely only make your App fail further. It’s for those exceptions where a recovery may be possible or, at the very least, an opportunity to clean up resources and prevent any data corruption before bringing the crash to a ‘soft landing’ and have the app fail gracefully.

To Catch An Error

In the next video below, you have the means to throw an error when attempting to open Page 2 in the example app. Again, the custom error screen is first demonstrated. However, it’s the ‘Catch Error’ option that’s of interest here. When that is switched on, the catchAyncError() function in the second screenshot below will ‘catch’ the error and return true.

another_controller.dart

another_controller.dart

To Err Is Programming

This example app has throw statements throughout its code. Again, this is to demonstrate this package’s ability to effectively handle errors. Another example is when the ‘Error in Builder’ is switched on. The first screenshot below shows an Exception being thrown. This simulates a Widget failing to be created in a build() function.

page_02.dart

build_error_widget.dart

To Build Or Not To Build

As you know now, any Asynchronous operations that may fail in the initAsync() function may be caught by the catchAsyncError() function. However, an error in a build() function can not be explicitly caught. No, if your build() function fails, it’s an error that should *not* happen in production. It’s an error, it will be recorded and logged, but it is not recoverable. Of course, it’s your app, you can address such errors right in your build() function and maybe even recover from it yourself — there’s just no ready means in the StateX class.

Quick Reference

StateX Class

.appStateX → Retrieves the App’s appStateX object. Returns null if not available.StateX

.controller → Retrieves the State object’s ‘current’ Controller.StateX

.deactivated → True if deactivated() was called.StateX

.disposed → True if disposed() was called.StateX

.firstBuild → True if build() not called or called once.StateX

.hiddenAppLifecycle → True if State object was in a ‘hidden’ state.StateX

.identifier → Unique Srtring identifier foe this StsteX object.StateX

.inactiveAppLifecycle → True if the State object was in an inactive state.StateX

.isLastState → True if it’s the ‘latest’ StateX object created in the app.StateX

.lastSystemEvent → Records the last system event called.StateX

  1. didChangeAccessibilityFeatures
  2. didChangeLocales
  3. didChangeMetrics
  4. didChangePlatformBrrightness
  5. didChangeTextScalFactor
  6. didHaveMemoryPressuure
  7. didRequestAppExit

.pausedAppLifecycle → True if State object was in a paused state.StateX

.resumedAppLifecycle → True if State object was in a resumed state.StateX

add() → Add a Controller to this StateX object.StateX

addAll() → Add a list of Controllers to be associated with this StateX object.StateX

stateByType() → Retrieves another State object by its type. Returns null if not found.StateX

stateById () → Retrieve another State object using its unique String identifier. Returns null if not found.StateX

controllerByType () → Retrieves one of its Controllers by their type.StateX

controllerById () → Retrieves one of its Controllers by their unique String identifier.StateX

StateXController Class

.state → Returns current State object.StateXController

.statex → Retrieves current StateX object.StateXController

.appStateX → References the App’s State object.StateXController

.initStateCalled→ True if initState() was already called.StateXController

activateState () → Called when this State object is reinserted into the Widget tree after having been removed by deactivate().StateXController

addState () → Associate this Controller to the specified State object. Returns the State Object’s unique String identifier.StateXController

deactivateState () → Called whenever the State object was removed from the Widget tree. Passes the ‘current’ State object parameter.StateXController

disposeState (covariant State state) → The supplied State object will never build again and taken into Flutter’s garbage collection.StateXController

initAsyncState (covariant State state) → Called with every StateX associated qith this Controller initializing any ‘time-consuming’ operations needed done at startup.StateXController

ofState )() → Retrieves a currently associated State object by its type T. Returns null if not found.StateXController

stateOf () → Retrieves an associated State object by its StatefulWidget. Returns null if not found.StateXController

Initialization Handlers

initAsync () → Initialize any ‘time-consuming’ operations at the beginning. Implement any asynchronous operations needed done at start up.StateX|StateXController

initAsyncState () → Called with every StateX associated with this Controller Initialize any ‘time-consuming’ operations at the beginning. Implement any asynchronous operations needed done at start up.StateX|StateXController

initState () → Initialize anything necessary at the beginning. Implement any asynchronous operations needed done at start up.StateX|StateXController

runInitAsync () → Call initAsync() all the time if returns true. Conditional calls initAsync() creating a Future with every rebuildStateX|StateXController

Event Handling

activate () → Called when the State object is reinserted into the Widget tree.StateX|StateXController

deactivate () → Called when the State object is removed from the Widget tree.StateX|StateXController

dependOnInheritedWidget () → Link a widget to the StateX Object’s InheritedWidget.StateX|StateXController

detachedAppLifecycleState () → The Flutter engine has detached the app from view and is shutting down.StateX|StateXController

didChangeAccessibilityFeatures () → Called when the current accessibility features have been changed.StateX|StateXController

didChangeDependencies () → Called immediately after initState() or when a widget dependent on an InheritedWidget is to be rebuilt.StateX|StateXController

didChangeLocales () → Called when the device’s locale has changed.StateX|StateXController

didChangeMetrics () → Called when the app’s UI dimensions change. E.g. when a phone is rotated.StateX|StateXController

didChangePlatformBrightness () → When the device’s brightness has changed.StateX|StateXController

didChangeTextScaleFactor () → Called when the platform’s text scale changes.StateX|StateXController

didHaveMemoryPressure () → Called when the system is running low on memory.StateX|StateXController

didPop () → Called when this State is popped off a route.StateX|StateXController

didPopNext () → The top route has been popped off returning this State object.StateX|StateXController

didPopRoute () → Called when the app pops the current route. E.g. On Android, called when the back button is presse.StateX|StateXController

didPush () → Called when this State is first pushed as a Route by a Navigator.StateX|StateXController

didPushNext () → New route has been pushed, and this State object’s route is no longer current.StateX|StateXController

didPushRouteInformation () → Called when the application pushes a new RouteInformation and a restoration state onto the router.StateX|StateXController

didRequestAppExit () → Called when a request is received from the system to exit the app. Exiting the app can proceed with [AppExitResponse.exit] response, and do not exit with [AppExitResponse.cancel] response.StateX|StateXController

didUpdateWidget () → Called when the State object’s StatefulWidget is recreated.StateX|StateXController

dispose () → Called when the [StateX] object will never build again and will be taken into Flutter’s garbage collection.StateX|StateXController

handleCancelBackGesture () → Called when a predictive back gesture is canceled indicating that no navigation should occur.StateX|StateXController

handleCommitBackGesture () → Called when a predictive back gesture is finished successfully indicating that the current route should be popped.StateX|StateXController

hiddenAppLifecycleState () → The app is about to be paused (on iOS and Android), or has been minimized (Windows) or is no longer visible (on the web).StateX|StateXController

inactiveAppLifecycleState () → The app is in an inactive state and is not receiving user input.StateX|StateXController

pausedAppLifecycleState () → The application is not currently visible to the user, not responding to user input, and running in the background.StateX|StateXController

reassemble () → Called when the app is reassembled during development, E.g. A hot reload.StateX|StateXController

resumedAppLifecycleState () → The app has returned from the background. Is now visible and ready for user input.StateX|StateXController

setState () → Call build() function of the State object in the next frame cycle.StateX|StateXController

updateShouldNotify () → As a dependency of an InheritedWidget, return true to rebuild the State object.StateX|StateXController

Error Handler

onError () Offer an error handlerStateX|StateXController

onAsyncError () In case the initAsync() function has failed in error. This must be handled too. StateX|StateXController

catchAsyncError () Catch it if initAsync () throws an error. Return true to indicate the error handled. Return false to continue the error handling StateX|StateXController

Next article will cover the following topics:

Event Handling

Remember, if not addressed, the example app’s Timer will always be running taking up valuable CPU cycles even when those red Wordpairs are no longer in view. There are event handlers available to the StateX class to address such considerations. The screenshots below and see the Timer is actually terminated when moving off Page 1. Nice.
The didPushNext() method is called when the ‘next route’ is pushed from this current State object. Note, the _cancelTimer() is called terminating the Timer before the Page 2 screen is displayed. Also, if there’s an error, you can see the Timer is terminated in the onError() method. Very nice.

word_pair_timer.dart

page_01.dart

word_pair_timer.dart

pat01_statex.dart

part01_statex.dart

Testing Your App

If I were you, a great way to get to know StateX would be to step through its accompanying test files that run whenever a new version of its package is pushed up to its GitHub repository.

The screenshots below give an idea of what will be covered in the next article.

test_widget.dart

intergration_testing.dart

Unit Testing

test_app_state.dart

test_statex.dart

test_statexcontroller.dart

test_run_mixins.dart

test_event_handling.dart

test_event_handling.dart

test_event_handling.dart

reset_page1_count.dart


This content originally appeared on DEV Community and was authored by Greg Perry