Flutter 工程构架设计(MVVM + Repository)
Flutter 工程构架设计MVVM Repository在 Flutter 应用开发中随着业务复杂度的提升合理的工程架构设计显得尤为重要。MVVMModel-View-ViewModel结合 Repository 模式能够有效分离关注点提高代码的可维护性、可测试性和可扩展性。本文将从原理出发深入剖析这一架构设计并通过可运行的代码示例演示其实现方式。## 架构核心原理### MVVM 模式MVVM 将应用分为三层-Model数据层负责业务逻辑和数据处理通常包含数据实体和业务规则。-ViewUI 层负责展示用户界面通常是一个 Widget。-ViewModel连接 View 和 Model 的桥梁负责将 Model 的数据转换为 View 可以直接使用的状态并处理用户交互事件。MVVM 的核心优势在于数据绑定即 ViewModel 中的状态变化会自动通知 View 更新从而实现响应式 UI。在 Flutter 中我们可以借助ChangeNotifier和Provider来实现这一机制。### Repository 模式Repository 模式是数据访问的抽象层它位于数据源和业务逻辑之间。数据源可以是远程 API、本地数据库如 SQLite、Hive或内存缓存。Repository 封装了数据获取和存储的逻辑使得业务层如 ViewModel无需关心数据来源的细节从而解耦了数据层与业务层。### 结合 MVVM 与 Repository将两者结合我们可以构建如下分层架构-View仅负责 UI 渲染通过监听 ViewModel 的状态来更新界面。-ViewModel包含业务逻辑调用 Repository 获取数据并将数据转换为 View 的状态。-Repository统一数据访问接口协调多个数据源如网络和缓存确保数据一致性。这种架构的优点是- 可测试性ViewModel 和 Repository 可以独立测试。- 可维护性各层职责清晰修改数据源或 UI 不影响其他层。- 可扩展性可以轻松替换数据源如从 REST API 切换到 GraphQL。## 代码示例用户列表应用下面通过一个简单的用户列表应用来演示 MVVM Repository 的实现。我们将使用Provider作为状态管理工具。### 1. 项目依赖首先在pubspec.yaml中添加依赖yamldependencies: flutter: sdk: flutter provider: ^6.0.0 http: ^0.13.0### 2. 定义数据模型创建User模型表示用户数据dart// models/user.dartclass User { final int id; final String name; final String email; User({required this.id, required this.name, required this.email}); // 从 JSON 解析 User 对象 factory User.fromJson(MapString, dynamic json) { return User( id: json[id], name: json[name], email: json[email], ); }}### 3. 实现 RepositoryUserRepository封装数据访问逻辑这里我们使用模拟数据来模拟网络请求dart// repositories/user_repository.dartimport package:flutter/foundation.dart;import ../models/user.dart;class UserRepository { // 模拟从远程 API 获取用户列表 FutureListUser fetchUsers() async { // 模拟网络延迟 await Future.delayed(const Duration(seconds: 1)); // 模拟返回数据 return [ User(id: 1, name: Alice, email: aliceexample.com), User(id: 2, name: Bob, email: bobexample.com), User(id: 3, name: Charlie, email: charlieexample.com), ]; } // 模拟根据 ID 获取单个用户 FutureUser getUserById(int id) async { await Future.delayed(const Duration(milliseconds: 500)); return User(id: id, name: User $id, email: user$idexample.com); }}### 4. 实现 ViewModelUserViewModel负责管理 UI 状态并调用 Repository 获取数据dart// view_models/user_view_model.dartimport package:flutter/foundation.dart;import ../models/user.dart;import ../repositories/user_repository.dart;// 定义 ViewModel 的状态类class UserViewModelState { final bool isLoading; final ListUser users; final String? errorMessage; UserViewModelState({ this.isLoading false, this.users const [], this.errorMessage, });}class UserViewModel extends ChangeNotifier { final UserRepository _repository; UserViewModelState _state UserViewModelState(); UserViewModel(this._repository); // 暴露状态给 View 使用 UserViewModelState get state _state; // 加载用户列表 Futurevoid loadUsers() async { // 更新状态开始加载 _state UserViewModelState(isLoading: true); notifyListeners(); try { final users await _repository.fetchUsers(); // 更新状态加载成功 _state UserViewModelState(users: users); } catch (e) { // 更新状态加载失败 _state UserViewModelState(errorMessage: Failed to load users: $e); } notifyListeners(); } // 添加新用户模拟业务操作 Futurevoid addUser(String name, String email) async { // 这里可以调用 Repository 的添加方法 // 为简化演示直接更新本地列表 final newUser User(id: _state.users.length 1, name: name, email: email); _state UserViewModelState( users: [..._state.users, newUser], ); notifyListeners(); }}### 5. 构建 ViewView 层使用Provider获取 ViewModel并监听状态变化dart// screens/user_list_screen.dartimport package:flutter/material.dart;import package:provider/provider.dart;import ../view_models/user_view_model.dart;class UserListScreen extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(User List)), body: ConsumerUserViewModel( builder: (context, viewModel, _) { final state viewModel.state; // 显示加载中 if (state.isLoading) { return Center(child: CircularProgressIndicator()); } // 显示错误信息 if (state.errorMessage ! null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(state.errorMessage!), ElevatedButton( onPressed: () viewModel.loadUsers(), child: Text(Retry), ), ], ), ); } // 显示用户列表 return ListView.builder( itemCount: state.users.length, itemBuilder: (context, index) { final user state.users[index]; return ListTile( title: Text(user.name), subtitle: Text(user.email), ); }, ); }, ), floatingActionButton: FloatingActionButton( onPressed: () _showAddUserDialog(context), child: Icon(Icons.add), ), ); } void _showAddUserDialog(BuildContext context) { final nameController TextEditingController(); final emailController TextEditingController(); showDialog( context: context, builder: (context) AlertDialog( title: Text(Add User), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameController, decoration: InputDecoration(labelText: Name), ), TextField( controller: emailController, decoration: InputDecoration(labelText: Email), ), ], ), actions: [ TextButton( onPressed: () Navigator.pop(context), child: Text(Cancel), ), TextButton( onPressed: () { final viewModel context.readUserViewModel(); viewModel.addUser( nameController.text, emailController.text, ); Navigator.pop(context); }, child: Text(Add), ), ], ), ); }}### 6. 注册依赖并启动应用在main.dart中使用Provider注册 ViewModeldart// main.dartimport package:flutter/material.dart;import package:provider/provider.dart;import repositories/user_repository.dart;import view_models/user_view_model.dart;import screens/user_list_screen.dart;void main() { runApp(MyApp());}class MyApp extends StatelessWidget { override Widget build(BuildContext context) { return MultiProvider( providers: [ // 注册 UserRepository ProviderUserRepository(create: (_) UserRepository()), // 注册 UserViewModel依赖 UserRepository ChangeNotifierProviderUserViewModel( create: (context) UserViewModel(context.readUserRepository()), ), ], child: MaterialApp( title: MVVM Repository Demo, home: UserListScreen(), ), ); }}## 架构扩展与最佳实践### 1. 数据源的多样化在实际项目中Repository 可以整合多个数据源。例如先从缓存获取数据再异步从网络更新dartclass UserRepository { final LocalDataSource _localDataSource; final RemoteDataSource _remoteDataSource; UserRepository(this._localDataSource, this._remoteDataSource); FutureListUser fetchUsers() async { // 优先从缓存获取 final cachedUsers await _localDataSource.getUsers(); if (cachedUsers.isNotEmpty) { return cachedUsers; } // 缓存为空时从网络获取 final remoteUsers await _remoteDataSource.fetchUsers(); await _localDataSource.saveUsers(remoteUsers); return remoteUsers; }}### 2. 错误处理与重试逻辑在 ViewModel 中可以添加更完善的错误处理例如重试机制dartFuturevoid loadUsersWithRetry({int maxRetries 3}) async { int retries 0; while (retries maxRetries) { try { await loadUsers(); return; // 成功则退出 } catch (e) { retries; if (retries maxRetries) { _state UserViewModelState(errorMessage: Failed after $maxRetries retries); notifyListeners(); } } }}### 3. 状态管理的高级用法对于复杂场景可以使用freezed库来生成不可变的状态类避免手动编写copyWith方法。## 总结MVVM Repository 架构为 Flutter 应用提供了一种清晰、可维护的工程组织方式。通过将 UI、业务逻辑和数据访问分离开发者可以更专注于各自领域的实现。Repository 模式进一步抽象了数据来源使得切换 API 或数据库变得简单。在实际项目中结合 Provider、Riverpod 或 Bloc 等状态管理工具可以充分发挥这一架构的优势。建议在项目初期就采用此架构以避免后续重构的麻烦。