Core concepts


The following is an exploration of what State is conceptually and how they are used in Flutter Forge.


State is effectively all the types of data that our Widget cares about in terms of presenting as well as interacting. In the case of a simple Counter Component, this is just the count. In more complex components that are based on user interactions that can fail, this could be data keeping track of the status of a process, or a variety of other things.

In Practice

We generally interact with state in the following ways.

Define state

To define State for a Component we simply define a @immutable class that generally extendsEquatable as State needs to be value equatable.

In the following example we define a State class named CounterWidgetState that holds a single value of count. Note: We have to make the State Equatable so that Flutter Forge can properly tell when the state has changed. If you aren't familiar with the equatable package, it is simply a package that streamlines the process of making a class Equatable via the required props getter.

class CounterWidgetState extends Equatable {
  const State({required this.count});

  final int count;

  List<Object> get props => [count];

Note: Adding copyWith to the State class is very common as it eases making changes to the State in an immutable fashion within the Reducer. Please also see the section below, Mutate State, to gain more context.

Access state

Generally you access the State by using either the Rebuilder or SelectRebuild that facilitate rebuilding a portion of your Widget tree when the State changes. It exposes the read-only, managed State instance to the builder function via the state parameter.

  store: store,
  builder: (context, state, child) {
	return Text(
	  style: Theme.of(context).textTheme.headline4,

In the above example we are accessing the CounterWidgetState's count property with or Text widget via state.count.

Mutate state

The other way we interact with State is by making changes to it. In Flutter Forge the only way to make changes to the state is through the Store and in turn the Reducer. The Reducer`s job is to interpret Actions and compute the new instance of State.

final counterReducer = Reducer<CounterWidgetState, CounterWidgetEnvironment, CounterWidgetAction>(
    (state, action) {
  switch (action) {
  	case CounterWidgetIncrementButtonTapped _:
		return ReducerTuple(CounterWidgetState(count: state.count + 1), []);

In the above example we have a simple Reducer for the Counter Component. It takes in Actions and checks if the action is a CounterWidgetIncrementButtonTapped action. If it is then it returns a ReducerTuple containing a newly constructed instance of the CounterWidgetState with a count equal to the previous count, plus one.