flutter_rx 0.5.1 icon indicating copy to clipboard operation
flutter_rx: ^0.5.1 copied to clipboard

A redux style state management library, loosely inspired by NgRx.

flutter_rx #

A redux style state management library, loosely inspired by NgRx as well as flutter_redux and redux.

Key concepts #

  • Actions describe unique events and are typically dispatched from widgets or returned from an [Effect], such as a button click or the intent to load data from a server.
  • State changes are handled by pure functions called reducers that take the current state and the latest action to compute a new state.
  • Selectors are pure functions used to select, derive, and compose pieces of state.
  • State is accessed with the Store, an observable of state and an observer of actions.
  • Effects are used to handle any and all side effects, such as loading data from a remote server.

Diagram #

The following diagram represents the overall general flow of application state in flutter_rx. flutter_rx Diagram

Usage #

Below is a counter app that uses flutter_rx

import 'package:flutter/material.dart';
import 'package:flutter_rx/flutter_rx.dart';
import 'package:rxdart/rxdart.dart';

/// The State of the Application.
/// The state must extend [StoreState], and should provide
/// overrides for [==] and [hashCode].
/// If a [==] override is not provided, selection memoization
/// will not work, which will lead to unnecessary rebuilds.
class AppState extends StoreState {
  const AppState({required this.counter, this.isLoading = false});
  final int counter;
  final bool isLoading;

  AppState copyWith({int? counter, bool? isLoading}) {
    return AppState(
      counter: counter ?? this.counter,
      isLoading: isLoading ?? this.isLoading,

  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other.runtimeType != runtimeType) return false;
    return other is AppState &&
        other.counter == counter &&
        other.isLoading == isLoading;

  int get hashCode => counter.hashCode ^ isLoading.hashCode;

/// Actions describe unique events and are typically dispatched
/// from widgets or returned from an [Effect].
/// Actions must extend [StoreAction].
class IncrementAction extends StoreAction {
  const IncrementAction();

/// Actions can optionally contain state.
class IncrementByAction extends StoreAction {
  const IncrementByAction({required this.value});
  final int value;

/// Actions can be used to initiate async tasks, such
/// as fetching data from a server.
class FetchCounterValueAction extends StoreAction {
  const FetchCounterValueAction();

class FetchCounterValueSuccessAction extends StoreAction {
  const FetchCounterValueSuccessAction({required this.value});
  final int value;

/// A reducer is just a pure function that takes in a state and
/// an action, and returns a new state.
/// The reducers below are intended to reduce on a specific
/// action, for example [IncrementAction], but reducers can
/// also reduce on any generic [StoreAction].
AppState incrementCounterReducer(
  AppState state,
  IncrementAction action,
) {
  return state.copyWith(counter: state.counter + 1);

AppState incrementCounterByReducer(
  AppState state,
  IncrementByAction action,
) {
  return state.copyWith(counter: state.counter + action.value);

AppState fetchCounterValueReducer(
  AppState state,
  FetchCounterValueAction action,
) {
  return state.copyWith(
    isLoading: true,

AppState fetchCounterValueSuccessReducer(
  AppState state,
  FetchCounterValueSuccessAction action,
) {
  return state.copyWith(
    counter: action.value,
    isLoading: false,

/// [createReducer] takes multiple single purpose reducers and combines them.
/// [On] is used to map a specific [StoreAction] to the reducer that
/// should be used when that action is dispatched.
/// For example, when [IncrementAction] is received, [incrementCounterReducer]
/// will be used to generate the new state.
final reducer = createReducer<AppState>([
  On<AppState, IncrementAction>(incrementCounterReducer),
  On<AppState, IncrementByAction>(incrementCounterByReducer),
  On<AppState, FetchCounterValueAction>(fetchCounterValueReducer),
  On<AppState, FetchCounterValueSuccessAction>(fetchCounterValueSuccessReducer),

/// Effects handle any and all side effects, such as fetching data from a
/// remote  server.
/// Effects receive the stream of actions from the store. Typically
/// this stream is filtered for a specific action.
/// Actions are only added to the stream that effects receive after
/// the app's reducers has processed this action and returned a new app state.
/// This guarantees that the state of the store includes mutations from the action
/// being handled by the effect.
/// Effects can optionally return one or more actions. These actions
/// will then be dispatched. If the effect returns a list of actions,
/// will be dispatched in the order of the list.
/// Effects should **not** call dispatch to dispatch new actions.
Effect<AppState> onFetchCounter = (
  Stream<StoreAction> actions,
  Store<AppState> store,
) {
  /// RxDart can be useful within effects, but does not need to be used.
  return actions.whereType<FetchCounterValueAction>().flatMap((action) {
    return Stream.fromFuture(fetchCounter()).map((value) {
      return FetchCounterValueSuccessAction(value: value);

/// Mock data call.
/// In a real app this would be a network call.
Future<int> fetchCounter() {
  return Future.delayed(const Duration(milliseconds: 100)).then((value) => 10);

void main() {
  /// Create your store as a final variable in the main function
  /// or inside a State object.
  final store = Store<AppState>(
    /// The initial state of the store.
    initialState: const AppState(counter: 0),

    /// The main reducer.
    /// This can be a simple pure function, or composed of multiple smaller reducers.
    reducer: reducer,

    /// The list of effects to be registered.
    effects: [onFetchCounter],

    /// The StoreProvider should generally be the top level widget. Only descendants
    /// will have access to the StoreProvider's state.
      store: store,
      child: const MaterialApp(
        home: HomePage(),

class HomePage extends StatelessWidget {
  const HomePage({

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('FlutterRx Counter Demo'),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times:',

            /// StoreConnector can be used to access data from the store
            /// using a selector.
            /// You do not have to use StoreConnector. Data can be streamed from the
            /// store using `StoreProvider.of<AppState>(context)`, and the current
            /// snapshot of the store can be read with
            /// `StoreProvider.of<AppState>(context).value`.
              /// The selector maps the apps state to a subset for use in your
              /// widget.
              /// A simple function could be used in place of createSelector,
              /// but createSelector uses memoization to reduce rebuilds.
              selector: createSelector((AppState state, _) => state.counter),

              /// onInit can be used execute code on the first build. This is
              /// often useful for dispatching an action that fetches data for
              /// view.
              onInit: () {
                  const FetchCounterValueAction(),

              /// The builder will only rebuild when when the selector emits
              /// a new value, and memoization ensures that this only happens when
              /// the app state has changed.
              /// createSelector1, createSelector2, etc. can be used to compose
              /// selectors together. The composed selector will only emit a new value
              /// when one of the input selectors does.
              builder: (BuildContext context, int value) {
                return Text(
                  style: Theme.of(context).textTheme.headline4,
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Use the StoreProvider to get the store and call the dispatch method
          // to dispatch actions.
            const IncrementAction(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
pub points


verified publisher iconsimmer.recipes

A redux style state management library, loosely inspired by NgRx.

Repository (GitHub)
View/report issues


API reference


Icon for licenses.MIT (LICENSE)


flutter, flutter_rx_core, rxdart


Packages that depend on flutter_rx