Use flowr_dart APIs correctly without Flutter or MVVM assumptions.
First Checks
- Follow the project
AGENTS.md: this repository usesfvmfor Flutter and Dart commands, for examplefvm dart testandfvm flutter analyze. - Before editing code, run
git status --short. If unrelated uncommitted changes exist, ask whether to commit or ignore them. - Prefer the project's existing architecture. This skill covers pure Dart FlowR API usage, not where files must live.
- If a project depends on
flowr, it implicitly depends onflowr_dart; use this skill as the reference for sharedFlowR/FlowBsemantics. - When both
flowr-usageandflowr-dart-usageare installed, handle shared core behavior here first and letflowr-usagehandle Flutter-specific widgets, providers, and MVVM extensions.
Imports
- Import
package:flowr_dart/flowr_dart.dart. - Import
dart:asyncwhen public methods useFutureOr,Stream, orStreamSubscriptiontypes directly. - Do not import
flowr_dart/src/...from application code.
FlowR Core
Use FlowR<T> for method-driven state:
class Counter extends FlowR<int> {
Counter() : super(0);
int increment() => put(value + 1);
}
Use update when the next state depends on the current state and may fail:
FutureOr<UserState?> refresh() => update(
(old) async => old.copyWith(user: await api.loadUser()),
onError: (error, stackTrace) => putError(error, stackTrace),
);
Use FlowB<E, S> for event-driven state:
sealed class CounterEvent {
const CounterEvent();
}
class CounterIncremented extends CounterEvent {
const CounterIncremented();
}
class CounterBloc extends FlowB<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncremented>((event, emit) => emit(state + 1));
}
}
Rules:
FlowR<T>extendsCubit<T>and exposesvalueas an alias ofstate.FlowB<E, S>extendsBloc<E, S>and should be driven fromadd(event).FlowB.putis protected/test-only style; public callers should dispatch events.FlowRandFlowBalready inherit FlowR logging helpers throughLoggableMx; do not re-implementlogger,logE, or ad-hoc wrappers in application code.FlowRandFlowBalready inherit FlowR helper mixins forrunCatching/skpIf/skpNull, debounce/throttle/mutex, andautoDispose; do not re-implement those primitives in application code.put(value)andupdate(...)follow bloc equality semantics: ifnewValue == currentValue, no stream event is emitted.streamuses bloc-native semantics. It does not replay the current state to new subscribers; usevalueorstatefor synchronous reads.- Do not add
valueStreamoverrides or compatibility switches to restore replayable streams. dispose()andclose()are both available; useclose()in bloc-native code.
Logging
- When a task asks to add log printing inside a
FlowRorFlowBclass, first use the built-in helpers:logger,logF,logI,logW,logE, andlogS. - Do not implement a new
logE,logger, extension, or mixin wrapper unless the task explicitly asks to change FlowR internals. - Use
logger('message')for ordinary trace logs nearupdate(...). - Use
logI,logW, orlogEwhen the severity matters. - Use
putError(error, stackTrace)when the failure should also reach the bloc/cubit error channel, not just the logger. - If an
update(...)body is async,await update(...)so log call sites stay accurate. - Load
references/flowr-logging.mdwhen the task asks to add logs, tune log levels, wireLogger.root, or decide betweenlogger,logE, andputError.
Control Flow
- Use
update(...)forFlowR<T>state changes. - Use
runCatching(...)for non-state work or shared async/sync work that should reuse FlowR skip/error handling. skpIf(...)andskpNull(...)throwSkipError, which stops the current flow without being treated as a failure.- Control
SkipErrorvisibility with logger level instead ofignoreSkipError. - Load
references/flowr-run-catching.mdwhen the task asks to skip a flow, catch failures without throwing, or decide whether to userunCatching,skpIf, orskpNull.
Scheduling And Disposal
debounceTagandthrottleTagonly apply whenslowlyMs > 0.mutexTagis independent ofslowlyMsand ignores overlapping work with the same tag.- Tags are scoped to a single
FlowR/FlowBinstance. Reuse a stable tag per logical action. - Use
autoDispose(subscription)to register stream subscriptions for cleanup. - In manually owned pure Dart code, call
dispose()orclose()when the instance is no longer used. - Load
references/flowr-slowly.mdwhen the task asks for debounce, throttle, mutex, or lock-state behavior. - Load
references/flowr-disposal.mdwhen the task asks aboutautoDispose,subBy,dispose, orclose. - Load
references/flowr-update.mdwhen the task asks for deeperupdate(...)semantics such asonError,logging, or scheduling tags on updates.
State Rules
- Prefer immutable state:
finalfields,constconstructors,copyWith, and value equality when the project already uses it. - When a task needs new
@freezedmodel/state classes or a repo does not yet havefreezedinstalled, loadreferences/freezed-install.mdfirst. - To trigger updates, return a new unequal model instance.
- For
List,Map, andSetfields, allocate a new collection before emitting:
update((old) => old.copyWith(items: [...old.items, item]));
- Do not mutate an existing state object and call
put(old). - Use
skpNull(value, 'name')orskpIf(condition, 'reason')to cancel a flow without treating it as a failure. - If a breaking-change compatibility setting is requested, explicitly tell the user what behavior changed and why. Do not hide it behind config.
Stream Helpers
Use normal Stream<T> helpers on FlowR.stream and FlowB.stream:
final labels = counter.stream.distinctWith((count) => 'count: $count');
final evenValues = counter.stream.where((count) => count.isEven);
final uniqueValues = counter.stream.distinctUnique();
distinctBy((event) => event.field)filters consecutive events by a selected key.distinctWith((event) => mapped)maps then de-duplicates consecutive mapped values.distinctUnique()filters duplicates across the whole stream history.ValueStreamhelpers such asmapValueandwhereValueare only for actualValueStream<T>instances, not FlowR or FlowB streams.
References
references/freezed-install.md: installingfreezed_annotation,freezed, andbuild_runner, plus the minimal@freezedscaffold and code generation commands.references/flowr-dart-install.md: addingflowr_dartto a pure Dart or shared logic package, entrypoint logger setup, and the firstFlowR/FlowBscaffold.references/flowr-logging.md: FlowR logging helpers,Logger.rootsetup, and when to uselogger,logE, orputError.references/flowr-run-catching.md:runCatching,skpIf,skpNull,SkipError, and failure-vs-skip control flow.references/flowr-slowly.md: debounce/throttle/mutex scheduling and lock semantics.references/flowr-disposal.md:DisposeMx,autoDispose,subBy,dispose, andclose.references/flowr-update.md:update(...),onError,logging, and scheduling tags on state updates.
Validation
- Format changed Dart files with
fvm dart format <paths>. - Run
fvm dart testor focused tests for touched pure Dart packages. - Run
fvm dart analyzewhen shared APIs or package surfaces change.