关于 flutter-apply-architecture-best-practices
为 Flutter 开发提供分层架构与功能实现流程指导,适合需要组织 UI、数据访问和业务逻辑的开发者。内容强调 MVVM、Repository 模式及逐步验证。
主要功能
- 01MVVM 管理界面状态
- 02Repository 隔离数据访问
- 03按需提取 Use Case
- 04按步骤实现新功能
- 05通过单元测试验证
适用场景
- 01规划 Flutter 项目分层
- 02实现依赖服务的数据功能
- 03拆分复杂或复用业务逻辑
- 04验证 ViewModel 与 Repository
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ flutter/ agent-plugins --skill flutter-apply-architecture-best-practices安装后在 Claude Code 中输入/flutter-apply-architecture-best-practices确认已加载。
详细介绍
Flutter 应用架构设计
目录
架构层
通过将应用划分为不同层,严格遵循关注点分离原则。切勿将 UI 渲染与业务逻辑或数据获取混在一起。
UI 层(展示层)
采用 MVVM(Model-View-ViewModel)模式管理 UI 状态和逻辑。
- **视图:**编写可复用、精简的组件。视图中的逻辑仅限于 UI 特定操作(例如动画、布局约束、简单路由)。将所有必需的数据从 ViewModel 传入。
- **ViewModel:**管理 UI 状态并处理用户交互。继承
ChangeNotifier(或使用Listenable)以公开状态。向视图公开不可变的状态快照。通过构造函数将 Repository 注入 ViewModel。
数据层
采用 Repository 模式隔离数据访问逻辑,并创建单一事实来源。
- **服务:**创建无状态类,用于封装外部 API(HTTP 客户端、本地数据库、平台插件)。返回原始 API 模型或
Result封装对象。 - **Repository:**使用一个或多个服务。将原始 API 模型转换为简洁的领域模型。处理缓存、离线同步和重试逻辑。向 ViewModel 公开领域模型。
逻辑层(领域层——可选)
- **用例:**仅当应用包含会使 ViewModel 变得臃肿的复杂业务逻辑,或逻辑需要在多个 ViewModel 间复用时,才实现此层。将这些逻辑提取到专用的用例(交互器)类中,置于 ViewModel 和 Repository 之间。
项目结构
采用混合方式组织代码库:按功能对 UI 组件分组,按类型对数据层/领域层组件分组。
lib/
├── data/
│ ├── models/ # API models
│ ├── repositories/ # Repository implementations
│ └── services/ # API clients, local storage wrappers
├── domain/
│ ├── models/ # Clean domain models
│ └── use_cases/ # Optional business logic classes
└── ui/
├── core/ # Shared widgets, themes, typography
└── features/
└── [feature_name]/
├── view_models/
└── views/
工作流:实现新功能
向应用添加新功能时,请遵循以下顺序工作流。复制此清单以跟踪进度。
任务进度
- **步骤 1:定义领域模型。**使用
freezed或built_value为该功能创建不可变数据类。 - **步骤 2:实现服务。**创建或更新服务类,以处理外部 API 通信。
- **步骤 3:实现 Repository。**创建 Repository 以使用服务并返回领域模型。
- 步骤 4:应用条件逻辑(领域层)。
- *如果该功能需要复杂的数据转换或跨 Repository 逻辑:*创建一个用例类。
- *如果该功能是简单的 CRUD 操作:*跳至步骤 5。
- **步骤 5:实现 ViewModel。**创建继承
ChangeNotifier的 ViewModel。注入所需的 Repository/用例。公开不可变状态和命令方法。 - **步骤 6:实现视图。**创建 UI 组件。使用
ListenableBuilder或AnimatedBuilder监听 ViewModel 的变化。 - **步骤 7:注入依赖项。**在依赖注入容器(例如
provider或get_it)中注册新的服务、Repository 和 ViewModel。 - **步骤 8:运行验证器。**为 ViewModel 和 Repository 执行单元测试。
- *反馈循环:*运行测试 -> 查看失败项 -> 修复逻辑 -> 重新运行,直到测试通过。
示例
数据层:服务和 Repository
// 1. Service (Raw API interaction)
class ApiClient {
Future<UserApiModel> fetchUser(String id) async {
// HTTP GET implementation...
}
}
// 2. Repository (Single source of truth, returns 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); // Transform to Domain Model
return _cachedUser!;
}
}
UI 层:ViewModel 和视图
// 3. ViewModel (State management and presentation logic)
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 (Dumb UI component)
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'),
),
],
);
},
);
}
}
评论
支持 Markdown