flutter-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTechnology 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 layer must be pure Dart. NO
domainimports.package:flutter - Layer Dependency: . Data layer implements Domain interfaces.
Presentation -> Domain <- Data - Feature-First 2.0: Enforce strict separation of (External/Raw) vs
DataSources(Domain abstraction).Repositories
- 领域层纯净性:层必须是纯Dart代码,禁止导入
domain。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 directory
core - Group shared widgets in by type
core/views/widgets
- 按照基于功能的整洁架构模式组织代码(如、
featureA/bloc、featureA/models)featureA/views - 将跨功能组件放在目录中
core - 按类型将共享组件分组到目录
core/views/widgets
Three-Layer Data Model Pattern
三层数据模型模式
-
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
-
Domain Layer (Item)
- Represents the internal data model
- Contains only the fields necessary for business logic
- Strips out unnecessary API fields
-
UI Layer (ItemUiState)
- Represents the data model optimized for UI rendering
- Contains parsed and formatted data ready for display
- Handles all UI-specific transformations
-
API层(ItemApiModel)
- 表示从服务器接收的原始数据结构
- 包含API提供的所有字段
- 必须完全匹配API契约
-
领域层(Item)
- 表示内部数据模型
- 仅包含业务逻辑所需的字段
- 剔除不必要的API字段
-
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) → UIUI Event → BLoC (emit Loading) → Repository → DataSource (API/SDK)
↓
Response → Repository (map to Domain Entity) → BLoC (emit Success/Error) → UIDart 3 Language Features
Dart 3 语言特性
- Sealed Classes: Use for domain failures to enable exhaustive pattern matching across layers.
sealed class - 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 over
if (value case final v?)for null checking with binding.if (value != null) - Class Modifiers: Use ,
final,interface, andbaseclass modifiers to express API intent.sealed
- 密封类:使用定义领域层错误,以实现跨层的穷尽模式匹配。
sealed class - 记录类型:在无需定义完整类的场景下,使用记录类型实现轻量级多值返回(如)。
(String name, int age) - If-Case模式:优先使用而非
if (value case final v?)进行带绑定的空值检查。if (value != null) - 类修饰符:使用、
final、interface和base类修饰符明确API意图。sealed
Error Handling
错误处理
- Functional Error Handling: Use or
Either<Failure, T>sealed classes. NEVER throw exceptions across layer boundaries.Result<T> - Pattern Matching: Exhaustively handle all sealed class states using Dart 3.x expressions in UI and BLoCs.
switch - 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 for one-off calls to native code (e.g., reading device info, triggering native APIs)
MethodChannel - Use for continuous streams from native to Flutter (e.g., sensor data, connectivity changes)
EventChannel - Place all channel code in a dedicated directory within the relevant feature
platform/ - Define channel names as constants:
static const channel = MethodChannel('com.app.feature/method') - Wrap all channel calls in a DataSource — never call directly from BLoC or UI
MethodChannel - Handle gracefully for platforms that don't implement the channel
MissingPluginException - Use checks to guard platform-specific behavior
defaultTargetPlatform
- 使用实现对原生代码的一次性调用(如读取设备信息、触发原生API)
MethodChannel - 使用实现从原生到Flutter的持续数据流(如传感器数据、网络连接状态变化)
EventChannel - 将所有通道代码放在对应功能模块下的专属目录中
platform/ - 将通道名称定义为常量:
static const channel = MethodChannel('com.app.feature/method') - 将所有通道调用包装在DataSource中——禁止从BLoC或UI层直接调用
MethodChannel - 对未实现该通道的平台,优雅处理
MissingPluginException - 使用检查来防护平台特定行为
defaultTargetPlatform
FFI (Foreign Function Interface)
FFI(外部函数接口)
- Use for performance-critical native C/C++ code
dart:ffi - 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.isAndroidfor runtime checks (importPlatform.isIOS)dart:io - For web, use from
kIsWebpackage:flutter/foundation.dart - Prefer adaptive widgets (,
Switch.adaptive) over manual platform checks where possibleSlider.adaptive
- 使用/
Platform.isAndroid进行运行时检查(需导入Platform.isIOS)dart:io - 针对Web平台,使用中的
package:flutter/foundation.dartkIsWeb - 尽可能使用自适应组件(如、
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 . Use
dynamicor explicit types.Object? - Guard Clauses: Use early returns (e.g., ) to reduce nesting and improve readability.
if (user == null) return; - Disposable Lifecycle: ,
TextEditingController,ScrollController,FocusNode,StreamSubscription, etc., MUST beAnimationControllerinitialized inlateand disposed ininitState().dispose() - No Print Statements: STRICTLY prohibit . Use
print()for all logging.AppLogger - Reuse: Extract widgets or code blocks used multiple times into or utilities.
core/views/widgets
- 简洁性:单个文件代码行数不超过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为唯一的事实来源。