flutter-architecture

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Technology Stack

技术栈

  • Flutter for cross-platform development
  • Dart as the primary programming language
  • bloc for state management
  • injectable for dependency injection
  • Dart Mappable for immutable data models
  • Dio for HTTP networking
  • isar for local database
  • Firebase for backend services
  • Flutter 用于跨平台开发
  • Dart 作为主要编程语言
  • bloc 用于状态管理
  • injectable 用于依赖注入
  • Dart Mappable 用于不可变数据模型
  • Dio 用于HTTP网络请求
  • isar 用于本地数据库
  • Firebase 用于后端服务

Clean Architecture

整洁架构

  • Domain Purity: The
    domain
    layer must be pure Dart. NO
    package:flutter
    imports.
  • Layer Dependency:
    Presentation -> Domain <- Data
    . Data layer implements Domain interfaces.
  • Feature-First 2.0: Enforce strict separation of
    DataSources
    (External/Raw) vs
    Repositories
    (Domain abstraction).
  • 领域层纯净性
    domain
    层必须是纯Dart代码,禁止导入
    package:flutter
  • 层依赖关系
    Presentation -> Domain <- Data
    。数据层实现领域层的接口。
  • 功能优先2.0:严格区分
    DataSources
    (外部/原始数据)与
    Repositories
    (领域抽象)。

Directory Structure

目录结构

  • Organize code according to feature-based Clean Architecture pattern (
    featureA/bloc
    ,
    featureA/models
    ,
    featureA/views
    )
  • Place cross-feature components in the
    core
    directory
  • Group shared widgets in
    core/views/widgets
    by type
  • 按照基于功能的整洁架构模式组织代码(如
    featureA/bloc
    featureA/models
    featureA/views
  • 将跨功能组件放在
    core
    目录中
  • 按类型将共享组件分组到
    core/views/widgets
    目录

Three-Layer Data Model Pattern

三层数据模型模式

  1. API Layer (ItemApiModel)
    • Represents the raw data structure as received from the server
    • Contains all fields provided by the API
    • Should match the API contract exactly
  2. Domain Layer (Item)
    • Represents the internal data model
    • Contains only the fields necessary for business logic
    • Strips out unnecessary API fields
  3. UI Layer (ItemUiState)
    • Represents the data model optimized for UI rendering
    • Contains parsed and formatted data ready for display
    • Handles all UI-specific transformations
  1. API层(ItemApiModel)
    • 表示从服务器接收的原始数据结构
    • 包含API提供的所有字段
    • 必须完全匹配API契约
  2. 领域层(Item)
    • 表示内部数据模型
    • 仅包含业务逻辑所需的字段
    • 剔除不必要的API字段
  3. UI层(ItemUiState)
    • 表示针对UI渲染优化的数据模型
    • 包含已解析和格式化、可直接用于展示的数据
    • 处理所有UI相关的转换逻辑

Data Model Rules

数据模型规则

  • Use Dart Mappable for defining immutable UI states
  • Each layer should have its own type definition
  • The UI layer should use the UI state data models, never directly the domain model or the API model
  • The UI state model should be derived from the domain model, not the API model
  • The domain model should be derived from the API model, not the UI state model
  • 使用Dart Mappable定义不可变UI状态
  • 每个层都应有自己的类型定义
  • UI层应使用UI状态数据模型,绝不能直接使用领域模型或API模型
  • UI状态模型应派生自领域模型,而非API模型
  • 领域模型应派生自API模型,而非UI状态模型

Repository & DataSource Pattern

仓库与数据源模式

  • Repositories orchestrate between data sources (return Domain Model)
  • Data sources access raw data (return API Model)
  • The repository should be the source of truth, and it returns the domain model
  • DataSources MUST only contain raw SDK/API calls. No mapping or business logic.
  • No direct backend SDK/API calls outside DataSources
  • 仓库协调不同数据源之间的逻辑(返回领域模型)
  • 数据源负责访问原始数据(返回API模型)
  • 仓库应为事实来源,并返回领域模型
  • DataSources必须仅包含原始SDK/API调用,禁止包含映射或业务逻辑
  • 禁止在DataSources之外直接调用后端SDK/API

Data Flow

数据流

UI Event → BLoC (emit Loading) → Repository → DataSource (API/SDK)
Response → Repository (map to Domain Entity) → BLoC (emit Success/Error) → UI
UI Event → BLoC (emit Loading) → Repository → DataSource (API/SDK)
Response → Repository (map to Domain Entity) → BLoC (emit Success/Error) → UI

Dart 3 Language Features

Dart 3 语言特性

  • Sealed Classes: Use
    sealed class
    for domain failures to enable exhaustive pattern matching across layers.
  • Records: Use records for lightweight multi-value returns where defining a full class is overkill (e.g.,
    (String name, int age)
    ).
  • If-Case Pattern: Prefer
    if (value case final v?)
    over
    if (value != null)
    for null checking with binding.
  • Class Modifiers: Use
    final
    ,
    interface
    ,
    base
    , and
    sealed
    class modifiers to express API intent.
  • 密封类:使用
    sealed class
    定义领域层错误,以实现跨层的穷尽模式匹配。
  • 记录类型:在无需定义完整类的场景下,使用记录类型实现轻量级多值返回(如
    (String name, int age)
    )。
  • If-Case模式:优先使用
    if (value case final v?)
    而非
    if (value != null)
    进行带绑定的空值检查。
  • 类修饰符:使用
    final
    interface
    base
    sealed
    类修饰符明确API意图。

Error Handling

错误处理

  • Functional Error Handling: Use
    Either<Failure, T>
    or
    Result<T>
    sealed classes. NEVER throw exceptions across layer boundaries.
  • Pattern Matching: Exhaustively handle all sealed class states using Dart 3.x
    switch
    expressions in UI and BLoCs.
  • Throw errors when needed, and catch them at appropriate boundaries
  • Log errors with context
  • Present user-friendly error messages in the UI
  • Avoid silent failures; always handle or propagate errors
  • 函数式错误处理:使用
    Either<Failure, T>
    Result<T>
    密封类。禁止跨层抛出异常。
  • 模式匹配:在UI层和BLoC中使用Dart 3.x的
    switch
    表达式穷尽处理所有密封类状态。
  • 在必要时抛出错误,并在合适的边界捕获
  • 附带上下文信息记录错误
  • 在UI中展示用户友好的错误提示
  • 禁止静默失败;必须处理或传播错误

Platform Channels & Native Integration

平台通道与原生集成

  • Use
    MethodChannel
    for one-off calls to native code (e.g., reading device info, triggering native APIs)
  • Use
    EventChannel
    for continuous streams from native to Flutter (e.g., sensor data, connectivity changes)
  • Place all channel code in a dedicated
    platform/
    directory within the relevant feature
  • Define channel names as constants:
    static const channel = MethodChannel('com.app.feature/method')
  • Wrap all channel calls in a DataSource — never call
    MethodChannel
    directly from BLoC or UI
  • Handle
    MissingPluginException
    gracefully for platforms that don't implement the channel
  • Use
    defaultTargetPlatform
    checks to guard platform-specific behavior
  • 使用
    MethodChannel
    实现对原生代码的一次性调用(如读取设备信息、触发原生API)
  • 使用
    EventChannel
    实现从原生到Flutter的持续数据流(如传感器数据、网络连接状态变化)
  • 将所有通道代码放在对应功能模块下的专属
    platform/
    目录中
  • 将通道名称定义为常量:
    static const channel = MethodChannel('com.app.feature/method')
  • 将所有通道调用包装在DataSource中——禁止从BLoC或UI层直接调用
    MethodChannel
  • 对未实现该通道的平台,优雅处理
    MissingPluginException
  • 使用
    defaultTargetPlatform
    检查来防护平台特定行为

FFI (Foreign Function Interface)

FFI(外部函数接口)

  • Use
    dart:ffi
    for performance-critical native C/C++ code
  • Define bindings in a separate class with clear documentation
  • Prefer Federated Plugins when sharing native code across multiple packages
  • 对性能关键的原生C/C++代码,使用
    dart:ffi
  • 在独立类中定义绑定并添加清晰的文档
  • 当在多个包之间共享原生代码时,优先使用联邦插件

Platform-Specific Code

平台特定代码

  • Use
    Platform.isAndroid
    /
    Platform.isIOS
    for runtime checks (import
    dart:io
    )
  • For web, use
    kIsWeb
    from
    package:flutter/foundation.dart
  • Prefer adaptive widgets (
    Switch.adaptive
    ,
    Slider.adaptive
    ) over manual platform checks where possible
  • 使用
    Platform.isAndroid
    /
    Platform.isIOS
    进行运行时检查(需导入
    dart:io
  • 针对Web平台,使用
    package:flutter/foundation.dart
    中的
    kIsWeb
  • 尽可能使用自适应组件(如
    Switch.adaptive
    Slider.adaptive
    )替代手动平台检查

Coding Guidelines & Maintenance

编码规范与维护

  • Conciseness: Keep files < 300 lines and functions < 50 lines. Keep classes to < 10 public methods.
  • Strong Typing: STRICTLY prohibit
    dynamic
    . Use
    Object?
    or explicit types.
  • Guard Clauses: Use early returns (e.g.,
    if (user == null) return;
    ) to reduce nesting and improve readability.
  • Disposable Lifecycle:
    TextEditingController
    ,
    ScrollController
    ,
    FocusNode
    ,
    StreamSubscription
    ,
    AnimationController
    , etc., MUST be
    late
    initialized in
    initState()
    and disposed in
    dispose()
    .
  • No Print Statements: STRICTLY prohibit
    print()
    . Use
    AppLogger
    for all logging.
  • Reuse: Extract widgets or code blocks used multiple times into
    core/views/widgets
    or utilities.
  • 简洁性:单个文件代码行数不超过300,函数不超过50行,类的公共方法不超过10个。
  • 强类型:严格禁止使用
    dynamic
    类型,使用
    Object?
    或显式类型替代。
  • 卫语句:使用提前返回(如
    if (user == null) return;
    )减少嵌套,提升可读性。
  • 可释放生命周期
    TextEditingController
    ScrollController
    FocusNode
    StreamSubscription
    AnimationController
    等必须在
    initState()
    中使用
    late
    初始化,并在
    dispose()
    中释放。
  • 禁止Print语句:严格禁止使用
    print()
    ,所有日志记录使用
    AppLogger
  • 复用性:将多次使用的组件或代码块提取到
    core/views/widgets
    或工具类中。

Documentation

文档规范

  • Why, not What: Comments MUST explain the rationale (intent), not what the code does.
  • Public API: Document public classes and methods with triple-slash (
    ///
    ) comments.
  • History: Do NOT include version history or "fixed by" comments. Git is the source of truth.
  • 说明原因而非内容:注释必须解释设计意图,而非代码功能。
  • 公共API:使用三斜杠(
    ///
    )注释为公共类和方法添加文档。
  • 历史记录:禁止包含版本历史或“由谁修复”的注释,Git为唯一的事实来源。