ToB企服应用市场:ToB评测及商务社交产业平台

标题: 使用 Flutter 实现 MVVM 架构 [打印本页]

作者: 涛声依旧在    时间: 2024-6-13 14:14
标题: 使用 Flutter 实现 MVVM 架构
在本文中,我们将探讨如安在 Flutter 应用中实现 MVVM(Model-View-ViewModel)架构。MVVM 架构有助于保持代码整齐、可维护,同时进步开辟服从。我们将通过高维度的架构计划和具体的代码案例来介绍如安在 Flutter 中实现 MVVM。
一、什么是 MVVM 架构?

MVVM(Model-View-ViewModel)是一种软件架构计划模式,用于将业务逻辑、界面表现和用户交互分离。MVVM 架构包罗以下三个重要组件:

通过将这三个组件分离,我们可以更轻易地维护和扩展代码,同时保持代码整齐。
二、MVVM 架构计划

要在 Flutter 中实现 MVVM 架构,我们需要创建 Model、View 和 ViewModel 类,并使用 Flutter 的数据绑定机制将它们连接起来。这里是一个高维度的架构计划:
三、代码案例

接下来,我们将通过一个简单的计数器应用来演示如安在 Flutter 中实现 MVVM 架构。
3.1 Model

首先,我们创建一个简单的 Counter 类作为 Model,用于存储计数器的值并举行增长操作。
  1. class Counter {
  2.   int _value = 0;
  3.   int get value => _value;
  4.   void increment() {
  5.     _value++;
  6.   }
  7. }
复制代码
3.2 ViewModel

接下来,我们创建一个继承 ChangeNotifier 的 CounterViewModel 类。这个类将处理 View 的输入,并更新 Model。同时,通过调用 notifyListeners() 关照 View 更新。
  1. import 'package:flutter/foundation.dart';
  2. import 'counter.dart';
  3. class CounterViewModel extends ChangeNotifier {
  4.   final Counter _counter = Counter();
  5.   int get value => _counter.value;
  6.   void increment() {
  7.     _counter.increment();
  8.     notifyListeners();
  9.   }
  10. }
复制代码
3.3 View

然后,我们创建一个 StatefulWidget,并在其 build 方法中构建 UI。将 ViewModel 作为 ChangeNotifierProvider 的参数,以便在子组件中访问 ViewModel。
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'counter_view_model.dart';
  4. void main() {
  5.   runApp(MyApp());
  6. }
  7. class MyApp extends StatelessWidget {
  8.   @override
  9.   Widget build(BuildContext context) {
  10.     return MaterialApp(
  11.       home: ChangeNotifierProvider(
  12.         create: (context) => CounterViewModel(),
  13.         child: CounterPage(),
  14.       ),
  15.     );
  16.   }
  17. }
  18. class CounterPage extends StatefulWidget {
  19.   @override
  20.   _CounterPageState createState() => _CounterPageState();
  21. }
  22. class _CounterPageState extends State<CounterPage> {
  23.   @override
  24.   Widget build(BuildContext context) {
  25.     return Scaffold(
  26.       appBar: AppBar(title: Text('MVVM Counter')),
  27.       body: Center(
  28.         child: Consumer<CounterViewModel>(
  29.           builder: (context, viewModel, child) {
  30.             return Text('Counter: ${viewModel.value}');
  31.           },
  32.         ),
  33.       ),
  34.       floatingActionButton: FloatingActionButton(
  35.         onPressed: () {
  36.           context.read<CounterViewModel>().increment();
  37.         },
  38.         child: Icon(Icons.add),
  39.       ),
  40.     );
  41.   }
  42. }
复制代码
在这个示例中,我们使用 ChangeNotifierProvider 将 ViewModel 提供给子组件,并使用 Consumer 监听 ViewModel 的变化。当 ViewModel 更新时,Consumer 会自动重修 UI,以显示最新的计数器值。另外,我们使用 context.read<CounterViewModel>().increment() 来调用 ViewModel 中的 increment 方法,以更新计数器。
四、扩展 MVVM 架构

虽然我们已经演示了一个简单的 MVVM 架构实现,但在实际项目中,你可能会遇到更复杂的场景。接下来,我们将探讨一些扩展 MVVM 架构的方法,以满意不同的需求。
4.1 引入服务层

在大型项目中,我们可能需要处理更复杂的业务逻辑,如网络请求、数据库操作等。为了保持 Model 的简便,我们可以引入服务层,将这些逻辑封装为独立的服务类。然后,在 ViewModel 中调用这些服务类来更新 Model。
例如,我们可以创建一个 CounterService 类,负责从远程服务器获取和更新计数器值。然后,在 CounterViewModel 中调用这个服务类,而不是直接操作 Model。
4.2 使用依赖注入

依赖注入是一种编程技术,用于将对象的依赖项(如服务类)动态地通报给它们。通过使用依赖注入,我们可以更灵活地管理和测试应用的各个组件。
在 Flutter 中,我们可以使用 provider 包提供的 ProxyProvider 或其他第三方库(如 get_it、injectable 等)来实现依赖注入。如许,我们可以将服务类和其他依赖项注入到 ViewModel 中,而不是在 ViewModel 内部创建它们。
4.3 状态管理

虽然 MVVM 架构提供了一种有效的状态管理方法,但在复杂的应用中,我们可能需要更强大的状态管理解决方案。在 Flutter 中,有许多状态管理库可供选择,如 provider、bloc、mobx 等。这些库可以与 MVVM 架构结合使用,以实现更高效的状态管理。
例如,我们可以使用 bloc 库来实现 ViewModel,将业务逻辑和状态管理进一步解耦。如许,我们可以在不修改 View 的情况下,更轻易地重构和测试 ViewModel。
4.4 遵循最佳实践

在实现 MVVM 架构时,我们应遵循一些最佳实践,以确保代码的可维护性和可扩展性。例如:

通过遵循这些最佳实践,我们可以确保 MVVM 架构在实际项目中发挥最大的作用。
五、实战案例:待服务项应用

为了更好地理解如安在实际项目中应用 MVVM 架构,我们将通过一个待服务项应用的实战案例来演示。这个应用将包罗以下功能:

5.1 Model

首先,我们创建一个简单的 TodoItem 类作为 Model,用于存储待服务项的信息。
  1. class TodoItem {
  2.   String title;
  3.   bool isDone;
  4.   TodoItem({required this.title, this.isDone = false});
  5. }
复制代码
5.2 服务层

接着,我们创建一个 TodoService 类,负责处理待服务项的业务逻辑,如添加、删除、更新等。
  1. class TodoService {
  2.   List<TodoItem> _todos = [];
  3.   List<TodoItem> get todos => _todos;
  4.   void addTodo(TodoItem todo) {
  5.     _todos.add(todo);
  6.   }
  7.   void removeTodo(TodoItem todo) {
  8.     _todos.remove(todo);
  9.   }
  10.   void toggleTodoStatus(TodoItem todo) {
  11.     todo.isDone = !todo.isDone;
  12.   }
  13. }
复制代码
5.3 ViewModel

然后,我们创建一个继承 ChangeNotifier 的 TodoViewModel 类。这个类将处理 View 的输入,并调用 TodoService 来更新 Model。同时,通过调用 notifyListeners() 关照 View 更新。
  1. import 'package:flutter/foundation.dart';
  2. import 'todo_item.dart';
  3. import 'todo_service.dart';
  4. class TodoViewModel extends ChangeNotifier {
  5.   final TodoService _todoService = TodoService();
  6.   List<TodoItem> get todos => _todoService.todos;
  7.   void addTodo(String title) {
  8.     _todoService.addTodo(TodoItem(title: title));
  9.     notifyListeners();
  10.   }
  11.   void removeTodo(TodoItem todo) {
  12.     _todoService.removeTodo(todo);
  13.     notifyListeners();
  14.   }
  15.   void toggleTodoStatus(TodoItem todo) {
  16.     _todoService.toggleTodoStatus(todo);
  17.     notifyListeners();
  18.   }
  19. }
复制代码
5.4 View

最后,我们创建 View 层,包括一个 TodoListPage 和一个 TodoItemWidget。TodoListPage 负责显示待服务项列表和处理用户输入,而 TodoItemWidget 负责渲染单个待服务项。
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import 'todo_view_model.dart';
  4. import 'todo_item.dart';
  5. void main() {
  6.   runApp(MyApp());
  7. }
  8. class MyApp extends StatelessWidget {
  9.   @override
  10.   Widget build(BuildContext context) {
  11.     return MaterialApp(
  12.       home: ChangeNotifierProvider(
  13.         create: (context) => TodoViewModel(),
  14.         child: TodoListPage(),
  15.       ),
  16.     );
  17.   }
  18. }
  19. class TodoListPage extends StatelessWidget {
  20.   @override
  21.   Widget build(BuildContext context) {
  22.     return Scaffold(
  23.       appBar: AppBar(title: Text('Todo List')),
  24.       body: Consumer<TodoViewModel>(
  25.         builder: (context, viewModel, child) {
  26.           return ListView.builder(
  27.             itemCount: viewModel.todos.length,
  28.             itemBuilder: (context, index) {
  29.               return TodoItemWidget(
  30.                 todo: viewModel.todos[index],
  31.                 onToggle: () {
  32.                   viewModel.toggleTodoStatus(viewModel.todos[index]);
  33.                 },
  34.                 onDelete: () {
  35.                   viewModel.removeTodo(viewModel.todos[index]);
  36.                 },
  37.               );
  38.             },
  39.           );
  40.         },
  41.       ),
  42.       floatingActionButton: FloatingActionButton(
  43.         onPressed: () async {
  44.           String? newTitle = await showDialog<String>(
  45.             context: context,
  46.             builder: (context) {
  47.               return AddTodoDialog();
  48.             },
  49.           );
  50.           if (newTitle != null && newTitle.isNotEmpty) {
  51.             context.read<TodoViewModel>().addTodo(newTitle);
  52.           }
  53.         },
  54.         child: Icon(Icons.add),
  55.       ),
  56.     );
  57.   }
  58. }
  59. class TodoItemWidget extends StatelessWidget {
  60.   final TodoItem todo;
  61.   final VoidCallback onToggle;
  62.   final VoidCallback onDelete;
  63.   TodoItemWidget({required this.todo, required this.onToggle, required this.onDelete});
  64.   @override
  65.   Widget build(BuildContext context) {
  66.     return ListTile(
  67.       title: Text(todo.title),
  68.       leading: Checkbox(
  69.         value: todo.isDone,
  70.         onChanged: (bool? value) {
  71.           onToggle();
  72.         },
  73.       ),
  74.       trailing: IconButton(
  75.         icon: Icon(Icons.delete),
  76.         onPressed: onDelete,
  77.       ),
  78.     );
  79.   }
  80. }
  81. class AddTodoDialog extends StatelessWidget {
  82.   final TextEditingController _controller = TextEditingController();
  83.   @override
  84.   Widget build(BuildContext context) {
  85.     return AlertDialog(
  86.       title: Text('Add Todo'),
  87.       content: TextField(
  88.         controller: _controller,
  89.         decoration: InputDecoration(hintText: 'Todo title'),
  90.       ),
  91.       actions: [
  92.         TextButton(
  93.           child: Text('Cancel'),
  94.           onPressed: () {
  95.             Navigator.of(context).pop();
  96.           },
  97.         ),
  98.         TextButton(
  99.           child: Text('Add'),
  100.           onPressed: () {
  101.             Navigator.of(context).pop(_controller.text);
  102.           },
  103.         ),
  104.       ],
  105.     );
  106.   }
  107. }
复制代码
在这个示例中,我们创建了一个待服务项应用,使用 MVVM 架构将业务逻辑、界面表现和用户交互分离。通过这种方式,我们可以更轻易地维护和扩展代码,同时保持代码整齐。
六、使用 Riverpod 库实现一个基于 MVVM 架构的计数器应用

在这个示例中,我们将使用 Riverpod 库实现一个基于 MVVM 架构的计数器应用。我们将遵循上文中描述的 MVVM 架构计划,并使用 Riverpod 更换 Provider 作为状态管理库。
首先,请确保已在 pubspec.yaml 文件中添加了 riverpod 和 flutter_riverpod 依赖:
  1. dependencies:
  2.   flutter:
  3.     sdk: flutter
  4.   flutter_riverpod: ^1.0.0
复制代码
接下来,我们将创建 Model、ViewModel 和 View 类,并使用 Riverpod 将它们连接起来。
6.1 Model

创建一个简单的 Counter 类作为 Model,用于存储计数器的值并举行增长操作。
  1. class Counter {
  2.   int _value = 0;
  3.   int get value => _value;
  4.   void increment() {
  5.     _value++;
  6.   }
  7. }
复制代码
6.2 ViewModel

创建一个继承 StateNotifier 的 CounterViewModel 类。这个类将处理 View 的输入,并更新 Model。同时,通过调用 state 属性关照 View 更新。
  1. import 'package:flutter_riverpod/flutter_riverpod.dart';
  2. import 'counter.dart';
  3. class CounterViewModel extends StateNotifier<Counter> {
  4.   CounterViewModel() : super(Counter());
  5.   int get value => state.value;
  6.   void increment() {
  7.     state = Counter().._value = state.value + 1;
  8.   }
  9. }
复制代码
6.3 View

创建一个 StatelessWidget,并在其 build 方法中构建 UI。将 ViewModel 作为 StateNotifierProvider 的参数,以便在子组件中访问 ViewModel。
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'counter_view_model.dart';
  4. void main() {
  5.   runApp(ProviderScope(child: MyApp()));
  6. }
  7. class MyApp extends StatelessWidget {
  8.   @override
  9.   Widget build(BuildContext context) {
  10.     return MaterialApp(
  11.       home: CounterPage(),
  12.     );
  13.   }
  14. }
  15. class CounterPage extends StatelessWidget {
  16.   @override
  17.   Widget build(BuildContext context) {
  18.     return Scaffold(
  19.       appBar: AppBar(title: Text('Riverpod MVVM Counter')),
  20.       body: Center(
  21.         child: Consumer(
  22.           builder: (context, watch, child) {
  23.             final viewModel = watch(counterViewModelProvider);
  24.             return Text('Counter: ${viewModel.value}');
  25.           },
  26.         ),
  27.       ),
  28.       floatingActionButton: FloatingActionButton(
  29.         onPressed: () {
  30.           context.read(counterViewModelProvider.notifier).increment();
  31.         },
  32.         child: Icon(Icons.add),
  33.       ),
  34.     );
  35.   }
  36. }
复制代码
在这个示例中,我们使用 StateNotifierProvider 将 ViewModel 提供给子组件,并使用 Consumer 监听 ViewModel 的变化。当 ViewModel 更新时,Consumer 会自动重修 UI,以显示最新的计数器值。另外,我们使用 context.read(counterViewModelProvider.notifier).increment() 来调用 ViewModel 中的 increment 方法,以更新计数器。
最后,别忘了在 ViewModel 文件中定义 Riverpod provider:
  1. import 'package:flutter_riverpod/flutter_riverpod.dart';
  2. final counterViewModelProvider = StateNotifierProvider<CounterViewModel, Counter>((ref) {
  3.   return CounterViewModel();
  4. });
复制代码
6.4 Riverpod 和 Provider 的区别

Riverpod 是一个用于状态管理的 Flutter 库,它提供了一种声明式、灵活且安全的方式来管理和访问应用步伐的状态。在我们的示例中,我们使用 Riverpod 更换了前文案例中的 Provider 库,以实现 MVVM 架构。
以下是 Riverpod 在示例中的作用以及与前文案例的区别:
  1. final counterViewModelProvider = StateNotifierProvider<CounterViewModel, Counter>((ref) {
  2.   return CounterViewModel();
  3. });
复制代码
  1. class CounterViewModel extends StateNotifier<Counter> {
  2.   // ...
  3. }
复制代码
  1. child: Consumer(
  2.   builder: (context, watch, child) {
  3.     final viewModel = watch(counterViewModelProvider);
  4.     return Text('Counter: ${viewModel.value}');
  5.   },
  6. ),
复制代码
  1. onPressed: () {
  2.   context.read(counterViewModelProvider.notifier).increment();
  3. },
复制代码
总之,Riverpod 在示例中的作用重要是提供状态管理和数据绑定。与前文案例相比,Riverpod 提供了更简便的 API、更灵活的状态管理机制以及更安全的访问方式。这些特性使得 Riverpod 成为实现 MVVM 架构的一个很好的选择。
七、结论

MVVM 架构是一种强大的计划模式,可以帮助我们构建可维护、可测试、可扩展的应用。在 Flutter 中,我们可以利用其强大的数据绑定和状态管理特性,轻松实现 MVVM 架构。
在本文中,我们介绍了 MVVM 架构的基本概念,展示了如安在 Flutter 中实现 MVVM 架构,并通过一个待服务项应用的实战案例,演示了如安在实际项目中应用 MVVM 架构。
希望这篇文章能帮助你理解和掌握 MVVM 架构,为你的 Flutter 项目提供有用的参考。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4