flowr_dart 4.0.1 copy "flowr_dart: ^4.0.1" to clipboard
flowr_dart: ^4.0.1 copied to clipboard

Base FlowR library for pure Dart

FlowR Dart #

State management based on Reactive programming for pure Dart.

Install #

dart pub add flowr_dart

Tip

If you are using Flutter, it is highly recommended to use the flowr package, which provides MVVM support and Flutter-specific features.

Getting started #

import 'package:flowr_dart/flowr_dart.dart';

class Counter extends FlowR<int> {
  Counter({required int initialState}) : super(initialState);

  incrementCounter() => update((old) {
        logger('incrementCounter: $old');
        return old + 1;
      });

  incrementSlowly() => update(
        (old) async {
          await Future<void>.delayed(const Duration(milliseconds: 300));
          return old + 1;
        },
        mutexTag: 'counter',
      );
}

Future<void> main() async {
  Logger.root.level = Level.INFO;
  Logger.root.onRecord.listen(LoggableMx.devLogRecordPrinter);

  final counter = Counter(initialState: 0);
  await counter.incrementCounter();
  print('counter: ${counter.value}');

  counter.dispose();
}

Core APIs #

  • FlowB<E, S> is the bloc-native event-driven base class and extends Bloc<E, S>.
  • FlowR<T> is the method-driven base class and extends Cubit<T>.
  • FlowR<T>.stream uses Cubit's native Stream<T> semantics and does not replay the current state to new subscribers.
  • update reads the current value, runs an updater, catches failures, and calls put on success.
  • runCatching is available for non-state work that should share the same error and skip handling.
  • skpIf and skpNull throw SkipError, which stops the current flow without treating it as a failure.
  • autoDispose(subscription) cancels registered stream subscriptions when dispose is called.

FlowR follows bloc equality semantics: if value == currentValue, listeners are not notified. Prefer immutable state updates, for example update((old) => old.copyWith(...)).

Concurrency #

update and runCatching can be scheduled with:

  • debounceTag: wait until calls stop, then run the last call.
  • throttleTag: run at most once during the configured window.
  • mutexTag: run immediately and ignore overlapping calls with the same tag.
  • slowlyMs: window size in milliseconds for debounce and throttle.

Tags are scoped to the FlowR instance. Use stable tag values for each independent action.

Stream helpers #

final names = counter.stream.distinctWith((count) => 'count: $count');
final evenValues = counter.stream.where((count) => count.isEven);
  • distinctBy filters events by the selected key.
  • distinctWith maps then de-duplicates mapped values.
  • distinctUnique filters duplicates across the whole stream history.

Logging #

FlowR uses package:logging. Configure the root logger once in your app:

Logger.root.level = Level.INFO;
Logger.root.onRecord.listen(LoggableMx.devLogRecordPrinter);

Set Logger.root.level = Level.FINE to see normal put, debounce, throttle, mutex, and SkipError logs.

Disposal #

Call dispose when a FlowR instance is no longer used. This closes the state subject, cancels subscriptions registered through autoDispose, and disposes pending debounce, throttle, or mutex timers.

Run example #

Demo FlowR: for dart main.dart

# From workspace root
dart run examples/example/lib/main.dart