Skip to main content

Global State

"Global state" refers to any logic shared throughout the whole app, e.g. authentication, database management or user settings. It's convenient to break this logic up into smaller pieces ("Global states"), adhering to the Single Responsibility Principle. Each one can be represented by a standalone hook, and then can be depended on by other global or local states.

Creating global states

General layout of a global state is similar to Local state, and consists of:

  1. State class which contains the entirety of the Data that needs to be accessible by the consumers of the global state and Actions (functions) that can be performed on it.
  2. Hook which returns the State and reacts to its Actions.
class AuthState {
final User? user;
final Future<void> Function(User) logIn;
final Future<void> Function() logOut;

const AuthState(/* ... */);

bool get isLoggedIn => user != null;
}

AuthState useAuthState() {
final userState = useState<User?>(null);

Future<void> logIn() async {
// ...
}

Future<void> logOut() async {
// ...
}

return AuthState(user: userState.value, logIn: logIn, logOut: logOut);
}

Consuming global states

A dependency on a global state is created using the useProvided hook, which causes the hook to be rebuilt whenever any of the dependencies change. This allows higher-level global states to depend on lower-level ones, creating a hierarchy of dependencies with local states at the bottom. It's recommended to place useProvided calls together at the top of the hook, making it easier to reason about the structure of the dependencies:

// Either global or local state hook
MyState useMyState() {
final stateA = useProvided<StateA>();
final stateB = useProvided<StateB>();

// ... rest of the logic
}

Registering global states

Global states are usually registered by wrapping the MaterialApp in HookProviderContainerWidget:

class MyApp extends StatelessWidget {

Widget build(BuildContext context) {
return HookProviderContainerWidget(
providers: {
AuthState: useAuthState,
// ... other global states
},
child: MaterialApp(
// ...
),
);
}
}

The order of declaration of the global states is important - a state can only depend on states declared before it.

INVALID:

providers: {
StateB: useStateB, // depends on StateA
StateA: useStateA,
// ...
}

See also

  • Testing - A guide on unit-testing and integration-testing global states.
  • Firebase Auth example - An example of managing global state of Firebase Auth using hooks.