涛声依旧在 发表于 2024-6-13 14:14:22

使用 Flutter 实现 MVVM 架构

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

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


[*]Model:负责处理数据和业务逻辑。
[*]View:负责显示 UI,并将用户操作通报给 ViewModel。
[*]ViewModel:负责处理 View 的输入,并更新 Model,同时关照 View 更新。
通过将这三个组件分离,我们可以更轻易地维护和扩展代码,同时保持代码整齐。
二、MVVM 架构计划

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

[*]Model:创建一个包罗业务逻辑的 Model 类。这个类可以包罗数据模型、网络请求、数据库操作等。
[*]ViewModel:创建一个继承 ChangeNotifier 的 ViewModel 类。这个类将处理 View 的输入,并更新 Model。同时,通过调用 notifyListeners() 关照 View 更新。
[*]View:创建一个 StatefulWidget,并在其 build 方法中构建 UI。将 ViewModel 作为 ChangeNotifierProvider 的参数,以便在子组件中访问 ViewModel。
[*]数据绑定:使用 Consumer 或 context.watch() 监听 ViewModel 的变化,并在 View 中更新 UI。
三、代码案例

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

首先,我们创建一个简单的 Counter 类作为 Model,用于存储计数器的值并举行增长操作。
class Counter {
int _value = 0;

int get value => _value;

void increment() {
    _value++;
}
}
3.2 ViewModel

接下来,我们创建一个继承 ChangeNotifier 的 CounterViewModel 类。这个类将处理 View 的输入,并更新 Model。同时,通过调用 notifyListeners() 关照 View 更新。
import 'package:flutter/foundation.dart';
import 'counter.dart';

class CounterViewModel extends ChangeNotifier {
final Counter _counter = Counter();

int get value => _counter.value;

void increment() {
    _counter.increment();
    notifyListeners();
}
}
3.3 View

然后,我们创建一个 StatefulWidget,并在其 build 方法中构建 UI。将 ViewModel 作为 ChangeNotifierProvider 的参数,以便在子组件中访问 ViewModel。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_view_model.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
    return MaterialApp(
      home: ChangeNotifierProvider(
      create: (context) => CounterViewModel(),
      child: CounterPage(),
      ),
    );
}
}

class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
@override
Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('MVVM Counter')),
      body: Center(
      child: Consumer<CounterViewModel>(
          builder: (context, viewModel, child) {
            return Text('Counter: ${viewModel.value}');
          },
      ),
      ),
      floatingActionButton: FloatingActionButton(
      onPressed: () {
          context.read<CounterViewModel>().increment();
      },
      child: Icon(Icons.add),
      ),
    );
}
}
在这个示例中,我们使用 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 架构时,我们应遵循一些最佳实践,以确保代码的可维护性和可扩展性。例如:


[*]保持 Model、View 和 ViewModel 的职责单一,避免将过多的逻辑放入一个类中。
[*]使用接口和抽象类来定义 Model 和 ViewModel,以便在不同的实现和平台之间共享代码。
[*]编写单元测试和集成测试,确保应用的准确性和稳定性。
[*]优化性能,避免不须要的重绘和重修。
通过遵循这些最佳实践,我们可以确保 MVVM 架构在实际项目中发挥最大的作用。
五、实战案例:待服务项应用

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


[*]显示待服务项列表
[*]添加新的待服务项
[*]标记待服务项为完成或未完成
[*]删除待服务项
5.1 Model

首先,我们创建一个简单的 TodoItem 类作为 Model,用于存储待服务项的信息。
class TodoItem {
String title;
bool isDone;

TodoItem({required this.title, this.isDone = false});
}
5.2 服务层

接着,我们创建一个 TodoService 类,负责处理待服务项的业务逻辑,如添加、删除、更新等。
class TodoService {
List<TodoItem> _todos = [];

List<TodoItem> get todos => _todos;

void addTodo(TodoItem todo) {
    _todos.add(todo);
}

void removeTodo(TodoItem todo) {
    _todos.remove(todo);
}

void toggleTodoStatus(TodoItem todo) {
    todo.isDone = !todo.isDone;
}
}
5.3 ViewModel

然后,我们创建一个继承 ChangeNotifier 的 TodoViewModel 类。这个类将处理 View 的输入,并调用 TodoService 来更新 Model。同时,通过调用 notifyListeners() 关照 View 更新。
import 'package:flutter/foundation.dart';
import 'todo_item.dart';
import 'todo_service.dart';

class TodoViewModel extends ChangeNotifier {
final TodoService _todoService = TodoService();

List<TodoItem> get todos => _todoService.todos;

void addTodo(String title) {
    _todoService.addTodo(TodoItem(title: title));
    notifyListeners();
}

void removeTodo(TodoItem todo) {
    _todoService.removeTodo(todo);
    notifyListeners();
}

void toggleTodoStatus(TodoItem todo) {
    _todoService.toggleTodoStatus(todo);
    notifyListeners();
}
}
5.4 View

最后,我们创建 View 层,包括一个 TodoListPage 和一个 TodoItemWidget。TodoListPage 负责显示待服务项列表和处理用户输入,而 TodoItemWidget 负责渲染单个待服务项。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'todo_view_model.dart';
import 'todo_item.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
    return MaterialApp(
      home: ChangeNotifierProvider(
      create: (context) => TodoViewModel(),
      child: TodoListPage(),
      ),
    );
}
}

class TodoListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Todo List')),
      body: Consumer<TodoViewModel>(
      builder: (context, viewModel, child) {
          return ListView.builder(
            itemCount: viewModel.todos.length,
            itemBuilder: (context, index) {
            return TodoItemWidget(
                todo: viewModel.todos,
                onToggle: () {
                  viewModel.toggleTodoStatus(viewModel.todos);
                },
                onDelete: () {
                  viewModel.removeTodo(viewModel.todos);
                },
            );
            },
          );
      },
      ),
      floatingActionButton: FloatingActionButton(
      onPressed: () async {
          String? newTitle = await showDialog<String>(
            context: context,
            builder: (context) {
            return AddTodoDialog();
            },
          );
          if (newTitle != null && newTitle.isNotEmpty) {
            context.read<TodoViewModel>().addTodo(newTitle);
          }
      },
      child: Icon(Icons.add),
      ),
    );
}
}

class TodoItemWidget extends StatelessWidget {
final TodoItem todo;
final VoidCallback onToggle;
final VoidCallback onDelete;

TodoItemWidget({required this.todo, required this.onToggle, required this.onDelete});

@override
Widget build(BuildContext context) {
    return ListTile(
      title: Text(todo.title),
      leading: Checkbox(
      value: todo.isDone,
      onChanged: (bool? value) {
          onToggle();
      },
      ),
      trailing: IconButton(
      icon: Icon(Icons.delete),
      onPressed: onDelete,
      ),
    );
}
}

class AddTodoDialog extends StatelessWidget {
final TextEditingController _controller = TextEditingController();

@override
Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Add Todo'),
      content: TextField(
      controller: _controller,
      decoration: InputDecoration(hintText: 'Todo title'),
      ),
      actions: [
      TextButton(
          child: Text('Cancel'),
          onPressed: () {
            Navigator.of(context).pop();
          },
      ),
      TextButton(
          child: Text('Add'),
          onPressed: () {
            Navigator.of(context).pop(_controller.text);
          },
      ),
      ],
    );
}
}
在这个示例中,我们创建了一个待服务项应用,使用 MVVM 架构将业务逻辑、界面表现和用户交互分离。通过这种方式,我们可以更轻易地维护和扩展代码,同时保持代码整齐。
六、使用 Riverpod 库实现一个基于 MVVM 架构的计数器应用

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

创建一个简单的 Counter 类作为 Model,用于存储计数器的值并举行增长操作。
class Counter {
int _value = 0;

int get value => _value;

void increment() {
    _value++;
}
}
6.2 ViewModel

创建一个继承 StateNotifier 的 CounterViewModel 类。这个类将处理 View 的输入,并更新 Model。同时,通过调用 state 属性关照 View 更新。
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'counter.dart';

class CounterViewModel extends StateNotifier<Counter> {
CounterViewModel() : super(Counter());

int get value => state.value;

void increment() {
    state = Counter().._value = state.value + 1;
}
}
6.3 View

创建一个 StatelessWidget,并在其 build 方法中构建 UI。将 ViewModel 作为 StateNotifierProvider 的参数,以便在子组件中访问 ViewModel。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'counter_view_model.dart';

void main() {
runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
}
}

class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Riverpod MVVM Counter')),
      body: Center(
      child: Consumer(
          builder: (context, watch, child) {
            final viewModel = watch(counterViewModelProvider);
            return Text('Counter: ${viewModel.value}');
          },
      ),
      ),
      floatingActionButton: FloatingActionButton(
      onPressed: () {
          context.read(counterViewModelProvider.notifier).increment();
      },
      child: Icon(Icons.add),
      ),
    );
}
}
在这个示例中,我们使用 StateNotifierProvider 将 ViewModel 提供给子组件,并使用 Consumer 监听 ViewModel 的变化。当 ViewModel 更新时,Consumer 会自动重修 UI,以显示最新的计数器值。另外,我们使用 context.read(counterViewModelProvider.notifier).increment() 来调用 ViewModel 中的 increment 方法,以更新计数器。
最后,别忘了在 ViewModel 文件中定义 Riverpod provider:
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterViewModelProvider = StateNotifierProvider<CounterViewModel, Counter>((ref) {
return CounterViewModel();
});
6.4 Riverpod 和 Provider 的区别

Riverpod 是一个用于状态管理的 Flutter 库,它提供了一种声明式、灵活且安全的方式来管理和访问应用步伐的状态。在我们的示例中,我们使用 Riverpod 更换了前文案例中的 Provider 库,以实现 MVVM 架构。
以下是 Riverpod 在示例中的作用以及与前文案例的区别:

[*]声明式 Provider:在 Riverpod 中,我们使用声明式的方式来定义 Provider。这意味着我们可以在全局范围内定义 Provider,而不需要将其放入 Widget 树中。这使得代码更易于阅读和维护。与前文案例相比,我们不再使用 ChangeNotifierProvider,而是使用 StateNotifierProvider 来创建 ViewModel 的实例。
final counterViewModelProvider = StateNotifierProvider<CounterViewModel, Counter>((ref) {
return CounterViewModel();
});

[*]StateNotifier:在 Riverpod 示例中,我们使用 StateNotifier 更换了 ChangeNotifier。StateNotifier 是 Riverpod 库的一部分,它提供了一种简单的状态管理机制。与 ChangeNotifier 相比,StateNotifier 具有更简便的 API,而且避免了一些潜在的性能问题。在 ViewModel 中,我们继承 StateNotifier 并使用 state 属性来关照 View 更新。
class CounterViewModel extends StateNotifier<Counter> {
// ...
}

[*]Consumer 和 watch:在 Riverpod 示例中,我们使用 Consumer 和 watch 函数来监听 ViewModel 的变化。Consumer 和 watch 函数是 Riverpod 库提供的数据绑定机制,它们可以自动重修 UI 以显示最新的状态。与前文案例中的 context.watch() 相比,Riverpod 的 watch 函数提供了更简便的语法。
child: Consumer(
builder: (context, watch, child) {
    final viewModel = watch(counterViewModelProvider);
    return Text('Counter: ${viewModel.value}');
},
),

[*]访问 ViewModel:在 Riverpod 示例中,我们使用 context.read() 函数来访问 ViewModel 并调用其方法。与前文案例中的 context.read() 相比,Riverpod 的 context.read() 函数提供了更简便的语法。此外,我们需要使用 notifier 属性来访问 StateNotifier 的实例。
onPressed: () {
context.read(counterViewModelProvider.notifier).increment();
},
总之,Riverpod 在示例中的作用重要是提供状态管理和数据绑定。与前文案例相比,Riverpod 提供了更简便的 API、更灵活的状态管理机制以及更安全的访问方式。这些特性使得 Riverpod 成为实现 MVVM 架构的一个很好的选择。
七、结论

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 使用 Flutter 实现 MVVM 架构