Routes topic

A TreeStateRouter has a list of routes, each of which provides visuals for a specific state in a state tree. These routes come in several varieties, described below.

StateRoute

A StateRoute provides visuals for a plain state (that is, not a data state). When the state is active in the state tree, TreeStateRouter will call the builder from the route to obtain the Widget that displays the state, and place it on top of the navigation stack.

The builder function is provided a StateRoutingContext that may be used to post messages to the state machine in response to user input.

For example:

StateRoute(
   // This route provides visuals for state1.
   States.state1,
   // This builder function creates a widget that displays state1.
   routeBuilder: (BuildContext ctx, StateRoutingContext stateCtx) {
      return Center(
         child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
               const Text('This is state 1'),
               ElevatedButton(
                  // When the button is pressed, send a message to the state machine
                  onPressed: () => stateCtx.currentState.post(AMessage()),
                  child: const Text('Send a message'),
               )
            ],
         ),
      );
   },
),

There are several related convenience classes (StateRoute1<DAnc>, StateRoute2<DAnc1, DAnc2>, etc.) that work in a similar way to StateRoute, but provide data from ancestor data states to the route builder functions.

DataStateRoute

DataStateRoute<D> provides visuals for a data state, and works in a similar manner to StateRoute. The main difference is that the routeBuilder and routePageBuilder functions for a DataStateRoute are provided the current value of the state data for the state, allowing it to be used when displaying the state. If the state data changes as a result of message processing by the state machine, the builder function will be called again with the updated data.

class CounterData {
  CounterData(this.counter);
  final int counter;
}

class States {
  static const counting = DataStateKey<CounterData>("counting");
}

var stateTree = StateTree(
    InitialChild(States.counting),
    childStates: [
      DataState<CounterData>(
        States.counting,
        InitialData(() => CounterData(1)),
      )
    ],
  );


var router = TreeStateRouter(
  stateMachine: TreeStateMachine(stateTree),
  routes: [
    DataStateRoute<CounterData>(
      States.counting,
      // The route builder for a DataStateRoute is provided the current state data 
      routeBuilder: (BuildContext ctx, StateRoutingContext stateCtx, CounterData data) {
         return Center(
            child: Text('The value is ${data.counter}'),
         );
      },
    ),
  ],
);

There are several related convenience classes (DataStateRoute2<D, DAnc>, DataStateRoute3<D, DAnc1, DAnc2>, etc.) that work in a similar way to DataStateRoute, but provide data from ancestor data states to the route builder functions.

Shell Routes

Both StateRoute and DataStateRoute provide shell factory methods. These factories permit a route for a parent state to provide page content that wraps the visuals of its descendant states. In other words, the parent route can provide a common layout or 'shell' that is wraps the visuals of its descendant states.

When calling the shell method, a list of routes corresponding to descendant states must be provided. Additionally, the routeBuilder or routePageBuilder functions accept a nestedRouter widget that reprents the visuals for active descendant states. The builder implementation can the decide where in its layout it would like to place that content.

StateRoute.shell(
   States.parent,
   routeBuilder: (_, __, nestedRouter) => Scaffold(
      body: Center(
         child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
               Text('This the parent'),
               // Visuals for descendant states appear here
               Expanded(child: nestedRouter),
            ],
         ),
      ),
   ),
   routes: [
      StateRoute(States.child1,
         routeBuilder: (_, __) => Center(child: Text('This is child1'))),
      StateRoute(States.child2,
         routeBuilder: (_, __) => Center(child: Text('This is child2'))),
   ],
)

Both StateRoute and DataStateRoute provide popup factory methods. When using these factories, the visuals produced by the routeBuilder are displayed in a modal popup. As a result, the visuals should be sized such that they do not occupy the entire available screen space, otherwise the popup effect will not be obvious.

StateRoute.popup(
   States.edit,
   routeBuilder: (context, stateCtx) => Container(
      padding: const EdgeInsets.all(10),
      child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         mainAxisSize: MainAxisSize.min,
         children: <Widget>[
         const Text('This is a popup'),
         // In order to dismiss the popup, post a message to the state machine to cause a 
         // state transition.
         button('Done', () => stateCtx.currentState.post(Messages.endEdit)),
         ],
      ),
   ),
);

Machine Routes

The StateRoute.machine factory constructs a route the can display visuals for the active states in a nested state machine. The factory is similar to the StateMachine.shell factory, in that requires a list of routes corresponding to states in the nested state machine. Also, the routeBuilder function is provided a nestedRouter that represents the visuals for the active states in the nested state machine, as well as a MachineTreeStateData that provides access to nested machine.

Refer to the MachineState documentation for more details on machine states,

Classes

DataStateRoute<D> Routes
A route that creates visuals for a data state in a state tree.
StateRoute Routes
A route that provides visuals for a state in a state tree.