state-management

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

BLoC Pattern

BLoC 模式

  • Sealed States & Events: Always use
    sealed class
    for both States and Events to ensure exhaustive UI handling and compile-time safety.
  • Immutability: All States, Events, and Domain Entities MUST be immutable (using
    final
    and
    Equatable
    or
    freezed
    ).
  • Official BLoC Part-Part Of Pattern: Every
    _bloc.dart
    file MUST include its corresponding
    _event.dart
    and
    _state.dart
    files using
    part
    directives. Each event/state file MUST have a
    part of
    directive pointing back to the bloc file. This ensures a single library scope and shared private members.
    dart
    // auth_bloc.dart
    part 'auth_event.dart';
    part 'auth_state.dart';
    
    class AuthBloc extends Bloc<AuthEvent, AuthState> { ... }
    
    // auth_event.dart
    part of 'auth_bloc.dart';
    
    // auth_state.dart
    part of 'auth_bloc.dart';
  • Mandatory Directory Structure: Every BLoC feature set MUST reside in its own sub-directory within the
    bloc/
    folder. Flat
    bloc/
    directories are STRICTLY prohibited.
    text
    presentation/bloc/
    └── <bloc_name>/
        ├── <bloc_name>_bloc.dart
        ├── <bloc_name>_event.dart
        └── <bloc_name>_state.dart
  • Loading State Mandate: ALWAYS emit
    Loading
    before async work, then
    Success
    or
    Error
    . Never skip the loading state.
  • Concurrency: Use
    transformers
    (e.g.,
    restartable()
    ,
    droppable()
    ) for events requiring debouncing (search) or throttling (buttons).
  • Zero-Logic UI: Widgets MUST NOT contain business logic, orchestration logic, or direct calls to external services. They should ONLY dispatch events and build UI based on BLoC states.
  • 密封状态与事件:始终对状态和事件使用
    sealed class
    ,以确保全面的UI处理和编译时安全性。
  • 不可变性:所有状态、事件和领域实体必须是不可变的(使用
    final
    以及
    Equatable
    freezed
    实现)。
  • 官方 BLoC Part-Part Of 模式:每个
    _bloc.dart
    文件必须使用
    part
    指令引入对应的
    _event.dart
    _state.dart
    文件。每个事件/状态文件必须包含指向对应bloc文件的
    part of
    指令。这能保证单一库作用域以及私有成员的共享。
    dart
    // auth_bloc.dart
    part 'auth_event.dart';
    part 'auth_state.dart';
    
    class AuthBloc extends Bloc<AuthEvent, AuthState> { ... }
    
    // auth_event.dart
    part of 'auth_bloc.dart';
    
    // auth_state.dart
    part of 'auth_bloc.dart';
  • 强制目录结构:每个BLoC功能集必须存放在
    bloc/
    文件夹下独立的子目录中。严格禁止扁平化的
    bloc/
    目录结构。
    text
    presentation/bloc/
    └── <bloc_name>/
        ├── <bloc_name>_bloc.dart
        ├── <bloc_name>_event.dart
        └── <bloc_name>_state.dart
  • 加载状态要求:在执行异步操作前始终抛出
    Loading
    状态,之后再抛出
    Success
    Error
    状态。绝对不能跳过加载状态。
  • 并发处理:对需要防抖(搜索)或节流(按钮点击)的事件使用
    transformers
    (例如
    restartable()
    droppable()
    )。
  • UI零逻辑:Widgets 绝对不能包含业务逻辑、编排逻辑或者直接调用外部服务。它们只应该派发事件,并根据BLoC的状态构建UI。

BLoC Widget Usage

BLoC Widget 使用规范

  • BlocBuilder
    for local UI rebuilds based on state
  • BlocListener
    for side effects (navigation, snackbars, dialogs)
  • BlocConsumer
    when both rebuild and side effects are needed
  • context.read<Bloc>().add(Event())
    for dispatching events
  • context.watch<Bloc>().state
    for reactive rebuilds (inside
    build()
    only)
  • 使用
    BlocBuilder
    基于状态实现局部UI重绘
  • 使用
    BlocListener
    处理副作用(导航、snackbars、弹窗)
  • 同时需要重绘和副作用时使用
    BlocConsumer
  • 使用
    context.read<Bloc>().add(Event())
    派发事件
  • 仅在
    build()
    方法内使用
    context.watch<Bloc>().state
    实现响应式重绘

BLoC Submission Checklist

BLoC 提交检查清单

  • Events and States use
    Equatable
    with correct
    props
  • All async operations follow
    Loading → Success/Error
    pattern
  • No business logic in UI widgets
  • No SDK/API calls outside DataSources
  • Zero hardcoded colors, spacing, or typography \u2014 use design tokens (
    AppColors
    ,
    AppSpacing
    )
  • Code formatted with
    dart format
  • 事件和状态使用
    Equatable
    并配置了正确的
    props
  • 所有异步操作都遵循
    Loading → Success/Error
    模式
  • UI Widget 中没有业务逻辑
  • 没有在 DataSources 之外调用SDK/API
  • 没有硬编码的颜色、间距或排版——使用设计令牌(
    AppColors
    AppSpacing
  • 代码已经使用
    dart format
    格式化

Dependency Injection

依赖注入

  • Use
    injectable
    for dependency injection and service location
  • Standardized Injection:
    • Use
      @injectable
      for screen-specific BLoCs to ensure a fresh instance per screen access.
    • Use
      @lazySingleton
      for global or shared BLoCs (e.g.,
      AuthBloc
      ,
      ThemeBloc
      ,
      SettingsBloc
      ,
      PasswordBloc
      ).
  • Organize blocs logically by feature and ensure strict separation of concerns
  • 使用
    injectable
    实现依赖注入和服务定位
  • 标准化注入规则
    • 对页面专属的BLoC使用
      @injectable
      ,确保每次访问页面时都得到全新的实例
    • 对全局或共享的BLoC使用
      @lazySingleton
      (例如
      AuthBloc
      ThemeBloc
      SettingsBloc
      PasswordBloc
  • 按功能逻辑组织BLoC,确保严格的关注点分离

Navigation & Routing

导航与路由

  • Dynamic Routes: STRICTLY prohibit hardcoded route strings in
    GoRouter
    configuration. Use static constants in
    AppRoutes
    .
  • Centralized BLoCs: BLoC providers MUST be injected via
    ShellRoute
    or
    BlocProvider
    in
    app_router.dart
    when shared across multiple screens or within a feature branch.
  • No Local Providers: Avoid
    BlocProvider
    in individual screen
    build()
    methods if the BLoC is needed by a feature set.
  • Primitive Route Arguments: STRICTLY prohibit passing complex objects (BLoCs, ChangeNotifiers, Entity instances) as route arguments. Pass only primitive IDs/Keys and fetch data in the destination screen using
    Repository
    or
    Bloc
    injection.
  • 动态路由:严格禁止在
    GoRouter
    配置中硬编码路由字符串。请使用
    AppRoutes
    中的静态常量。
  • 集中式BLoC注入:如果BLoC需要在多个页面或某个功能分支内共享,必须通过
    app_router.dart
    中的
    ShellRoute
    BlocProvider
    进行注入。
  • 禁止局部Provider:如果某个BLoC是整个功能集所需,不要在单个页面的
    build()
    方法中使用
    BlocProvider
    注入。
  • 基础类型路由参数:严格禁止传递复杂对象(BLoC、ChangeNotifier、实体实例)作为路由参数。仅传递基础类型的ID/键,在目标页面通过注入的
    Repository
    Bloc
    获取数据。