Flutter笔记:聊一聊依赖注入(上)

Flutter笔记
聊一聊依赖注入(上)

作者李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134007247


【介绍】:依赖注入(Dependency Injection,DI)是一种有力的工具,可以提高代码质量,使应用程序更易于扩展和维护。通过正确实施依赖注入,可以编写更清晰、更可测试和更可维护的代码,以满足现代软件开发的需求。本文基于 Flutter 框架介绍依赖注入相关内容。

下一节:《 聊一聊依赖注入(下)


1. 概述

1.1 什么是依赖注入

依赖注入并不是 Dart语言或者 Flutter框架 中独有的,它是一种重要的 编程模式,旨在更好地组织和管理类之间的依赖关系,以提高代码的可维护性、可测试性和可扩展性。这个模式在许多编程语言中得到了广泛应用,其中包括 DartJavaC#JavaScript 等不同语言的各种项目中。

核心思想是 通过外部注入依赖项,而不是在类内部创建或查找依赖项。这种做法有多重好处:

  1. 解耦合: 依赖注入通过将依赖项传递给类,降低了各个类之间的耦合。每个类只需要知道如何使用其依赖项,而不需要知道如何创建或管理它们。这使得代码更容易维护和修改,因为对一个类的更改不会轻易影响其他类。

  2. 可测试性: 依赖注入使得编写单元测试变得更容易。您可以轻松地注入模拟或替代依赖项,以测试类的行为而不依赖于真实的依赖项。这有助于识别和修复问题,同时确保代码的稳定性。

  3. 可重用性: 通过注入依赖项,这些依赖项变得更容易重用。您可以创建通用的依赖项,然后在多个类中重复使用它们,而无需为每个类创建新的实例。

  4. 可扩展性: 依赖注入允许您在不更改现有类的情况下轻松添加新的功能或依赖项。这种灵活性对于扩展应用程序非常重要。

  5. 可维护性: 依赖注入提供了更清晰的代码结构,因为它明确显示了一个类所依赖的内容。这有助于其他开发人员理解代码并快速定位和解决问题。

1.2 依赖注入实现方法

依赖注入可以通过以下几种方式实现:

  • 构造函数注入:依赖通过构造函数传递给类。
  • 方法注入:依赖通过方法调用传递给类。
  • 属性注入:依赖通过类的属性设置传递给类。
  • 服务定位:通过一个全局的服务定位器(如Provider等)来获取依赖项。

一般情况下,构造函数注入是最常用的方式,因为它在类创建时就清晰地显示出类所需的依赖项,使代码易于理解和维护。

1.3 依赖注入在Flutter中的应用

在Flutter中,依赖注入是一种常见的模式,用于管理和组织代码中的依赖关系。Flutter并没有内置的依赖注入系统,但是有许多第三方库可以帮助我们实现依赖注入,如 ProviderGet ItRiverpod 等。

这些库都提供了一种方式,使我们可以在应用的任何地方访问我们的依赖项,而无需手动传递它们。这使得我们的代码更加清晰,更易于维护和测试。

例如,我们可以使用Provider库来创建一个可以在应用的任何地方访问的共享状态。这个状态可以是任何类型的对象,如数据库连接、网络服务等。我们只需要在应用的顶层提供这个状态,然后在任何需要的地方,我们都可以通过 Provider.of 方法来获取这个状态。

在接下来的章节中,我们将详细介绍如何使用这些库进行依赖注入,并提供一些最佳实践和常见问题的解决方案。

2. Provider 库与依赖注入(简介)

Provider是Flutter社区中广泛使用的一个状态管理库,它也提供了一种简单而强大的方式来实现依赖注入,它使我们的代码更加清晰,更易于维护和测试。

Provider的主要思想是 将数据模型或服务封装在 提供者(provider) 中,然后在需要的地方通过 context 获取。例如:

// 定义一个需要共享的数据模型
class Counter with ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}// 在应用的顶层提供这个数据模型
void main() {runApp(ChangeNotifierProvider(create: (context) => Counter(),child: MyApp(),),);
}// 在需要的地方获取这个数据模型
class MyWidget extends StatelessWidget {Widget build(BuildContext context) {var counter = Provider.of<Counter>(context);return Text('${counter.count}');}
}

我们首先定义了一个 Counter 类,它有一个 _count 字段和一个 increment 方法。然后我们在应用的顶层使用 ChangeNotifierProvider 来提供这个 Counter 对象。在需要的地方,我们可以通过 Provider.of(context) 来获取这个 Counter 对象。

这样,我们就可以在应用的任何地方访问和修改 Counter 对象,而无需手动传递它。这就是 Provider 的依赖注入功能。

在实际开发中,我们可能会有多个不同类型的数据模型或服务需要共享。在这种情况下,我们可以使用 MultiProvider 来提供多个对象。

例如,我们可能有一个用于管理用户信息的 UserModel,一个用于管理购物车信息的 CartModel,以及一个用于处理 API 调用的 ApiService 等。在这种情况下,我们可以使用 MultiProvider 来同时提供这些对象。

请看下面的代码:

void main() {runApp(MultiProvider(providers: [ChangeNotifierProvider(create: (context) => UserModel()),ChangeNotifierProvider(create: (context) => CartModel()),Provider(create: (context) => ApiService()),],child: MyApp(),),);
}

我们使用 MultiProvider 同时提供了 UserModelCartModelApiService。这样,我们就可以在应用的任何地方通过 Provider.of(context) 来获取这些对象,其中T是对象的类型。

如果我们需要在一个 Widget 中访问 UserModel,我们可以这样做:

class MyWidget extends StatelessWidget {Widget build(BuildContext context) {var userModel = Provider.of<UserModel>(context);// 使用userModel}
}

这样,我们就可以在应用的任何地方访问和修改 UserModelCartModelApiService,而无需手动传递它们。这就是 MultiProvider 的依赖注入功能。

3. Riverpod 库与依赖注入(简介)

Riverpod 是一个由 Flutter 社区成员开发的新的状态管理库,它也提供了依赖注入的功能。与 Provider 不同,Riverpod 不依赖于 BuildContext,这使得它更加灵活和强大。

例如:

// 定义一个需要共享的数据模型
final counterProvider = StateProvider<int>((ref) => 0);// 在需要的地方获取这个数据模型
class MyWidget extends ConsumerWidget {Widget build(BuildContext context, ScopedReader watch) {final counter = watch(counterProvider).state;return Text('$counter');}
}// 在需要的地方修改这个数据模型
class IncrementButton extends ConsumerWidget {Widget build(BuildContext context, ScopedReader watch) {return FloatingActionButton(onPressed: () => context.read(counterProvider).state++,child: Icon(Icons.add),);}
}

在上面的代码中,先定义了一个 counterProvider,它是一个 StateProvider。然后我们在需要的地方使用 watch 函数来获取这个 counterProvider 的状态。我们也可以使用 read 函数来修改这个 counterProvider**的状态。

这样,我们就可以在应用的任何地方访问和修改 counterProvider的状态,而无需手动传递它。这就是 Riverpod 的依赖注入功能。

在实际开发中,我们可能会有多个不同类型的数据模型或服务需要共享。在这种情况下,我们可以定义多个 Provider,并在需要的地方使用 watch 或 read 函数来获取或修改它们。

4. GetX库 与依赖注入(简介)

GetX 是一个功能强大的 Flutter库,它提供了状态管理、路由管理 等众多功能,其中也提供了对于 依赖注入的支持。

GetX的依赖注入系统非常简单易用,它允许我们在应用的任何地方访问我们的依赖项。

比如,我们定义一个Get服务,它继承自 GetxService

class MyService extends GetxService {Future<MyService> init() async {// Perform async initializationreturn this;}
}

在应用启动时,我们使用 Get.putAsync() 方法来提供这个服务:

void main() async {await Get.putAsync<MyService>(() async => await MyService().init());runApp(MyApp());
}

4.1 从一个例子说起

GetX 中,我们可以使用 Get.put() 方法来提供一个依赖项。

例如,我们定义了一个 AuthService 类,它有一个 _user 字段来存储当前用户,以及 login() 和 logout() 方法来模拟登录和登出操作。我们还提供了一个 isUserLoggedIn 属性来检查用户是否已登录:

class AuthService extends GetxService {// 假设我们有一个User类来表示用户User _user;// 获取当前用户User get user => _user;// 模拟登录操作Future<void> login(String username, String password) async {// 在这里,我们只是模拟登录操作,实际情况下,你可能需要调用APIawait Future.delayed(Duration(seconds: 1)); // 模拟网络延迟// 假设登录成功,创建一个User对象_user = User(username, password);}// 模拟登出操作Future<void> logout() async {// 在这里,我们只是模拟登出操作,实际情况下,你可能需要调用APIawait Future.delayed(Duration(seconds: 1)); // 模拟网络延迟// 清除用户信息_user = null;}// 检查用户是否已登录bool get isUserLoggedIn => _user != null;
}

然后,我们可以在应用启动时使用Get.put()方法来提供这个服务:

void main() {Get.put(AuthService());runApp(MyApp());
}

在需要的地方,我们可以使用Get.find()方法来获取这个服务:

class MyWidget extends StatelessWidget {Widget build(BuildContext context) {var authService = Get.find<AuthService>();if (authService.isUserLoggedIn) {// 用户已登录,显示用户信息} else {// 用户未登录,显示登录页面}}
}

这样,我们就可以在应用的任何地方访问和修改 AuthService,而无需手动传递它。

4.1 put、putAsync、lazyPut的区别

在GetX中,put(), putAsync(), 和 lazyPut() 方法都用于向依赖注入系统中添加对象,但它们的行为有所不同。

  1. put():这个方法会立即创建并添加对象。
    当你调用 Get.put(Service()) 时,Service 的实例会立即被创建并添加到依赖注入系统中。

  2. putAsync():这个方法用于异步创建并添加对象。
    当你调用 Get.putAsync(() async => await Service.create()) 时,Service 的实例会在 Service.create() 异步操作完成后被创建并添加到依赖注入系统中。

  3. lazyPut():这个方法用于延迟创建并添加对象。
    当你调用 Get.lazyPut(() => Service()) 时,Service 的实例会在第一次通过 Get.find() 获取时被创建并添加到依赖注入系统中。

在实际项目中,应该根据你的需求选择合适的方法。如果你的对象需要进行耗时的初始化操作,你可能需要使用 putAsync()。如果你的对象只在某些特定的情况下需要,你可能需要使用 lazyPut()。在其他情况下,你可以使用 put()。

需要注意的是,如果你的服务、控制器等,它们在逻辑上如果是相互依赖的,使用 put 可能需要更多注意先后关系。因此使用 lazyPut 有时候会更加方便一些。

5. 使用Get It 库实现依赖注入(简介)

GetIt是一个简单而强大的服务定位器,也可以用于依赖注入。它允许我们在应用的任何地方访问我们的依赖项。

5.1 定义和注册依赖项

首先,我们定义一个服务类,然后在应用启动时,我们使用GetIt.I.registerSingleton()方法来注册这个服务:

void main() {GetIt.I.registerSingleton<MyService>(MyService());runApp(MyApp());
}class MyService {fetchData() {// fetch data}
}

代码中,我们在main函数中使用GetIt.I.registerSingleton()方法注册了一个MyService对象。这样,我们就可以在应用的任何地方访问这个MyService对象。

5.2 获取和使用依赖项

在GetIt中,我们可以使用 GetIt.I.get() 方法来获取一个依赖项。例如,我们可以在任何需要的地方获取并使用MyService对象:

class MyWidget extends StatelessWidget {Widget build(BuildContext context) {var myService = GetIt.I.get<MyService>();myService.fetchData();// ...}
}

在上面的例子中,我们在MyWidget中使用GetIt.I.get()方法获取了MyService对象,并调用了它的fetchData方法。

下一篇:《聊一聊依赖注入(下)》将重点讲解 Get it 库

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

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

相关文章

【使用python绘制统计图表】

在Python中&#xff0c;我们通常使用matplotlib&#xff0c;pandas&#xff0c;和seaborn等库来绘制统计图表。 A.以下是一些示例&#xff1a; 首先&#xff0c;确保你已经安装了这些库。如果没有&#xff0c;可以使用以下命令进行安装&#xff1a; pip install matplotlib …

P6入门:项目初始化1-项目详情介绍

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

React + Antd 自定义Select选择框 全选、清空功能

实现代码 import React, { useState, useEffect } from react; import { Select, Space, Divider, Button } from antd;export default function AllSelect (props) {const {fieldNames, // 自定义节点labbel、value、options、grouLabeloptions, // 数据化配置选项内容&#…

MeterSphere 任意文件读取漏洞(CVE-2023-25814)

MeterSphere 任意文件读取漏洞&#xff08;CVE-2023-25814&#xff09; 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"MeterSphere" 漏洞复现1. 构造poc2. 发送数据包3. 查看文件 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地…

线性代数(六)| 二次型 标准型转换 正定二次型 正定矩阵

文章目录 1. 二次型化为标准型1.1 正交变换法1.2 配方法 2 . 正定二次型与正定矩阵 1. 二次型化为标准型 和第五章有什么样的联系 首先上一章我们说过对于对称矩阵&#xff0c;一定存在一个正交矩阵Q&#xff0c;使得$Q^{-1}AQB $ B为对角矩阵 那么这一章中&#xff0c;我们…

C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发006:基于redis查找玩家姓名+游戏业务实现总结

文章目录 1 Redis的安装与API的使用1.1 安装目录及环境变量1.2 设置远程客户端连接和守护进程1.3 启动redis1.4 Hiredis API的使用1.5 我的动态库和头文件 2 Redis的使用2.1 初始化时候2.2 结束的时候 3 测试4 Makefile5 游戏业务总结 1 Redis的安装与API的使用 1.1 安装目录及…

VUE页面导出PDF方案

1&#xff0c;技术方案为&#xff1a;html2canvas把页面生成canvas图片&#xff0c;再通过jspdf生成PDF文件&#xff1b; 2&#xff0c;安装依赖&#xff1a; npm i html2canvas -S npm i jspdf -S 3&#xff0c;封装导出pdf方法exportPdf.js: // 页面导出为pdf格式 //titl…

已解决:rm: 无法删除“/opt/module/zookeeper-3.4.10/zkData/zookeeper_server.pid“: 权限不够

解决&#xff1a; ZooKeeper JMX enabled by default Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg Stopping zookeeper ... /opt/module/zookeeper-3.4.10/bin/zkServer.sh: 第 182 行:kill: (4149) - 不允许的操作 rm: 无法删除"/opt/module/zooke…

【技术驿站】分布式基础与常见面试问题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Brain Teaser概率类 - 抛硬币

问题 你有 17 个硬币&#xff0c;我有 16 个硬币&#xff0c;我们同时抛掷所有硬币。 如果你有更多的正面&#xff0c;那么你赢&#xff0c;否则我赢。 你获胜的概率是多少&#xff1f; 解答 划分出3个相互独立的事件&#xff1a;你抛前16枚硬币&#xff1b;我抛16枚硬币&am…

SQL第三次上机作业

1.查询与王利就读同一专业学生的借书证号和姓名 USE TSGL GO SELECT Lno,Rname FROM Reader WHERE Dept(SELECT DeptFROM ReaderWHERE Rname王利) and Rname ! 王利2.查询比希望出版社出版的所有图书价格都高的图书信息 SELECT * FROM Book WHERE Price>(SELECT MAX(Price…

【经验模态分解】3.EMD模态分解算法设计与准备工作

/*** poject 经验模态分解及其衍生算法的研究及其在语音信号处理中的应用* file EMD模态分解算法设计与准备工作* author jUicE_g2R(qq:3406291309)* * language MATLAB* EDA Base on matlabR2022b* editor Obsidian&#xff08;黑曜石笔记软…

Unity3d C#实现编辑器不运行状态下执行的脚本

第一章方式&#xff1a; 函数前面 [ContextMenu("Play")] &#xff0c;Inspector面板右键调用 第二种方式&#xff1a; OnValidate() &#xff0c;值改变自动执行 using UnityEngine; using System.Linq;public class NightController : MonoBehaviour {pub…

halcon的算子加速

关于实现Halcon算法加速的基础知识(2)&#xff08;多核并行/GPU&#xff09;-阿里云开发者社区 多核并行 aop gpu 内存泄漏 HOperatorSet.GenImage1Extern最后一个参数Marshal.GetFunctionPointerDelegate(freePtr) private static HalconApi.HClearProcCallBack freePtr …

已解决:云原生领域的超时挂载Bug — Kubernetes深度剖析

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

3.0.3版vsftpd所支持的FTP命令

2023年11月9日&#xff0c;周四下午 ABOR&#xff1a;中止当前的数据连接。ACCT&#xff1a;提供用户帐户信息&#xff0c;通常用于特定的站点访问控制。ALLO&#xff1a;为服务器上的文件分配存储空间。APPE&#xff1a;将数据添加到现有的远程文件中。CDUP&#xff1a;将当前…

17.复制字符串 ,包括\0

#include<stdio.h> #include <cstring>int main(){int len1,len2;char s1[44];char s2[33];scanf("%s",s1);scanf("%s",s2);len1strlen(s1)1;printf("先s1的字符长度为&#xff1a;%d\n",len1) ;strcpy(s1,s2) ;printf("复制字…

CCF ChinaSoft 2023 论坛巡礼 | 云计算标准化论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

03【远程协作开发、TortoiseGit、IDEA绑定Git插件的使用】

上一篇&#xff1a;02【Git分支的使用、Git回退、还原】 下一篇&#xff1a;【已完结】 目录&#xff1a;【Git系列教程-目录大纲】 文章目录 一、远程协作开发1.1 远程仓库简介1.1.1 Github1.1.2 Gitee1.1.3 其他托管平台 1.2 发布远程仓库1.2.1 创建项目1&#xff09; 新…

案例:用户管理

案例&#xff1a;用户管理 1.展示用户列表2.添加用户3.删除用户 1.展示用户列表 url函数 获取用户星系HTML渲染 2.添加用户 url函数 GET。看到页面&#xff0c;输入内容POST。提交->写入到数据库 3.删除用户 url函数 http://127.0.0.1:8000/info/add/&#xff1f;ni…