The modular state management solution for flutter.

Last update: Jan 5, 2022

logo

The modular state management solution for flutter.

  • Easy debugging : each event is predictable and goes into a single pipeline
  • Centralized state : solid state validation
  • Power of async generators : writing asynchronous code is easy thanks to Dart generators

Quickstart

The global logical state of our application :

class CounterState {
  factory CounterState.initial() => const CounterState(0, 10);
  const CounterState(this.count, this.max);
  final int count;
  final int max;
  bool get isMax => count >= max;
}

The available actions that alterate the logical state of the application :

class AddAction extends Action<CounterState> {
  const AddAction(this.value);
  final int value;
  @override
  Stream<Updater<CounterState>> call(
    Context<CounterState> context,
  ) async* {
    yield (state) => CounterState(min(state.count + value, state.max), state.max);
  }
}

class ResetAction extends Action<CounterState> {
  @override
  Stream<Updater<CounterState>> call(
    Context<CounterState> context,
  ) async* {
    yield (state) => CounterState(0, state.max);
  }
}

class SaveAction extends Action<CounterState> {
  @override
  Stream<Updater<CounterState>> call(
    Context<CounterState> context,
  ) async* {
    await File(_cachePath).writeAsString(context.state.count.toString());
  }
}

class LoadAction extends Action<CounterState> {
  @override
  Stream<Updater<CounterState>> call(
    Context<CounterState> context,
  ) async* {
    final content = await File(_cachePath).readAsString();
    final count = int.parse(content);
    yield (state) => CounterState(min(count, state.max), state.max);
  }
}

The view that subscribes to the state changes and dispatches actions :

class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Builder(
              builder: (context) {
                /// This widget will be rebuilt each time the `count` value changes
                final count =
                    context.select((CounterState state) => state.count);
                return Text(
                  '$count',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Builder(
        builder: (context) {
          /// This widget will be rebuilt each time the `isMax` value changes
          final isMax = context.select((CounterState state) => state.isMax);
          if (isMax) {
            return FloatingActionButton(
              /// The action is executed in the [ApplicationContext].
              onPressed: () => context.dispatch<CounterState>(ResetAction()),
              tooltip: 'Reset',
              child: Icon(Icons.delete),
            );
          }

          return FloatingActionButton(
            /// The action is executed in the [ApplicationContext].
            onPressed: () => context.dispatch<CounterState>(AddAction(1)),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          );
        },
      ),
    );
  }
}

The state initialization at the root of the tree with the Fountain widget :

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Fountain(
      initialState: (context) => CounterState.initial(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

Core concepts

schema

Context

The application context maintains a unique global logical immutable state for the application.

The context also contains all the middlewares that will process the dispatched event.

The application context is provided to the widget tree from a Fountain so that any descendent widget can observe a property of the state with the select extension method. It can also dispatch events into the middlewares with the dispatch extension methods.

Middleware

The application middleware process the dispatched Events and can produce state updates.

They are composable by nature which means that each middleware can wrap another middleware.

Event

The events are inputs for middlewares. They can describe a user action, or a system event for exemple. They are processed by the middlewares which can produce new application states.

Included middlewares

To import middlewares, use the import 'package:fountain/middlewares.dart'; directive.

Actions

By default, the framework includes a ActionExecutor middleware that allows to define Action s which are then invoked directly to produce new states.

Defining an action

To create custom actions, inherits from Action and implement all of the update logic in the call method. Since the method returns a Stream, a convenient way to implement the logic is often by using async * generators which allows to yield a sequence of state updates.

class RefreshAction extends Action<MyApp> {
  const RefreshAction();

  @override
  Stream<Updater<CounterState>> call(
    Context<CounterState> context,
  ) async* {
    if(!context.state.isLoading) {
        yield (state) => state.copyWith(
            isLoading: true,
        );

        final news = await Api.instance.getNews();

        yield (state) => state.copyWith(
            isLoading: false,
            news: news,
        );
    }
  }
}

Note that the actions aren't yielding states directly, but Updaters. This is to insist on the fact the the initial state may have changed during the action execution, and therefore, it must be taken into account when updated.

Logging

The framework also includes a Logging middleware that logs all events and state updates.

Fountain(
    middlewares: <Middleware<CounterState>>[
        Logging<CounterState>(),
        ...Fountain.defaultMiddlewares<CounterState>(),
    ],
    // ...
);

ErrorHandler

This middleware catches all unmanaged exceptions from middlewares below it and dispatches new events if so.

Fountain(
    middlewares: <Middleware<CounterState>>[
        ErrorHandler<CounterState>(
            (context, event, initialState,error,stackTrace) {
                // An unknow error occured during event processing
                return DisplayAlertAction('Sorry, an error occured');
            },
        ),
        ...Fountain.defaultMiddlewares<CounterState>(),
    ],
    // ...
);

About

Wait ... yet another state management solution for Flutter ?

Fountain is not so new to me, I've used this approach for a longtime now. Centralizing it as an opensource library makes a lot of sense to create a standard for a all of my personal and professional projects.

Inspired by

This project stands on the shoulders of giants, with the intention of reducing boilerplate, being minimalist and simple at its core.

  • Redux for the functional update cycle and general principles, with the purpose of having less boilerplate by being a bit more opiniated.
  • Express | Koa for their composability and modularity thanks to middlewares.
  • Bloc for its use of Streams.
  • Provider for its select method.

How is it different from other popular solutions ?

  • Provider : Fountain is more opiniated than Provider, and thus Provider is a more general purpose tool. Provider is often used in combination with a ChangeNotifier to create a simple state management solution.
  • Redux : Fountain has the same overall philosophy than Redux with Thunks but brings less boilerplate when dealing with asynchronous logic with ApplicationActions logic by using Streams. Fountain also brings the middleware concepts.
  • Bloc : Fountain use a global state instead of various Blocs. We believe it is important since there's a lot of areas which need to be aware of another part of your app logic. This makes also persistence, testability easier to deal with.

GitHub

https://github.com/aloisdeniel/fountain
You might also like...

Example of use bloc + freezed with a state that contains a list

blocfreezedlistexample A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you s

Mar 21, 2022

Timer based on provider state manager

timer_provider Timer based on provider state manager Getting Started This project is a starting point for a Flutter application. A few resources to ge

Nov 6, 2021

Trip management mobile Application

Trip management mobile Application

HereYouGO Trip management mobile Application This app will help you Track your expense during your trips. Track your trip destinations and the sub tri

Apr 12, 2022

Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Fpdart Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Jun 24, 2022

Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to tidily bound separated logic into complex solution.

Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to tidily bound separated logic into complex solution.

Flutter Control is complex library to maintain App and State management. Library merges multiple functionality under one hood. This approach helps to

Feb 23, 2022

A super-fast and efficient state management solution for Flutter...

turbo A super-fast, efficient state management solution for Flutter. Turbo does not use streams, which are extremely inefficient, or use complex abstr

Apr 26, 2022

GetX - one of the most popular state management solution in flutter

GetX - one of the most popular state management solution in flutter

GteX Tutorial - Udemy GetX is one of the most popular state management solution in flutter. In addition to state management, GetX provides easy way to

May 18, 2022

Another state management solution

VxState VxState is a state management library built for Flutter apps with focus on simplicity. It is inspired by StoreKeeper & libraries like Redux, V

Jun 20, 2022

Cubit is a lightweight state management solution

Cubit is a lightweight state management solution

Cubit is a lightweight state management solution. It is a subset of the bloc package that does not rely on events and instead uses methods to emit new states.

May 23, 2022

Modular Get Flutter

Modulex Um framework para desenvolvimento de aplicativos escalaveis e modulares. Motivacao Sempre utilizei o Getx em meus projetos, porem, acho ele mu

May 15, 2022

Github Search - Flutter Modular example

Github Search - Flutter Modular example

Desafio Github Search Projeto desenvolvido no desafio quizenal realizado pela equipe Flutterando e comunidade. Foram realizadas adaptações para implem

Nov 6, 2021

Flutter Modular - A smart project structure

Flutter Modular - A smart project structure

Flutter Modular Getting started with Modular flutter_modular Documentation flutter_modular_test Documentation modular_codegen Documentation Features a

Jun 28, 2022

A basic Modular app

lifecare_app A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if

Jan 10, 2022

Modular server framework with ConnectMe (WebSockets + PackMe) and MongoDb support.

What is ServeMe ServeMe is a simple and powerful modular server framework. It allows to easily create backend services for both mobile and web applica

Dec 29, 2021

State Persistence - Persist state across app launches. By default this library store state as a local JSON file called `data.json` in the applications data directory. Maintainer: @slightfoot

State Persistence Persist state across app launches. By default this library store state as a local JSON file called data.json in the applications dat

Dec 23, 2021

Practice building basic animations in apps along with managing app state by BLoC State Management, Flutter Slider.

Practice building basic animations in apps along with managing app state by BLoC State Management including: Cubit & Animation Widget, Flutter Slider.

Jun 8, 2022

An extension to the bloc state management library which lets you create State Machine using a declarative API

An extension to the bloc state management library which lets you create State Machine using a declarative API

May 29, 2022

A powerful state machine for MobX management, that can be used in almost any application state.

A powerful state machine for MobX management, which can be used in almost any application state. It has 3 states - loading, success, error - and is pe

Apr 25, 2022

Your best flutter coding friend. All in one; state management, navigation management(with dynamic routing), local storage, localization, dependency injection, cool extensions with best usages and with the support of best utilities!

okito Your best flutter coding friend. All in one; state management, navigation management(with dynamic routing), local storage, dependency injection,

Feb 2, 2022
Comments
  • 1. Add Portuguese translation and structure for other translations

    As @irvine5k said in issue #1, this is very interesting project. So, for we Brazilizan and other people that speak Portuguese, I added the translation to follow the project progress and as reference, when it gaining more attention (and I think it will😊).

    Reviewed by robsonsilv4 at 2021-05-06 12:07
  • 2. Provide a more complex example

    I was from the time of Scoped Model, so I tried a lot of approaches of state management and I can say that Fountain is really interesting, and by the docs and visual identity, I can see that you put your heart into it. Nice job!

    I liked the idea of separate actions instead of a class that handles all of them. The state injection in the widget tree is really easy, and since it's a global state, we can eliminate duplicated code(Providers everywhere). You brought the redux idea of middlewares, which is really useful to automate some boilerplate stuff like Analytics, Logging, Mock.

    I only miss a more complex example that can better explain how the global state is built and the view listens only to one part of it.

    Reviewed by irvine5k at 2021-05-05 18:42

Related

MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.
MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.

Language: English | Português | Chinese mobx.dart MobX for the Dart language. Supercharge the state-management in your Dart apps with Transparent Func

Jun 22, 2022
A flutter boilerplate project with GetX state management.
A flutter boilerplate project with GetX state management.

flutter_getx_boilerplate Languages: English (this file), Chinese. Introduction During my study of Flutter, I have been using the flutter_bloc state ma

Jun 30, 2022
Flutter MVU architecture/state management package

mvu_flutter No mutability. No builders. No connectors. No reducers. No StreamControllers and subscription management. A truly declarative state manage

Jul 29, 2021
Simple global state management for Flutter

Slices Slices is a minimalist state manegement, focused specifically for applications that needs a global state where different "pieces" of the applic

Jun 15, 2021
Flutter State Management with provider :rocket:
Flutter State Management with provider :rocket:

Flutter - Gerenciamento de Estados com Provider Objetivos ao completar os estudos Aprenda a gerenciar o estado da sua aplicação com Single Source of T

Dec 6, 2021
A predictable state management library that helps implement the BLoC design pattern
A predictable state management library that helps implement the BLoC design pattern

A predictable state management library that helps implement the BLoC design pattern. Package Pub bloc bloc_test flutter_bloc angular_bloc hydrated_blo

Jun 27, 2022
London App Brewery State Management Project

todey_flutter A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

Nov 1, 2021
A simple way to access state while robust and testable.
A simple way to access state while robust and testable.

A state-management library that: catches programming errors at compile time rather than at runtime removes nesting for listening/combining objects ens

Jun 29, 2022
A lightweight, yet powerful way to bind your application state with your business logic.
A lightweight, yet powerful way to bind your application state with your business logic.

binder A lightweight, yet powerful way to bind your application state with your business logic. The vision As other state management pattern, binder a

May 22, 2022
Manage the state of your widgets using imperative programming concepts.

Imperative Flutter Manage the state of your widgets using imperative programming concepts. Setup Intall imperative_flutter package in pubspec.yaml dep

Jan 9, 2022