flutter-bloc

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中无业务逻辑
  • 无在DataSource之外调用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
    注入。
  • 避免本地提供器:如果BLoC是某个功能集所需,不要在单独页面的
    build()
    方法中使用
    BlocProvider
    注入。
  • 基础类型路由参数:严格禁止传递复杂对象(BLoCs、ChangeNotifiers、实体实例)作为路由参数。仅传递基础类型的ID/键,在目标页面通过
    Repository
    Bloc
    注入获取数据。