文档章节

依赖管理(二):第三方组件库在Flutter中要如何管理

拉维
 拉维
发布于 2019/08/15 08:48
字数 3140
阅读 60
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

前面的文章中,我介绍了Flutter工程的资源管理机制。在Flutter中,资源采用先声明后使用的机制,在pubspec.yaml显示地声明资源路径后,才可以使用。


对于图片,Flutter基于像素密度,设立不同分辨率的目录分开管理,但只需要在pubspec.yaml中声明一次;而字体则基于样式支持,除了正常字体,还可以支持粗体、斜体等样式。最后,由于Flutter工程的启动需要原生运行环境,因此对于在其启动之前所需的启动图和icon图标这两类特殊资源,我们还需要分别去原生工程中进行相应的设置


其实,除了管理这些资源外,pubspec.yaml更为重要的作用是管理Flutter工程代码的依赖,比如第三方库、Dart运行环境、Flutter SDK版本都可以通过它来进行统一管理。所以,pubspec.yaml与iOS中的Podfile、Android中的build.gradle、前端的package.json在功能上是类似的。


今天,我们就来聊聊,在Flutter中如何通过配置文件来管理工程代码依赖。


Pub


Dart提供了包管理工具Pub,用来管理代码和资源。从本质上讲,包(package)实际上就是一个包含了pubspec.yaml文件的目录,其内部可以包含代码、资源、脚本、测试和文档等文件。包中包含了需要被外部依赖的功能抽象,也可以依赖其他包。


与iOS中的CocoaPods、Android中的JCenter/Maven、前端中的npm库类似,Dart提供了官方的包仓库Pub。通过Pub,我们可以很方便地查找到有用的第三方包。


当然,这并不意味着我们可以简单地拿别人的库拼凑成一个应用程序。Dart提供包管理工具的真正目的是,让你能够找到真正好用的、经过线上大量验证的库,复用他人的成果来缩短开发周期,提升软件质量


在Dart中,库和应用都属于包pubspec.yaml是包的配置文件,包含了包的元数据(比如,包的名称和版本)、运行环境(也就是Dart SDK 与 Flutter SDK 版本)、外部依赖、内部配置(比如,资源管理)。


在下面的例子中,我们声明了一个flutter_app_example的应用配置文件,其版本为1.0,Dart运行环境支持2.0到3.0之间,依赖 flutter cupertino_icons

name: flutter_app_example # 应用名称description: A new Flutter application. # 应用描述version: 1.0.0 #Dart 运行环境区间environment:  sdk: ">=2.1.0 <3.0.0"#Flutter 依赖库dependencies:  flutter:    sdk: flutter  cupertino_icons: ">0.1.1"


运行环境和依赖库 cupertino_icons 冒号后面的部分是版本约束信息,由一组空格分割的版本描述组成,可以支持指定版本、版本号区间,以及任意版本这三种版本约束方式。比如上面的例子中,cupertino_icons 引用了大于 0.1.1 的版本。


需要注意的是,由于元数据与名称使用空格分割,因此版本号中不能出现空格;同时又由于大于符号“>”也是YAML语法中的折叠换行符号,因此在指定版本范围的时候,必须使用引号,比如 ">=2.1.0 <3.0.0" 。


对于包,我们通常是指定版本区间,而很少指定特定版本,因为包升级变化很频繁,如果有其他的包直接或间接依赖这个包的其他版本时,就会经常发生冲突。


对于运行环境,如果是团队多人协作的工程,建议将Dart与Flutter的SDK环境写死,统一团队的开发环境,避免因为跨SDK版本出现的API差异进而导致工程问题。


比如,在上面的示例中,我们可以将Dart SDK写死为2.3.0,FlutterSDK写死为1.2.1。

environment:  sdk: 2.3.0  flutter: 1.2.1


基于版本的方式引用第三方包,需要在其Pub上进行公开发布,我们可以访问 https://pub.dev 来获取可用的第三方包。而对于不对外公开发布,或者目前处于开发调试阶段的包,我们需要设置数据源,使用本地路径或者Git地址的方式进行包声明


在下面的例子中,我们分别以路径依赖以及Git依赖的方式,声明了package1package2这两个包

dependencies:  package1:    path: ../package1/  # 路径依赖  date_format:    git:      url: https://github.com/xxx/package2.git #git 依赖


在开发应用时,我们可以不写明具体的版本号,而是以区间的方式声明包的依赖;但对于一个程序而言,其运行时具体引用哪个版本的依赖包必须要确定下来。因此,除了管理第三方依赖,包管理工具Pub的另一个职责是,找出一组同时满足每个包版本约束的包版本。包版本一旦确定,接下来就是下载对应版本的包了。


对于dependencies中的不同数据源,Dart会使用不同的方式进行管理最终会将远端的包全部下载到本地。比如,对于Git声明依赖的方式,Pub会clone Git仓库;对于版本号的方式,Pub则会pub.dartlang.org下载包。如果包还有其他的依赖包,比如package1包还依赖package3包,Pub也会一并下载。


然后,在完成了所有依赖包的下载后,Pub会在应用的根目录下创建.package文件,将依赖的包名与系统缓存中的包文件路径进行映射,方便后续维护。


最后,Pub会自动创建pudspec.lock文件。pubspec.lock 文件的作用类似于iOS的Podfile.lock文件,用于记录当前状态下实际安装的各个直接依赖、间接依赖的包的具体来源和版本号


比较活跃的第三方包的升级通常比较频繁,因此对于多人协作的Flutter应用来说,我们需要把pubspec.lock文件也一并提交到代码版本管理中,这样团队中的所有人在使用这个应用时安装的所有依赖都是完全一样的,以避免出现库函数找不到或者其他的依赖错误。


除了提供功能和代码维度的依赖之外,包还可以提供资源的依赖。在资源包中的pubspec.yaml文件已经声明了同样资源的情况下,为节省应用程序安装包大小,我们需要复用依赖包中的资源。


在下面的例子中,我们的应用程序依赖了一个名为package4的包,而它的目录结构是这样的:

pubspec.yaml    └──assets    ├──2.0x    │   └── placeholder.png    └──3.0x        └── placeholder.png


其中,placeholder.png是可复用的资源。因此,在应用程序中,我们可以通过 Image 和 AssetImage 提供的 package 参数,根据设备实际分辨率去加载图像。

Image.asset('assets/placeholder.png', package: 'package4');
AssetImage('assets/placeholder.png'package'package4');


例子


接下来,我们通过一个日期格式化的例子,来演示如何使用第三方库。


在Flutter中,提供了表达日期的数据结构 DateTime ,这个类拥有极大的表示范围,可以表达1970-01-01UTC时间后100,000,000天内的任意时刻。不过,如果我们想要格式化显示日期和时间,DateTime 并没有提供非常方便的方法,我们不得不自己取出年、月、日、时、分、秒,来定制显示方式


值得庆幸的是,我们可以通过 date_format 这个第三方包来实现我们的诉求:date_format 提供了若干常用的日期格式化方法,可以很方便地实现格式化日期的功能


首先,我们在 Pub 上找到 date_format 这个包,确定其使用说明:


date_format包最新的版本是1.0.6,于是接下来我们把 date_format 添加到 pubspec.yaml 中:

dependencies:  date_format: ^1.0.6


随后,按 conmand+s 来保存文件的修改,VSCode就会自动运行 flutter pub get 终端命令来安装依赖包。


安装完成后,我们就可以在工程中使用 date_format 来进行日期的格式化了:

  print(formatDate(DateTime.now(), [yyyy, '-', mm, '-', dd]));  print(formatDate(DateTime.now(), [yy, '-', m, '-', dd]));  print(formatDate(DateTime.now(), [yy, '-', m, '-', d]));
print(formatDate(DateTime.now(), [yy, '-', MM, '-', d])); print(formatDate(DateTime.now(), [yy, '-', M, '-', d]));
print(formatDate(DateTime.now(), [yy, '-', M, '-', d]));
print(formatDate(DateTime.now(), [yy, '-', M, '-', DD])); print(formatDate(DateTime.now(), [yy, '-', M, '-', D]));
print(formatDate(DateTime.now(), [HH, ':', nn, ':', ss]));
print(formatDate( DateTime.now(), [hh, ':', nn, ':', ss, ' ', am]));
print(formatDate( DateTime.now(), [hh, ':', nn, ':', ss, ' ', am]));
print(formatDate(DateTime.now(), [hh])); print(formatDate(DateTime.now(), [h]));
print(formatDate(DateTime.now(), [am])); print(formatDate(DateTime.now(), [am]));
print( formatDate(DateTime.now(), [HH, ':', nn, ':', ss, z]));
print(formatDate( DateTime.now(), [HH, ':', nn, ':', ss, ' ', Z]));
print(formatDate(DateTime.now(), [yy, ' ', w])); print(formatDate(DateTime.now(), [yy, ' ', W]));
print(formatDate(DateTime.now(), [yy, '-W', W])); print(formatDate(DateTime.now(), [yy, '-', mm, '-w', W]));
print(formatDate( DateTime.now(), [HH, ':', nn, ':', ss, ' ', Z]));

输出结果如下:

Syflutter: 2019-08-13                                                     Syflutter: 19-8-13                                                        Syflutter: 19-8-13                                                        Syflutter: 19-August-13                                                   Syflutter: 19-Aug-13                                                      Syflutter: 19-Aug-13                                                      Syflutter: 19-Aug-Tuesday                                                 Syflutter: 19-Aug-Tue                                                     Syflutter: 14:06:09                                                       Syflutter: 02:06:09 PM                                                    Syflutter: 02:06:09 PM                                                    Syflutter: 02                                                             Syflutter: 2                                                              Syflutter: PM                                                             Syflutter: PM                                                             Syflutter: 14:06:09+0800                                                  Syflutter: 14:06:09 CST                                                   Syflutter: 19 2                                                           Syflutter: 19 33                                                          Syflutter: 19-W33                                                         Syflutter: 19-08-w33                                                      Syflutter: 14:06:09 CST


总结


在Flutter中,资源与工程代码依赖属于包管理范畴,采用包的配置文件pubspec.yaml 进行统一管理。


我们可以通过pubspec.yaml 设置包的元数据(比如,包的名称和版本)、运行环境(比如,Dart SDK与Flutter SDK版本)、外部依赖和内部配置。


对于依赖的指定,可以以区间的方式确定版本兼容范围,也可以指定本地路径、Git、Pub这三种不同的数据源,包管理工具会找出同时满足每个依赖包版本约束的包版本,然后依次下载,并通过.packages 文件建立下载缓存与包名的映射,最后统一将当前状态下,实际安装的各个包的具体来源和版本号记录至pubspec.lock 文件


现代编程语言大都自带依赖管理机制,其核心功能是为工程中所有直接或间接依赖的代码库找到合适的版本,但这并不容易。就比如前端的依赖管理器 npm 的早期版本,就曾因为不太合理的算法设计,导致计算依赖耗时过长,依赖文件夹也高速膨胀,一度被开发者们戏称为“黑洞”。而Dart使用的Pub依赖管理机制所采用的PubGrub算法则解决了这些问题,因此被称为下一代版本依赖解决算法,在2018年底被苹果公司吸纳,成为Swift所采用的依赖管理器算法。


当然,如果你的工程中的依赖比较多,并且依赖关系比较复杂,即使再优秀的依赖解决算法也需要花费较长时间才能计算出合适的依赖库版本。如果我们想减少依赖管理器为你寻找代码库依赖版本所耗费的时间,一个简单的做法就是从源头抓起,在pubspec.yaml 文件中固定那些依赖关系复杂的第三方库们,及它们递归依赖的第三方库的版本号。


实际上,绝大部分的功能现在都已经有了Flutter插件。

比如音视频,在Pub上现在已经有很多的音视频类插件可供选择了,你可以选择一个评分和star相对高一些的,像声网这样专业的音视频公司也有专门的SDK做Flutter音视频通信。

地图插件大都基于GoogleMap,我们可以耐心等待国内的地图厂商提供Flutter插件版本。

即时通讯目前没有好的Flutter插件,所以必须通过混合开发的方式把IM能力融合到Flutter应用中。


  • pubspec.yaml,配置各种包依赖,找寻符合各种依赖关系的包版本;

  • pubspec.lock,存储第三方包最终确定的版本以及具体来源;

  • .packages,包名与下载缓存的映射


以上。

本文分享自微信公众号 - iOS小生活(iOSHappyLife)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

拉维
粉丝 0
博文 200
码字总数 21569
作品 0
杭州
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
用vertx实现高吞吐量的站点计数器

工具:vertx,redis,mongodb,log4j 源代码地址:https://github.com/jianglibo/visitrank 先看架构图: 如果你不熟悉vertx,请先google一下。我这里将vertx当作一个容器,上面所有的圆圈要...

jianglibo
2014/04/03
4.2K
3
CDH5: 使用parcels配置lzo

一、Parcel 部署步骤 1 下载: 首先需要下载 Parcel。下载完成后,Parcel 将驻留在 Cloudera Manager 主机的本地目录中。 2 分配: Parcel 下载后,将分配到群集中的所有主机上并解压缩。 3 激...

cloud-coder
2014/07/01
6.8K
1
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.4K
16
树莓派(Raspberry Pi):完美的家用服务器

自从树莓派发布后,所有在互联网上的网站为此激动人心的设备提供了很多有趣和具有挑战性的使用方法。虽然这些想法都很棒,但树莓派( RPi )最明显却又是最不吸引人的用处是:创建你的完美家用...

异次元
2013/11/09
6.9K
8

没有更多内容

加载失败,请刷新页面

加载更多

如何设置HTML的默认值 元件? - How can I set the default value for an HTML element?

问题: I thought that adding a "value" attribute set on the <select> element below would cause the <option> containing my provided "value" to be selected by default: 我认为在下面......

富含淀粉
27分钟前
20
0
CSS--select消除点击背景色

1. IOS(苹果)系统手机上触发点击事件会有高亮的阴影,解决办法如下: *{ -webkit-tap-highlight-color: rgba(0,0,0,0);-webkit-tap-highlight-color: transparent; } 2. Android(安卓)...

xrshop
32分钟前
20
0
相约周四晚8点 都说今年秋招难,来直播听参加过提前批的同学怎么说

说到每年的秋招,相信大家都知道它分“提前批”和“正式批”。 提前批的要求一般来说比正式批更高,难度也更大,是各个公司企业间的“人才战”,但sp/ssp相比正式批较多,而且就算没有通过也...

空白贝塔
今天
0
0
数据分析神器!好用,盘它!

在职场中,有些人总是手忙脚乱,看起来忙得飞起,实际上工作却杂乱无章,做完还要检查,甚至返工。 但也有一些人,游刃有余,喝着奶茶就把工作搞定了,从来不加班。 这背后是扎心的职场真相:...

BI佐罗
今天
0
0
Kubernetes Service与Endpoint指向外部服务

--------------------------------------------------kubectl apply -f - <<EOFkind: ServiceapiVersion: v1metadata: name: snake-demo-svc namespace: basespec: ports: - po......

_snake_
37分钟前
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部