flowdux_flutter 0.3.0 copy "flowdux_flutter: ^0.3.0" to clipboard
flowdux_flutter: ^0.3.0 copied to clipboard

Flutter bindings for FlowDux state management library. Provides StoreScope, StoreProvider, StoreBuilder, and StoreConsumer widgets.

FlowDux Flutter #

Flutter bindings for FlowDux state management library.

pub package License: Apache 2.0

Features #

  • StoreProvider - Provide store to widget tree via InheritedWidget
  • StoreScope - Lifecycle-aware provider that creates/closes a store automatically
  • StoreBuilder - Rebuild widgets when state changes
  • StoreSelector - Optimized rebuilds with selectors
  • StoreConsumer - Access both store and state
  • StoreListener - Side effects on state changes

Installation #

dependencies:
  flowdux: ^0.3.2
  flowdux_flutter: ^0.3.0

Quick Start #

1. Wrap Your App with StoreProvider #

void main() {
  final store = createStore<AppState, AppAction>(
    initialState: AppState(),
    reducer: appReducer,
    middlewares: [AppMiddleware()],
  );

  runApp(
    StoreProvider<AppState, AppAction>(
      store: store,
      child: MyApp(),
    ),
  );
}

2. Use StoreBuilder for Reactive UI #

class CounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StoreBuilder<AppState, AppAction>(
      builder: (context, state) {
        return Text('Count: ${state.count}');
      },
    );
  }
}

3. Dispatch Actions #

// Using context extension
ElevatedButton(
  onPressed: () => context.dispatch<AppState, AppAction>(IncrementAction()),
  child: Text('Increment'),
)

// Or access the store directly
ElevatedButton(
  onPressed: () {
    final store = StoreProvider.of<AppState, AppAction>(context);
    store.dispatch(IncrementAction());
  },
  child: Text('Increment'),
)

Widgets #

StoreProvider #

Provides a store to all descendant widgets using InheritedWidget.

StoreProvider<AppState, AppAction>(
  store: store,
  child: MyApp(),
)

// Access the store
final store = StoreProvider.of<AppState, AppAction>(context);

// Or use the extension
final store = context.store<AppState, AppAction>();

// Safe access (returns null if not found)
final store = StoreProvider.maybeOf<AppState, AppAction>(context);

StoreScope #

Owns a store's lifecycle: creates it once via create and closes it on dispose. Use this anywhere the build function may run multiple times — e.g. inside a PageRoute's pageBuilder, a custom route builder, or a NestedRoute. Without it, building a fresh Store inline causes the store to be replaced on rebuild while child State objects keep stale references, dropping dispatched actions silently.

PageRouteBuilder(
  pageBuilder: (_, __, ___) => StoreScope<AppState, AppAction>(
    create: () => createStore<AppState, AppAction>(
      initialState: AppState(),
      reducer: appReducer,
    ),
    child: MyScreen(),
  ),
);

The created store is exposed to descendants via StoreProvider, so all the widgets below (StoreBuilder, StoreSelector, StoreConsumer, StoreListener, context.store, context.dispatch) work unchanged.

StoreBuilder #

Rebuilds its child when the store's state changes.

StoreBuilder<AppState, AppAction>(
  builder: (context, state) {
    return Column(
      children: [
        Text('Count: ${state.count}'),
        Text('Name: ${state.name}'),
      ],
    );
  },
)

You can also pass a store directly:

StoreBuilder<AppState, AppAction>(
  store: myStore, // Optional, uses provider if not specified
  builder: (context, state) {
    return Text('Count: ${state.count}');
  },
)

StoreSelector #

Optimized widget that only rebuilds when the selected value changes.

StoreSelector<AppState, AppAction, int>(
  selector: (state) => state.count,
  builder: (context, count) {
    return Text('Count: $count');
  },
)

// Complex selector
StoreSelector<AppState, AppAction, String>(
  selector: (state) => '${state.firstName} ${state.lastName}',
  builder: (context, fullName) {
    return Text('Name: $fullName');
  },
)

StoreConsumer #

Combines StoreBuilder with store access for dispatching actions.

StoreConsumer<AppState, AppAction>(
  builder: (context, store, state) {
    return Column(
      children: [
        Text('Count: ${state.count}'),
        ElevatedButton(
          onPressed: () => store.dispatch(IncrementAction()),
          child: Text('Increment'),
        ),
      ],
    );
  },
)

With listener for side effects:

StoreConsumer<AppState, AppAction>(
  listener: (context, store, state) {
    if (state.error != null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(state.error!)),
      );
    }
  },
  builder: (context, store, state) {
    return MyWidget(state: state);
  },
)

StoreListener #

Listens to state changes without rebuilding. Use for side effects like navigation or showing dialogs.

StoreListener<AppState, AppAction>(
  listener: (context, store, state) {
    if (state.shouldNavigate) {
      Navigator.of(context).pushNamed('/next');
    }
  },
  child: MyWidget(),
)

With conditional listening:

StoreListener<AppState, AppAction>(
  listenWhen: (previous, current) => previous.route != current.route,
  listener: (context, store, state) {
    Navigator.of(context).pushNamed(state.route);
  },
  child: MyWidget(),
)

Complete Example #

import 'package:flutter/material.dart';
import 'package:flowdux_flutter/flowdux_flutter.dart';

// State
class CounterState {
  final int count;
  CounterState({this.count = 0});
  CounterState copyWith({int? count}) =>
      CounterState(count: count ?? this.count);
}

// Actions
class IncrementAction implements Action {}
class DecrementAction implements Action {}

// Reducer
final counterReducer = buildReducer<CounterState, Action>((b) {
  b.on<IncrementAction>((state, _) => state.copyWith(count: state.count + 1));
  b.on<DecrementAction>((state, _) => state.copyWith(count: state.count - 1));
});

void main() {
  final store = createStore<CounterState, Action>(
    initialState: CounterState(),
    reducer: counterReducer,
  );

  runApp(
    StoreProvider<CounterState, Action>(
      store: store,
      child: MaterialApp(
        home: CounterPage(),
      ),
    ),
  );
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: StoreBuilder<CounterState, Action>(
          builder: (context, state) {
            return Text(
              '${state.count}',
              style: Theme.of(context).textTheme.headlineLarge,
            );
          },
        ),
      ),
      floatingActionButton: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          FloatingActionButton(
            onPressed: () =>
                context.dispatch<CounterState, Action>(IncrementAction()),
            child: Icon(Icons.add),
          ),
          SizedBox(height: 8),
          FloatingActionButton(
            onPressed: () =>
                context.dispatch<CounterState, Action>(DecrementAction()),
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

Testing #

testWidgets('counter increments', (tester) async {
  final store = createStore<CounterState, Action>(
    initialState: CounterState(),
    reducer: counterReducer,
  );

  await tester.pumpWidget(
    MaterialApp(
      home: StoreProvider<CounterState, Action>(
        store: store,
        child: CounterPage(),
      ),
    ),
  );

  expect(find.text('0'), findsOneWidget);

  await tester.tap(find.byIcon(Icons.add));
  await tester.runAsync(() => Future.delayed(Duration(milliseconds: 50)));
  await tester.pumpAndSettle();

  expect(find.text('1'), findsOneWidget);
});

API Reference #

See the API documentation for complete details.

License #

Apache License 2.0 - see LICENSE for details.

0
likes
160
points
118
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Flutter bindings for FlowDux state management library. Provides StoreScope, StoreProvider, StoreBuilder, and StoreConsumer widgets.

Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

flowdux, flutter

More

Packages that depend on flowdux_flutter