flowr-usage

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

FlowR Usage

FlowR 使用指南

Use FlowR APIs correctly without assuming a specific project file layout.
无需依赖特定项目文件结构,正确使用FlowR API。

First Checks

初始检查

  • Follow the project
    AGENTS.md
    : this repository uses
    fvm
    for Flutter and Dart commands, for example
    fvm dart test
    and
    fvm 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 FlowR API usage, not where files must live.
  • 遵循项目的
    AGENTS.md
    文档:本仓库使用
    fvm
    执行Flutter和Dart命令,例如
    fvm dart test
    fvm flutter analyze
  • 编辑代码前,运行
    git status --short
    。若存在无关的未提交变更,询问是否提交或忽略。
  • 优先遵循项目现有架构。本指南仅覆盖FlowR API的使用方式,不规定文件存放位置。

Imports

导入规则

  • Pure Dart code: import
    package:flowr_dart/flowr_dart.dart
    .
  • Flutter MVVM code: import
    package:flowr/flowr_mvvm.dart
    .
  • Import
    dart:async
    when public methods use
    FutureOr
    ,
    Stream
    , or
    StreamSubscription
    types directly.
  • Do not import
    flowr/src/...
    or
    flowr_dart/src/...
    from application code.
  • 纯Dart代码:导入
    package:flowr_dart/flowr_dart.dart
  • Flutter MVVM代码:导入
    package:flowr/flowr_mvvm.dart
  • 当公共方法直接使用
    FutureOr
    Stream
    StreamSubscription
    类型时,导入
    dart:async
  • 应用代码中禁止导入
    flowr/src/...
    flowr_dart/src/...

flowr_dart Core

flowr_dart 核心用法

Use
FlowR<T>
for method-driven state:
dart
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:
dart
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:
dart
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>
    extends
    Cubit<T>
    and exposes
    value
    as the legacy name for
    state
    .
  • FlowB<E, S>
    extends
    Bloc<E, S>
    and should be driven from
    add(event)
    .
  • FlowB.put
    is protected/test-only style; public callers should dispatch events.
  • put(value)
    and
    update(...)
    follow bloc equality semantics: if
    newValue == currentValue
    , no stream event is emitted.
  • stream
    uses bloc-native semantics. It does not replay the current state to new subscribers; use
    value
    or
    state
    for synchronous reads.
  • Do not add
    valueStream
    overrides or compatibility switches to restore replayable streams.
  • dispose()
    is kept for legacy FlowR APIs; bloc-native code may use
    close()
    .
使用
FlowR<T>
实现方法驱动的状态管理:
dart
class Counter extends FlowR<int> {
  Counter() : super(0);

  int increment() => put(value + 1);
}
当下一状态依赖当前状态且可能执行失败时,使用
update
dart
FutureOr<UserState?> refresh() => update(
  (old) async => old.copyWith(user: await api.loadUser()),
  onError: (error, stackTrace) => putError(error, stackTrace),
);
使用
FlowB<E, S>
实现事件驱动的状态管理:
dart
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));
  }
}
规则说明:
  • FlowR<T>
    继承自
    Cubit<T>
    ,并保留
    value
    作为
    state
    的兼容命名。
  • FlowB<E, S>
    继承自
    Bloc<E, S>
    ,应通过
    add(event)
    触发状态变更。
  • FlowB.put
    为受保护/仅测试用方法;外部调用者应通过分发事件触发。
  • put(value)
    update(...)
    遵循Bloc的相等性语义:若
    newValue == currentValue
    ,则不会发送流事件。
  • stream
    使用Bloc原生语义,不会向新订阅者重放当前状态;同步读取状态请使用
    value
    state
  • 请勿添加
    valueStream
    重写或兼容开关来恢复可重放流。
  • dispose()
    为FlowR遗留API;Bloc原生代码可使用
    close()

State Rules

状态管理规则

  • Prefer immutable state:
    final
    fields,
    const
    constructors,
    copyWith
    , and value equality when the project already uses it.
  • To trigger a UI update, return a new unequal model instance.
  • For
    List
    ,
    Map
    , and
    Set
    fields, allocate a new collection before emitting:
dart
update((old) => old.copyWith(items: [...old.items, item]));
  • Do not mutate an existing state object and call
    put(old)
    .
  • Use
    skpNull(value, 'name')
    or
    skpIf(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.
  • 优先使用不可变状态:
    final
    字段、
    const
    构造方法、
    copyWith
    方法,以及项目已采用的值相等性判断。
  • 若要触发UI更新,需返回一个不相等的新模型实例。
  • 对于
    List
    Map
    Set
    类型的字段,在发送状态前需分配新的集合:
dart
update((old) => old.copyWith(items: [...old.items, item]));
  • 请勿修改现有状态对象后调用
    put(old)
  • 使用
    skpNull(value, 'name')
    skpIf(condition, 'reason')
    来终止流程,且不将其视为失败。
  • 若需要启用破坏性变更的兼容设置,请明确告知用户行为变更的内容及原因,不要隐藏在配置背后。

Stream Helpers

流工具用法

Use normal
Stream<T>
helpers on
FlowR.stream
and
FlowB.stream
:
dart
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.
  • Legacy
    ValueStream
    helpers such as
    mapValue
    and
    whereValue
    are only for actual
    ValueStream<T>
    instances, not FlowR view-model streams.
FlowR.stream
FlowB.stream
上使用标准
Stream<T>
工具:
dart
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)
    根据选定的键过滤连续重复的事件。
  • distinctWith((event) => mapped)
    先映射事件,再过滤连续重复的映射值。
  • distinctUnique()
    过滤整个流历史中的重复值。
  • 遗留的
    ValueStream
    工具(如
    mapValue
    whereValue
    )仅适用于实际的
    ValueStream<T>
    实例,不适用于FlowR视图模型流。

flowr Flutter Usage

flowr Flutter 用法

Define models as
FrModel
-compatible immutable objects. Use
FrViewModel<M>
for method-driven Flutter state:
dart
class CounterModel {
  final int value;

  const CounterModel({this.value = 0});

  CounterModel copyWith({int? value}) =>
      CounterModel(value: value ?? this.value);
}

class CounterViewModel extends FrViewModel<CounterModel> {
  CounterViewModel() : super(const CounterModel());

  FutureOr<CounterModel?> increment() =>
      update((old) => old.copyWith(value: old.value + 1));
}
Use
FrBlocViewModel<E, M>
when callers naturally dispatch events:
dart
class CounterViewModel
    extends FrBlocViewModel<CounterEvent, CounterModel> {
  CounterViewModel() : super(const CounterModel()) {
    on<CounterIncremented>(
      (event, emit) => emit(state.copyWith(value: state.value + 1)),
    );
  }
}
Register ownership with
FrProvider
:
dart
FrProvider(
  (context) => CounterViewModel(),
  child: const CounterPage(),
);
  • Use
    FrProvider.di
    for GetIt-created instances that should be pulled into the widget tree.
  • Use
    FrProvider.value
    for an existing instance, such as dialog/subtree reuse.
  • Use
    FrProvider.multi
    for multiple providers.
  • FrProvider
    disposes
    DisposeMx
    and closes bloc
    Closable
    instances.
Build UI with:
dart
FrView<CounterViewModel, CounterModel>(
  builder: (context, snap, child) => Text('${snap.data.value}'),
);
  • FrView
    rebuilds from state.
  • FrListener
    handles side effects.
  • FrConsumer
    combines listener and builder.
  • FrMultiListener
    groups listeners.
  • FrSnap
    is a record:
    (vm: VM, data: M)
    .
  • FrView
    ,
    FrListener
    ,
    FrConsumer
    , and
    FrViewU
    route view models through bloc-native UI components.
将模型定义为兼容
FrModel
的不可变对象。使用
FrViewModel<M>
实现方法驱动的Flutter状态管理:
dart
class CounterModel {
  final int value;

  const CounterModel({this.value = 0});

  CounterModel copyWith({int? value}) =>
      CounterModel(value: value ?? this.value);
}

class CounterViewModel extends FrViewModel<CounterModel> {
  CounterViewModel() : super(const CounterModel());

  FutureOr<CounterModel?> increment() =>
      update((old) => old.copyWith(value: old.value + 1));
}
当调用者自然需要分发事件时,使用
FrBlocViewModel<E, M>
dart
class CounterViewModel
    extends FrBlocViewModel<CounterEvent, CounterModel> {
  CounterViewModel() : super(const CounterModel()) {
    on<CounterIncremented>(
      (event, emit) => emit(state.copyWith(value: state.value + 1)),
    );
  }
}
通过
FrProvider
注册实例所有权:
dart
FrProvider(
  (context) => CounterViewModel(),
  child: const CounterPage(),
);
  • 使用
    FrProvider.di
    将GetIt创建的实例引入组件树。
  • 使用
    FrProvider.value
    复用现有实例,例如对话框/子树场景。
  • 使用
    FrProvider.multi
    注册多个提供者。
  • FrProvider
    会自动释放
    DisposeMx
    实例并关闭Bloc的
    Closable
    实例。
构建UI的方式:
dart
FrView<CounterViewModel, CounterModel>(
  builder: (context, snap, child) => Text('${snap.data.value}'),
);
  • FrView
    会根据状态变化重建UI。
  • FrListener
    处理副作用。
  • FrConsumer
    结合了监听器和构建器的功能。
  • FrMultiListener
    用于分组管理多个监听器。
  • FrSnap
    是一个记录类型:
    (vm: VM, data: M)
  • FrView
    FrListener
    FrConsumer
    FrViewU
    通过Bloc原生UI组件传递视图模型。

FrUnion

FrUnion 用法

Use
FrUnionViewModel
for small global typed state sets:
dart
FrConfig.initialize(
  frUnion: FrUnion.of({CounterModel(), UserModel()}),
);
  • FrUnion.of({...})
    accepts plain model values.
  • FrUnion.ofTaggedModel({(model, 'tag')})
    or
    FrUnionViewModel.ofTag(...)
    supports multiple values of the same type.
  • Read typed values with
    FrViewU<M>
    or
    vm.streamBy<M>(tag: ...)
    .
  • Update typed values with
    vm.updateBy<M>((old) => next, tag: ...)
    .
  • Avoid a single global
    FrUnionViewModel
    for complex app domains with unclear ownership.
使用
FrUnionViewModel
管理小型全局类型化状态集合:
dart
FrConfig.initialize(
  frUnion: FrUnion.of({CounterModel(), UserModel()}),
);
  • FrUnion.of({...})
    接受普通模型值。
  • FrUnion.ofTaggedModel({(model, 'tag')})
    FrUnionViewModel.ofTag(...)
    支持同一类型的多个值。
  • 使用
    FrViewU<M>
    vm.streamBy<M>(tag: ...)
    读取类型化值。
  • 使用
    vm.updateBy<M>((old) => next, tag: ...)
    更新类型化值。
  • 避免为复杂应用域使用单一全局
    FrUnionViewModel
    ,以免所有权模糊。

References

参考文档

Load these only when the request touches the package or scenario:
  • references/fr-mvvm-env.md
    : environment selector package usage.
  • references/fr-mvvm-locale.md
    : locale state and locale switcher usage.
  • references/fr-mvvm-theme.md
    : theme switching, ThemeExtension helpers, built-in and JSON-config theme sources, and image scheme usage.
  • references/fr-mvvm-user.md
    : user selector/session state package usage.
  • references/migration.md
    : detailed migration after FlowR breaking changes.
仅当请求涉及对应包或场景时,加载以下文档:
  • references/fr-mvvm-env.md
    :环境选择器包的使用指南。
  • references/fr-mvvm-locale.md
    :Locale状态及语言切换器的使用指南。
  • references/fr-mvvm-theme.md
    :主题切换、ThemeExtension工具、内置及JSON配置主题源,以及图片方案的使用指南。
  • references/fr-mvvm-user.md
    :用户选择器/会话状态包的使用指南。
  • references/migration.md
    :FlowR破坏性变更后的详细迁移指南。

Validation

验证步骤

  • Format changed Dart files with
    fvm dart format <paths>
    .
  • For pure Dart package code, run
    fvm dart test
    or focused tests.
  • For Flutter package/app code, run
    fvm flutter test
    for touched widgets or providers.
  • Run
    fvm dart analyze
    or
    fvm flutter analyze
    when shared APIs or package surfaces change.
  • 使用
    fvm dart format <paths>
    格式化修改后的Dart文件。
  • 对于纯Dart包代码,运行
    fvm dart test
    或指定测试用例。
  • 对于Flutter包/应用代码,运行
    fvm flutter test
    测试修改的组件或提供者。
  • 当共享API或包表面发生变更时,运行
    fvm dart analyze
    fvm flutter analyze