关于 flutter-apply-architecture-best-practices
flutter-apply-architecture-best-practices 是一项提供 Flutter 与 Dart 实践指导的技能,采用 Clean Architecture 和按功能组织的结构。它规定分层边界、数据模型约定、错误处理方式及新增功能的步骤。
主要功能
- 01Clean Architecture 分层边界
- 02按功能组织目录结构
- 03三层数据模型约定
- 04Dart 3 错误处理模式
- 05新增功能的分步清单
适用场景
- 01规划新的 Flutter 功能结构
- 02分离 API、领域和 UI 模型
- 03检查数据访问与依赖边界
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dhruvanbhalara/ skills --skill flutter-apply-architecture-best-practices安装后在 Claude Code 中输入/flutter-apply-architecture-best-practices确认已加载。
详细介绍
技术栈
- 使用 Flutter 进行跨平台开发
- 使用 Dart 作为主要编程语言
- 使用 bloc 进行状态管理
- 使用 injectable 进行依赖注入
- 使用 Dart Mappable 定义不可变数据模型
- 使用 Dio 进行 HTTP 网络通信
- 使用 isar 作为本地数据库
- 使用 Firebase 提供后端服务
整洁架构
- 领域层纯净:
domain层必须是纯 Dart。禁止导入package:flutter。 - 层级依赖:
Presentation -> Domain <- Data。Data 层实现 Domain 接口。 - 以功能为先 2.0:严格区分
DataSources(外部/原始数据)与Repositories(领域抽象)。
目录结构
- 按照基于功能的整洁架构模式组织代码(
featureA/bloc、featureA/models、featureA/views) - 将跨功能组件放在
core目录中 - 在
core/views/widgets中按类型对共享组件进行分组
三层数据模型模式
-
API 层(ItemApiModel)
- 表示从服务器接收的原始数据结构
- 包含 API 提供的所有字段
- 应与 API 契约完全一致
-
领域层(Item)
- 表示内部数据模型
- 仅包含业务逻辑所需的字段
- 剔除不必要的 API 字段
-
UI 层(ItemUiState)
- 表示为 UI 渲染优化的数据模型
- 包含已解析和格式化、可直接显示的数据
- 处理所有 UI 特定的转换
数据模型规则
- 使用 Dart Mappable 定义不可变的 UI 状态
- 每一层都应有自己的类型定义
- UI 层应使用 UI 状态数据模型,绝不能直接使用领域模型或 API 模型
- UI 状态模型应从领域模型派生,而不是从 API 模型派生
- 领域模型应从 API 模型派生,而不是从 UI 状态模型派生
Repository 与 DataSource 模式
- Repository 在数据源之间协调(返回领域模型)
- 数据源访问原始数据(返回 API 模型)
- Repository 应作为事实来源,并返回领域模型
- DataSources 必须仅包含原始 SDK/API 调用。不得包含映射或业务逻辑。
- DataSources 以外不得直接调用后端 SDK/API
数据流
UI Event → BLoC (emit Loading) → Repository → DataSource (API/SDK)
↓
Response → Repository (map to Domain Entity) → BLoC (emit Success/Error) → UI
Dart 3 语言特性
- 密封类:对领域故障使用
sealed class,以便在各层进行穷尽式模式匹配。 - 记录:在定义完整类显得过于繁琐时,使用记录进行轻量级多值返回(例如
(String name, int age))。 - If-Case 模式:进行带绑定的空值检查时,优先使用
if (value case final v?),而不是if (value != null)。 - 类修饰符:使用
final、interface、base和sealed类修饰符来表达 API 意图。
错误处理
- 函数式错误处理:使用
Either<Failure, T>或Result<T>密封类。绝不要跨层边界抛出异常。 - 模式匹配:在 UI 和 BLoC 中使用 Dart 3.x
switch表达式,穷尽处理所有密封类状态。 - 在需要时抛出错误,并在适当的边界处捕获
- 在日志中记录错误上下文
- 在 UI 中向用户显示易于理解的错误消息
- 避免静默失败;始终处理或传播错误
平台通道与原生集成
- 使用
MethodChannel执行一次性原生代码调用(例如读取设备信息、触发原生 API) - 使用
EventChannel接收从原生端到 Flutter 的持续数据流(例如传感器数据、连接状态变化) - 将所有通道代码放在相关功能下专用的
platform/目录中 - 将通道名称定义为常量:
static const channel = MethodChannel('com.app.feature/method') - 将所有通道调用封装在 DataSource 中:绝不要直接从 BLoC 或 UI 调用
MethodChannel - 对于未实现该通道的平台,妥善处理
MissingPluginException - 使用
defaultTargetPlatform检查来保护平台特定行为
FFI(外部函数接口)
- 使用
dart:ffi处理对性能要求较高的原生 C/C++ 代码 - 在单独的类中定义绑定,并提供清晰的文档说明
- 跨多个软件包共享原生代码时,优先使用 Federated Plugins
特定平台的代码
- 使用
Platform.isAndroid/Platform.isIOS进行运行时检查(导入dart:io) - 对于 Web,使用
package:flutter/foundation.dart中的kIsWeb - 尽可能优先使用自适应组件(
Switch.adaptive、Slider.adaptive),而不是手动进行平台检查
编码指南与维护
- 简洁性:文件保持在 300 行以内,函数保持在 50 行以内。每个类的公开方法少于 10 个。
- 强类型:严格禁止使用
dynamic。请使用Object?或显式类型。 - 守卫子句:使用提前返回(例如,
if (user == null) return;)减少嵌套并提升可读性。 - 可释放对象的生命周期:
TextEditingController、ScrollController、FocusNode、StreamSubscription、AnimationController等,必须在initState()中使用late初始化,并在dispose()中释放。 - 禁止使用打印语句:严格禁止使用
print()。所有日志记录都使用AppLogger。 - 复用:将多次使用的组件或代码块提取到
core/views/widgets或工具类中。
文档
- 说明原因,而非内容:注释必须解释设计依据(意图),而非代码的功能。
- 公开 API:使用三斜线(
///)注释为公开类和方法编写文档。 - 历史记录:不要添加版本历史或“修复者”注释。Git 才是事实来源。
工作流:实现新功能
为应用添加新功能时,请按顺序遵循以下工作流。复制此检查清单以跟踪进度。
任务进度
- 步骤 1:定义领域模型。 使用 Dart Mappable 为该功能创建不可变数据类。
- 步骤 2:实现数据源。 创建或更新 DataSource 类,以处理原始 SDK/API 调用(Dio、Isar、Firebase)。
- 步骤 3:实现仓库。 创建 Repository 以使用数据源并返回纯领域模型。
- 步骤 4:实现 BLoC/Cubit。 创建状态管理。注入所需的 Repository。公开不可变 UI 状态。
- 步骤 5:实现 UI。 创建 View 组件。使用
BlocBuilder或BlocConsumer监听状态变化。 - 步骤 6:注入依赖项。 在依赖注入容器(
injectable)中注册新的 DataSource、Repository 和 BLoC。
评论
支持 Markdown