SKILL.md
只读
名称
flutter-apply-architecture-best-practices
描述
使用推荐的分层方法(UI、逻辑、数据)架构 Flutter 应用程序。适用于构建新项目或为可扩展性重构时使用。
架构 Flutter 应用程序
目录
架构分层
通过将应用程序划分为不同的层来强制实现严格的关注点分离。切勿将 UI 渲染与业务逻辑或数据获取混在一起。
UI 层(表示层)
实现 MVVM(Model-View-ViewModel)模式来管理 UI 状态和逻辑。
- 视图: 编写可重用、精简的 widget。将视图中的逻辑限制为 UI 特定的操作(例如动画、布局约束、简单路由)。从 ViewModel 传递所有所需数据。
- ViewModel: 管理 UI 状态并处理用户交互。扩展
ChangeNotifier(或使用Listenable)来暴露状态。向视图暴露不可变的状态快照。通过构造函数将 Repository 注入 ViewModel。
数据层
实现 Repository 模式以隔离数据访问逻辑并创建单一数据源。
- 服务: 创建无状态类来封装外部 API(HTTP 客户端、本地数据库、平台插件)。返回原始 API 模型或
Result包装器。 - 仓库: 使用一个或多个服务。将原始 API 模型转换为干净的领域模型。处理缓存、离线同步和重试逻辑。向 ViewModel 暴露领域模型。
逻辑层(领域层 - 可选)
- 用例: 仅当应用程序包含使 ViewModel 变得臃肿的复杂业务逻辑,或者逻辑需要在多个 ViewModel 之间复用时,才实现此层。将此逻辑提取到专用的用例(交互器)类中,这些类位于 ViewModel 和 Repository 之间。
项目结构
使用混合方法组织代码库:按功能分组 UI 组件,按类型分组数据/领域组件。
lib/
├── data/
│ ├── models/ # API 模型
│ ├── repositories/ # 仓库实现
│ └── services/ # API 客户端、本地存储包装器
├── domain/
│ ├── models/ # 干净的领域模型
│ └── use_cases/ # 可选的业务逻辑类
└── ui/
├── core/ # 共享 widget、主题、排版
└── features/
└── [feature_name]/
├── view_models/
└── views/
工作流程:实现新功能
在向应用程序添加新功能时,请遵循以下顺序工作流程。复制清单以跟踪进度。
任务进度
- [ ] 步骤 1:定义领域模型。 使用
freezed或built_value为该功能创建不可变的数据类。 - [ ] 步骤 2:实现服务。 创建或更新服务类以处理外部 API 通信。
- [ ] 步骤 3:实现仓库。 创建仓库以使用服务并返回领域模型。
- [ ] 步骤 4:应用条件逻辑(领域层)。
- 如果该功能需要复杂的数据转换或跨仓库逻辑: 创建一个用例类。
- 如果该功能是简单的 CRUD 操作: 跳到步骤 5。
- [ ] 步骤 5:实现 ViewModel。 创建扩展
ChangeNotifier的 ViewModel。注入所需的仓库/用例。暴露不可变的状态和命令方法。 - [ ] 步骤 6:实现视图。 创建 UI widget。使用
ListenableBuilder或AnimatedBuilder监听 ViewModel 的变化。 - [ ] 步骤 7:注入依赖。 在依赖注入容器(例如
provider或get_it)中注册新的服务、仓库和 ViewModel。 - [ ] 步骤 8:运行验证器。 执行 ViewModel 和仓库的单元测试。
- 反馈循环: 运行测试 -> 审查失败 -> 修复逻辑 -> 重新运行直到通过。
示例
数据层:服务和仓库
// 1. 服务(原始 API 交互)
class ApiClient {
Future<UserApiModel> fetchUser(String id) async {
// HTTP GET 实现...
}
}
// 2. 仓库(单一数据源,返回领域模型)
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); // 转换为领域模型
return _cachedUser!;
}
}
UI 层:ViewModel 和视图
// 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. 视图(哑 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('用户未找到'));
}
return Column(
children: [
Text(user.name),
ElevatedButton(
onPressed: () => viewModel.loadProfile(user.id),
child: const Text('刷新'),
),
],
);
},
);
}
}




