SKILL.md
只读
名称
flutter-apply-architecture-best-practices
描述
采用推荐的分层架构(UI 层、逻辑层、数据层)构建 Flutter 应用。适用于搭建新项目架构或进行可扩展性重构。
Flutter 应用架构最佳实践
目录
架构分层
严格遵循“关注点分离”原则,将应用程序划分为独立的架构层。切勿将 UI 渲染与业务逻辑或数据获取混在一起。
UI 层(表示层)
采用 MVVM(Model-View-ViewModel)模式管理 UI 状态和逻辑。
- Views(视图): 编写高复用、轻量化的 Widget。View 内部逻辑仅限 UI 相关操作(如动画、布局约束、简单路由)。ViewModel 所需的所有数据均由外部传入。
- ViewModels(视图模型): 管理 UI 状态并处理用户交互。继承
ChangeNotifier(或使用Listenable)来暴露状态。向 View 暴露不可变的状态快照。通过构造函数将 Repository 注入到 ViewModel 中。
Data 层(数据层)
采用 Repository(存储库)模式隔离数据访问逻辑,打造单一数据源(Single Source of Truth)。
- Services(服务): 创建无状态类来封装外部 API(HTTP 客户端、本地数据库、平台插件)。返回原始 API 模型或
Result包装对象。 - Repositories(存储库): 调用一个或多个 Service。将原始 API 模型转换为干净的 Domain Model(领域模型)。负责处理缓存、离线同步和重试逻辑。向 ViewModel 暴露 Domain Model。
Logic 层(Domain 领域层 - 可选)
- Use Cases(用例): 仅当应用包含较为复杂的业务逻辑(导致 ViewModel 过度膨胀),或者逻辑需要在多个 ViewModel 之间复用时,才需要引入该层。将这些逻辑提取到独立的 Use Case(交互器)类中,位于 ViewModel 与 Repository 之间。
项目结构
采用混合方式组织代码库:UI 组件按功能模块(feature)分组,Data/Domain 组件按类型分组。
lib/
├── data/
│ ├── models/ # API 模型
│ ├── repositories/ # Repository 实现
│ └── services/ # API 客户端、本地存储封装
├── domain/
│ ├── models/ # 干净的领域模型
│ └── use_cases/ # 可选的业务逻辑类
└── ui/
├── core/ # 共享 Widget、主题、排版
└── features/
└── [feature_name]/
├── view_models/
└── views/
工作流:开发新功能
为应用添加新功能时,请遵循以下顺序工作流。可以复制此清单来跟踪进度。
任务进度清单
- [ ] 步骤 1:定义 Domain Model。 使用
freezed或built_value为该功能创建不可变的数据类。 - [ ] 步骤 2:实现 Service。 创建或更新 Service 类,负责处理与外部 API 的通信。
- [ ] 步骤 3:实现 Repository。 创建 Repository 类,调用 Service 并返回 Domain Model。
- [ ] 步骤 4:根据条件选择逻辑(Domain 层)。
- 如果该功能涉及复杂的数据转换或跨 Repository 逻辑: 创建 Use Case 类。
- 如果该功能仅为简单的 CRUD 操作: 直接跳至步骤 5。
- [ ] 步骤 5:实现 ViewModel。 创建继承自
ChangeNotifier的 ViewModel。注入所需的 Repository/Use Case。暴露不可变状态与命令方法。 - [ ] 步骤 6:实现 View。 创建 UI Widget。使用
ListenableBuilder或AnimatedBuilder监听 ViewModel 的变化。 - [ ] 步骤 7:注入依赖。 在依赖注入容器(如
provider或get_it)中注册新的 Service、Repository 和 ViewModel。 - [ ] 步骤 8:运行校验。 为 ViewModel 和 Repository 执行单元测试。
- 反馈循环: 运行测试 -> 检查失败项 -> 修复逻辑 -> 重新运行直到全部通过。
示例
Data 层:Service 与 Repository
// 1. Service(负责与原始 API 交互)
class ApiClient {
Future<UserApiModel> fetchUser(String id) async {
// HTTP GET 实现...
}
}
// 2. Repository(单一数据源,返回 Domain Model)
class UserRepository {
UserRepository({required ApiClient apiClient}) : _apiClient = apiClient;
final ApiClient _apiClient;
User? _cachedUser;
Future<User> getUser(String id) async {
if (_cachedUser != null) return _cachedUser!;
final apiModel = await _apiClient.fetchUser(id);
_cachedUser = User(id: apiModel.id, name: apiModel.fullName); // 转换为 Domain Model
return _cachedUser!;
}
}
UI 层:ViewModel 与 View
// 3. ViewModel(状态管理与表示层逻辑)
class ProfileViewModel extends ChangeNotifier {
ProfileViewModel({required UserRepository userRepository})
: _userRepository = userRepository;
final UserRepository _userRepository;
User? _user;
User? get user => _user;
bool _isLoading = false;
bool get isLoading => _isLoading;
Future<void> loadProfile(String id) async {
_isLoading = true;
notifyListeners();
try {
_user = await _userRepository.getUser(id);
} finally {
_isLoading = false;
notifyListeners();
}
}
}
// 4. View(纯 UI 展示组件)
class ProfileView extends StatelessWidget {
const ProfileView({super.key, required this.viewModel});
final ProfileViewModel viewModel;
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: viewModel,
builder: (context, _) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
}
final user = viewModel.user;
if (user == null) {
return const Center(child: Text('User not found'));
}
return Column(
children: [
Text(user.name),
ElevatedButton(
onPressed: () => viewModel.loadProfile(user.id),
child: const Text('Refresh'),
),
],
);
},
);
}
}






