使用 Flutter 实现 MVVM 架构

文章目录

    • 什么是 MVVM 架构?
    • MVVM 架构设计
    • 代码案例
      • Model
      • ViewModel
      • View
    • 扩展 MVVM 架构
      • 1. 引入服务层
      • 2. 使用依赖注入
      • 3. 状态管理
      • 4. 遵循最佳实践
    • 实战案例:待办事项应用
      • Model
      • 服务层
      • ViewModel
      • View
    • 结论

在本文中,我们将探讨如何在 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 的数据绑定机制将它们连接起来。这里是一个高维度的架构设计:

  1. Model:创建一个包含业务逻辑的 Model 类。这个类可以包含数据模型、网络请求、数据库操作等。
  2. ViewModel:创建一个继承 ChangeNotifier 的 ViewModel 类。这个类将处理 View 的输入,并更新 Model。同时,通过调用 notifyListeners() 通知 View 更新。
  3. View:创建一个 StatefulWidget,并在其 build 方法中构建 UI。将 ViewModel 作为 ChangeNotifierProvider 的参数,以便在子组件中访问 ViewModel。
  4. 数据绑定:使用 Consumercontext.watch() 监听 ViewModel 的变化,并在 View 中更新 UI。

代码案例

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

Model

首先,我们创建一个简单的 Counter 类作为 Model,用于存储计数器的值并进行增加操作。

class Counter {int _value = 0;int get value => _value;void increment() {_value++;}
}

ViewModel

接下来,我们创建一个继承 ChangeNotifierCounterViewModel 类。这个类将处理 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();}
}

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 {Widget build(BuildContext context) {return MaterialApp(home: ChangeNotifierProvider(create: (context) => CounterViewModel(),child: CounterPage(),),);}
}class CounterPage extends StatefulWidget {_CounterPageState createState() => _CounterPageState();
}class _CounterPageState extends State<CounterPage> {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 架构的方法,以满足不同的需求。

1. 引入服务层

在大型项目中,我们可能需要处理更复杂的业务逻辑,如网络请求、数据库操作等。为了保持 Model 的简洁,我们可以引入服务层,将这些逻辑封装为独立的服务类。然后,在 ViewModel 中调用这些服务类来更新 Model。

例如,我们可以创建一个 CounterService 类,负责从远程服务器获取和更新计数器值。然后,在 CounterViewModel 中调用这个服务类,而不是直接操作 Model。

2. 使用依赖注入

依赖注入是一种编程技术,用于将对象的依赖项(如服务类)动态地传递给它们。通过使用依赖注入,我们可以更灵活地管理和测试应用的各个组件。

在 Flutter 中,我们可以使用 provider 包提供的 ProxyProvider 或其他第三方库(如 get_itinjectable 等)来实现依赖注入。这样,我们可以将服务类和其他依赖项注入到 ViewModel 中,而不是在 ViewModel 内部创建它们。

3. 状态管理

虽然 MVVM 架构提供了一种有效的状态管理方法,但在复杂的应用中,我们可能需要更强大的状态管理解决方案。在 Flutter 中,有许多状态管理库可供选择,如 providerblocmobx 等。这些库可以与 MVVM 架构结合使用,以实现更高效的状态管理。

例如,我们可以使用 bloc 库来实现 ViewModel,将业务逻辑和状态管理进一步解耦。这样,我们可以在不修改 View 的情况下,更容易地重构和测试 ViewModel。

4. 遵循最佳实践

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

  • 保持 Model、View 和 ViewModel 的职责单一,避免将过多的逻辑放入一个类中。
  • 使用接口和抽象类来定义 Model 和 ViewModel,以便在不同的实现和平台之间共享代码。
  • 编写单元测试和集成测试,确保应用的正确性和稳定性。
  • 优化性能,避免不必要的重绘和重建。

通过遵循这些最佳实践,我们可以确保 MVVM 架构在实际项目中发挥最大的作用。

实战案例:待办事项应用

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

  • 显示待办事项列表
  • 添加新的待办事项
  • 标记待办事项为完成或未完成
  • 删除待办事项

Model

首先,我们创建一个简单的 TodoItem 类作为 Model,用于存储待办事项的信息。

class TodoItem {String title;bool isDone;TodoItem({required this.title, this.isDone = false});
}

服务层

接着,我们创建一个 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;}
}

ViewModel

然后,我们创建一个继承 ChangeNotifierTodoViewModel 类。这个类将处理 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();}
}

View

最后,我们创建 View 层,包括一个 TodoListPage 和一个 TodoItemWidgetTodoListPage 负责显示待办事项列表和处理用户输入,而 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 {Widget build(BuildContext context) {return MaterialApp(home: ChangeNotifierProvider(create: (context) => TodoViewModel(),child: TodoListPage(),),);}
}class TodoListPage extends StatelessWidget {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[index],onToggle: () {viewModel.toggleTodoStatus(viewModel.todos[index]);},onDelete: () {viewModel.removeTodo(viewModel.todos[index]);},);},);},),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});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();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 架构将业务逻辑、界面表示和用户交互分离。通过这种方式,我们可以更容易地维护和扩展代码,同时保持代码整洁。

结论

MVVM 架构是一种强大的设计模式,可以帮助我们构建可维护、可测试、可扩展的应用。在 Flutter 中,我们可以利用其强大的数据绑定和状态管理特性,轻松实现 MVVM 架构。

在本文中,我们介绍了 MVVM 架构的基本概念,展示了如何在 Flutter 中实现 MVVM 架构,并通过一个待办事项应用的实战案例,演示了如何在实际项目中应用 MVVM 架构。

希望这篇文章能帮助你理解和掌握 MVVM 架构,为你的 Flutter 项目提供有用的参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/11812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程和计划任务管理

查看系统进程信息 静态查看系统进程信息之ps命令 &#xff08;每五秒刷新一次数据&#xff09; 方法一&#xff1a;ps aux //显示所有进程 a&#xff1a;显示现行终端下的所有进程&#xff0c;包括其它用户的进程。u&#xff1a;显示进程的归属用户及内存的使用情况。x…

【qt】动态属性

这里写目录标题 一.属性1.属性的好处2.添加属性3.使用属性 二.只读属性 一.属性 1.属性的好处 说到属性&#xff08;property&#xff09;&#xff0c;你们会想到什么&#xff1f;我会联想到特点&#xff0c;就是一类对象所特有的&#xff0c;在C中&#xff0c;成员数据就是这…

2.3 Spark运行架构与原理

Spark运行架构由SparkContext、Cluster Manager和Worker构成。在集群模式下&#xff0c;Driver进程初始化SparkContext并向Cluster Manager申请资源&#xff0c;后者根据算法在Worker节点上启动Executor。Executor负责任务执行&#xff0c;反馈状态给Cluster Manager。任务由Ta…

MyBatis-Plus核心功能详解:条件构造器、自定义SQL与Service接口

在Java的Web开发中&#xff0c;MyBatis-Plus作为MyBatis的增强工具&#xff0c;提供了许多实用的功能&#xff0c;极大地简化了数据库操作的开发过程。下面&#xff0c;我们将详细探讨MyBatis-Plus的三大核心功能&#xff1a;条件构造器、自定义SQL以及Service接口。 一、条件…

FBX Python SDK 入门教程

安装 1. 下载 FBX Python SDK 官网地址&#xff0c;根据你的操作系统&#xff0c;下载相应的 FBX SDK 2020.3.4 Python exe 文件。 2. 安装 执行下载的文件 fbx202034_fbxpythonsdk_win.exe&#xff0c;安装完的程序路径我的是 D:\Program Files\Autodesk\FBX\FBX Python SDK\…

构建滴滴业务中台:系统架构设计探索

在当今数字化时代&#xff0c;滴滴作为中国领先的出行平台&#xff0c;承载着数亿用户的出行需求&#xff0c;业务规模庞大且复杂多样。为了更好地支撑业务发展和提升服务质量&#xff0c;滴滴不断探索和构建业务中台&#xff0c;以实现业务的快速响应、灵活运营和持续创新。在…

【vivado】debug相关时钟及其约束关系

一、前言 在xilinx fpga的degug过程中&#xff0c;经常出现由于时钟不对而导致的观测波形失败&#xff0c;要想能够解决这些问题需要了解其debug的组成环境以及之间的数据流。本文主要介绍debug过程中需要的时钟及各时钟之间的关系。 二、debug相关时钟 Vivado 硬件管理器使…

【NodeMCU实时天气时钟温湿度项目 6】解析天气信息JSON数据并显示在 TFT 屏幕上(心知天气版)

今天是第六专题&#xff0c;主要内容是&#xff1a;导入ArduinoJson功能库&#xff0c;借助该库解析从【心知天气】官网返回的JSON数据&#xff0c;并显示在 TFT 屏幕上。 如您需要了解其它专题的内容&#xff0c;请点击下面的链接。 第一专题内容&#xff0c;请参考&a…

Spring MVC 拦截器编程开以及常见问题

写在前面&#xff1a;最近在对一个微服务增减接口拦截器的时候&#xff0c;定义的拦截器未能生效&#xff0c;并且未报任何异常&#xff0c;swagger的ui也无法正常访问。经查明原因&#xff1a;prehandler方法最后返回的布尔值是false&#xff0c;拦截器定义部分是从其他服务拷…

商城小程序系统与C#.net商城小程序系统源码_OctShop

在移动互联网与电商的时代&#xff0c;商城小程序系统已经成为了众多企业和商家开展电商业务的重要工具。OctShop将以商城小程序系统、C#商城小程序系统源码和.net 商城小程序系统为主题&#xff0c;探讨这些系统的特点和优势。 一、商城小程序系统是什么 商城小程序系统是一种…

Python实战开发及案例分析(20)—— 宽度优先

宽度优先搜索&#xff08;Breadth-First Search, BFS&#xff09;是一种遍历图或树的算法。它从根节点开始&#xff0c;探索所有邻近节点&#xff0c;然后再按顺序访问每个邻近节点的邻居&#xff0c;直到所有节点都被访问为止。在图中&#xff0c;为了避免访问同一个节点多次&…

建筑乙级资质延期中的政策变化与应对策略

在建筑乙级资质延期过程中&#xff0c;政策的变化可能会对企业的运营和策略产生重大影响。为了应对这些变化&#xff0c;企业需要密切关注政策动态&#xff0c;灵活调整自身的经营策略。以下是一些可能的政策变化及相应的应对策略&#xff1a; 一、政策变化 资质延期条件调整&a…

福派斯鲜肉狗粮大揭秘,狗狗吃了都说好!

狗粮哪个牌子好&#xff1f;我强烈推荐福派斯鲜肉狗粮&#xff01;在众多狗粮品牌中&#xff0c;福派斯以其高品质、营养均衡以及独特的产品特点&#xff0c;受到了广大宠物主人的一致好评。 (1) 福派斯鲜肉狗粮的配方表透露出其对于原料选择的严谨和用心。高达75%的鲜肉含量&a…

安卓手机APP开发__支持不同的像素深度

安卓手机APP开发__支持不同的像素深度 目录 概述 使用对深度独立的像素 把DP单位转换成像素单位 概述 安卓设备不仅有不同的大小,还有不同的像素深度.一个设备可能在每一英寸上有 160个像素,另一个设备在相同的尺度上有480个像素.如果你不考虑这些不同, 系统可能会缩放你的…

linux不小心将/etc/passwd用户文件清空或删除解决方法

大概思路&#xff1a;进入单用户模式将passwd-引子程序复制为删除的passwd用户文件&#xff0c;关闭selinux 此系统为&#xff1a;centos 7 1.在GRUB引导的时候按e进入编辑模式&#xff0c;linux16那一行的ro 修改为rw rd.break ‘ ’ 2.ctrlx执行 3.进入单用户模式后修改根…

springboot学习整理

视频&#xff1a;基础篇-01_springboot概述_哔哩哔哩_bilibili 介绍 spring boot 是spring提供的一个子项目&#xff0c;用于快速构建spring应用程序 spring构建&#xff1a; 1 导入依赖繁琐 &#xff1b; 2 项目配置繁琐 spring Framework: 核心 spring Boot :快速构建spring…

AI学习指南概率论篇-信息论

AI学习指南概率论篇-信息论 信息论是信息科学中的一个重要分支&#xff0c;主要研究信息的量、质和流通规律等问题。在人工智能领域中&#xff0c;信息论被广泛应用于数据压缩、数据传输和模型优化等方面。本文将从信息论的概述、AI中的使用场景、定义和意义以及相关公式讲解这…

win11右键菜单恢复win10风格

有很多朋友升级到win11系统之后不是特别喜欢右键菜单&#xff0c;因为经常需要多点击一次显示更多选项&#xff0c;很不舒服。大家就想知道如何修改回原来win10的右键菜单&#xff0c;今天给大家介绍Win11右键菜单怎么修改为以前的样子&#xff0c;仅需执行2条明路&#xff0c;…

秒杀系统之设计方向

1 秒杀存在的问题 对于一个日常平稳的业务系统&#xff0c;如果直接开通秒杀功能的话&#xff0c;往往会出现很多问题—— 2 设计方向的思考 秒杀本质是要求一个瞬时高发下的承压系统&#xff0c;这也是其区别于其他业务的核心场景。对日常系统秒杀产生的问题逐一进行拆解分…

PostgreSQL自带的命令行工具24- postgres

PostgreSQL自带的命令行工具24- postgres postgres 命令主要是 PostgreSQL 数据库服务器的主要执行文件。当你启动 PostgreSQL 服务时&#xff0c;实际上就是在后台运行 postgres 程序。这个程序负责处理用户请求、执行 SQL 命令、管理数据库文件等核心数据库管理任务。通常&a…