Flutter 作为前端一个发展方向大热,有必要了解一下。
踩坑流水账,遇到啥记啥,不求全;遇到的 弯弯绕绕,错误示范 也 如实记载 。当前 Flutter 还不太稳定,可能过一段时间操作细节就变了(希望是改进),这种记录可以跟将来做对比。
我的水平远远够不上厉害,但在一些朋友看来可能有可取之处,当他们自学遇到困难时,偶尔会问我是怎么学的。记录这样的流水账,你可以看到,我遇到新事物,一样是一点点试错。相信真正的大牛,(至少曾经)也是这样过来。 只不过大牛可能悟性好,试错时间和次数少一些。自己试错多了,也会掌握试错的套路。
某个程序员朋友,连 help 和 man 都不知道,让人着急。可我高中时,明明已经学过简单的编程,对 Linux 居然连听都没听过,听同学讨论一头问号。我的反应是什么?放假第一时间让我的电脑变成 Windows / Ubuntu 双系统。他之前不知道 help,现在知道了。互联网时代,知道了关键词,就不能说你一无所知。起码要把公开资料看完吧?
你看到的『最佳实践』的文章,其实已经是作者熟练之后提炼的结果。好处是读者不必再走一次弯路,坏处是读者可能会产生心理落差。
—— 这段文字跟本文内容关系不大,发散一下,算是呼应之前《S/L 大法——平凡人的做事方法》的观点吧。
后面如果觉得 Flutter 开发(在当前、对我而言)可以入坑,再来写经过整理总结的经验帖,否则就此一篇。
环境配置
获取 Flutter stable
考虑 flutter 还在快速开发中,为了方便更新,直接拉仓库的 stable 分支。
|
|
得到的版本是 1.12.13+Hotfix.8
。
然后运行 flutter_console.bat ,按提示,先执行
|
|
提示 powershell 版本太低,这时才想起,去看看依赖(这台老电脑买的时候预装 Win8,因为 Win8 只是个过渡产物很快升了 Win10。结果某个硬件驱动不兼容 Win10,干脆退回 Win7。)
Windows PowerShell 5.0 或者更高的版本(Windows 10 中已经预装了)
Git for Windows 2.x,并且勾选从 Windows 命令提示符使用 Git 选项。
如果 Windows 版的 Git 已经安装过了,那么请确保能从命令提示符或者 PowerShell 中直接执行 git 命令。
Powershell
git 作为最高频的工具,几乎是最新的。而平时不用的 Powershell,什么版本真的没概念,赶紧打开一个 Powershell,输入
|
|
才 2.0,装个最新的吧。
发现 6.0 以上不能直接安装,还得安装 Powershell Core (因为从 Win 专用变成跨平台了)。要不要装最新的呢?先不管,反正 .NET 4.5 以上是一定需要的,先看 .NET 的版本
|
|
是 4.7 ,够新了。
WMF
然后是 WMF(Windows Management Framework)。
不同版本的 Powershell 和 WMF 的依赖关系如下:
Powershell 版本 | PS 5.0 | PS 5.1 | PS 6.0+ |
---|---|---|---|
依赖的 WMF 版本 | WMF 5.0 (已包含 PS 5.0) | WMF 5.1 (已包含 PS 5.1) | WMF 4.0+ |
结论,不管我装不装 6.0+,安装一个 WMF 5.1 总是没错的。
下载,解压,按说明在 Powershell 里运行安装的 ps1 脚本。提示没有签名。
|
|
再运行一次,可以了。
这时再看 Powershell 的版本。
|
|
Powershell 5.1 已经包含在 WMF 5.1 了,已经满足要求,就没啥动力继续折腾 6.0+ 了。
Flutter 镜像
回到 flutter_console ,继续 flutter doctor
,这回 Powershell 不报错,开始自动下载 Dart SDK 了(难怪没有要求先安装 Dart):
|
|
貌似因为网络不好,下载过程重试了几遍,还好最终成功了
|
|
同样的,pub upgrade
也重试了很多遍,不过次数太多,引起注意,先停掉。翻到
扫了一眼,直接用 Flutter 社区的镜像。因为是 Windows,打开环境变量,加上这两个值:
|
|
Android SDK
重新打开 flutter_console.bat,再次 flutter doctor
|
|
作为一个前 Android 开发者,电脑上其实一直有 Android Studio 和 Android SDK。
开始这篇流水账之前,我已经把 Android Studio 升级到当下最新的 3.6,SDK tools 也升级到最新的 26.1.1。只是 Android Studio 能定位 SDK 的位置,所以没有设置 ANDROID_HOME。
因为 Android Studio 已经很稳定,下载很快,安装很傻瓜,就不提了。
SDK version
新添加 ANDROID_HOME
,重开 console(以重新加载环境变量),再次 flutter doctor
,SDK 居然不是最新,而且也不是 Android Studio 里显示的 26.1.1,可能升级时漏了什么:
|
|
那就去跑一下 sdkmanager 吧
|
|
虽然因为有一段时间没写 Java,并非最新的 LTS 版本(当前是 Java8 u241 或者 Java 11.0.6),但 Java 10 也不旧。我还特意看了 Java 10 的文档,确定有 javax/xml/bind/annotation/XmlSchema
。而且 sdkmanager 的版本也很迷。
(后来发现,Java 很可能是太新,而不是太旧,所以需要指定 JavaEE 包。据说 Java 11 已经移除了 JavaEE (变成了雅加达独立出去了),更加运行不了。)
暂时没有兴趣深入了解哪里出了问题,直接卸载 Android SDK tools,然后下载最新的 SDK tools 压缩包。
https://www.androiddevtools.cn/ 有收集。注意只是收集各个版本的地址而已,来源还是官方的,下完可以校验一下。这回的 SDK tools 在 Android Studio 显示 26.0.1,终于可以调用了。有一个可用更新 26.1.1,感觉这里就是问题所在,又给升了级,果然又出现上面的 NoClassDefFoundError
。
先不管这个问题,退回到可用的 26.0.1。这是重新认真看 Doctor summaries,发现 "platforms;android-28" "build-tools;28.0.3"
,也就是它要求的所谓 SDK Version,并非我理解的 SDK tools 的 version,而是分别指 platform 和 build-tools 的版本,对应 Android 9(Pie)。为什么迟迟没有发现这点?因为在flutter 官方的引导上面有这么一句话
To prepare to run and test your Flutter app on an Android device, you’ll need an Android device running Android 4.1 (API level 16) or higher.
被我理解为了开发的 API level 是 16+。
license status unknown
再次 flutter doctor
,这回 SDK 版本不报错了,只剩下 Android license status unknown.
。
|
|
跟上面说的 Try re-installing or updating your Android SDK Manager.
一致。
|
|
NoClassDefFoundError
又出现了…… 本来不想在这上面耗时间,看来是躲不过了。
最后找到StackOverflow 的两个问题的回答: StackOverflow 问题1 ,StackOverflow 问题2
编辑器打开 sdkmanager.bat,找到 DEFAULT_JVM_OPTS,在后面追加 -XX:+IgnoreUnrecognizedVMOptions --add-modules java.se.ee
,注意 Windows 环境不需要额外在外面加单引号,加了会导致变量扩充 %~dp0\..
出错,加完之后那一行变成了:
|
|
然后再次执行
|
|
没有任何变化,版本没变,文件修改时间没变,然后 flutter doctor --android-licenses
依然说需要新版本。
然后尝试执行
|
|
提示有 licenses 还没 accept,赶紧一路 yes。
这时我试着再 flutter doctor
一次,居然通过了。说明版本旧不是根本问题。关键是 accept 掉 licenses 。
升级 SDK tools
本来这样就完事了,但还是有点好奇。记不记得 Android Studio 可以直接升级 SDK tools 到 26.1.1 ,那就升级看看。
Android Studio 确实可以升级 SDK tools,但是执行到最后,升级失败。因为升级靠的是打补丁,升级程序检查到 sdkmanage.bat 有过修改,补丁失败。然后升级程序自动改为全量安装,不知道是不是因为打开了某些目录或者文件没管,继续失败。我猜 sdkmanage --update
也是因为类似的原因所以失败了,只是没有报错信息。
这时我想到两个选择:
- 把 sdkmanager.bat 改回去,然后用 Android Studio 升级。
- 把修改之后的 sdkmanager.bat 改个名字,然后复制一份原名的改回去。然后执行修改过的 bat。
(因为没有备份)这需要一字不差改回去,还好改动不多。我尝试了 1 ,成功了。2 就没有验证。
升级完 还得把参数加回去 ,不然又会出现 NoClassDefFoundError
。
继续 flutter doctor
,这回
|
|
这次有了经验,我没有按它的来,而是 sdkmanager --licenses
。应该是升级过的 tools 还要再确认一遍 licenses。
Android Studio Plugins
Settings > Plugins 或者直接在打开项目的界面 Configure > Plugins, 直接搜索 Flutter 安装,安装过程中会提示依赖 Dart 一起安装。
如果中间出现网络不好导致超时,就不要同时装,先装 Dart,再装 Flutter。
据说用 VSCode 开发也可以,我也有在用 VSCode。但是既然 flutter doctor 要求 Android Studio,就先按它的套路走通。
真机 or AVD
最后一步就比较简单了,要么插个真机,要么创建一个 AVD。因为没有多余手机,随便选了一个 Pixel 3 + 奥利奥8.0 x86_64 的AVD。
|
|
这四个 ✔ 得来不易。
第一个项目
安装 flutter 插件之后的 Android Studio ,New 菜单会多出一个 New Flutter Project,有引导,没啥值得说的,一路 Next。结果创建完之后,在生成项目的界面转了半天。第一次生成,对需要多长时间没有概念,以为是老机器卡,就先丢下去干别的。等我离开电脑半天,回来还在转,事情就不太正常了。
flutter lockfile
试图强关 AS 无效之后,只好直接杀掉进程。这时去看 AS 的 workspace,项目看起来已经生成好了。试着重新打开 AS,可能是因为杀进程丢失了一些配置,需要重新指定 Android SDK 的位置。打开 AS 没看到新建的项目,就去打开前面生成的项目。
打开成功,没有提示缺这少那,说明项目生成是没什么问题的。但是初始化之后(自动生成的)代码一片红色,明显是缺少依赖引起的,同时 AS 也弹出提醒,还没有run flutter packages get
,旁边给了几个选项,分别是 Get dependencies 和 Update dependencies(第三个忘了)。
一次都还没获取依赖,自然点第一个,底下的 log 显示:
|
|
查了一下,flutter 有一个全局的 lockfile,在 flutter/bin/cache/lockfile
,每次只允许一个进程打开,作为全局锁。这种套路经常跟 Linux 打交道的应该很熟悉。按网上的说法,把它删掉(因为有进程占用,使用了 unlocker 释放句柄),然后再来。
flutter pub get
这回只是输出少了一行,然后又陷入了无尽的等待。
|
|
没办法,退出 AS 再来(这次没有生成的对话框,可以正常退出),然后又提示锁的问题。死循环了。
锁的问题前面已经说了,是因为 dart 执行命令异常退出,没有释放锁,强制释放就行(后来发现 lockfile 不用删,释放掉就行。当然如果没有 unlocker 之类的工具,强删也行,反正就一个大小为 0 的文件,没有会自动新建)。问题在于为什么第一次会卡死。
pub 是 dart 的包管理器,如同 pip 于 Python,go mod 于 go,Maven 于 Java,npm 于 nodejs… 试了一下,pub 不能直接调用,大概因为没有独立安装 dart,而 flutter pub 是针对 flutter 的封装。
既然是获取依赖包这步卡了,那么大概率就是 GFW 的问题。还记得上面设置的镜像吗?这回释放完 lockfile,改在 flutter_console 里执行:
|
|
不到 6 秒完事,还真就好了。代码里的各种错误也没了。
Android Studio 的环境变量
可是每次都要手动执行命令也很烦。而且就算我愿意,AS 也不会每次执行命令停下来,告诉我对应的命令让执行;它一旦卡死,还得费劲去释放 lockfile。所以,是 AS 读不到设置的环境变量吗?
首先,flutter help
输出的指令里没有 packages
,然后 flutter help packages
告诉我们,packages
只是 pub
的别名。
|
|
再 flutter help packages get
一下。三层命令 help 进来,都没有看到 --no-color
参数干嘛用,先不管它。
这次模仿 AS 的调用方式,不直接在 flutter_console 操作,而是直接调用 flutter/bin/flutter.bat
:
|
|
完全没问题。其实没问题是应该的,因为打开 flutter_console.bat 看就知道,里面只做了两件事,把 flutter/bin/
临时加入 PATH,然后打开一个 cmd,没了。
(好吧,其实是三件,前面还显示了 flutter 的 ascii-art 和 简单的说明。)
|
|
会不会是因为依赖已经下载好了,根本没有触发网络访问?在 AS 再试一次,又卡了……(又要找 unlocker)
在 AS 里打开Terminal,从输出看其实就是一个 cmd,在这里再执行一次,又卡死了……
|
|
环境变量实锤了。
在对着 AS 的 Terminal 一顿 echo 之后,发现绝大多数环境变量都能读到,无论是系统变量,还是用户变量。什么 PATH
,GOROOT
,GOPATH
….. 都有值,唯独 ANDROID_HOME
和 两个镜像地址。然后想了想,这几个值好像都是这次新建的,并且中间没有重启过。呃,人家 cmd 都是关掉重开就可以加载到,难道就你 AS 非要重启计算机?(AS 都重启好多遍了)
试试吧。重启,第一时间打开 AS。
|
|
我x [口吐芬芳],还真是!浪费时间。估计新建项目时也是卡在这里。有空研究一下 AS 的环境变量究竟是怎么加载的。这老电脑开关机慢,一堆文件也懒得关了重新打开,平时都是休眠的,改点啥都要重启真是为难人。
Run
接下来还剩一步,就是验证自带的模板程序能不能跑起来。
打开 AVD,启动前面创建的虚拟设备,然后选这个设备为 run target,然后 run。
|
|
这明显是 jcenter 的网络问题。侥幸认为应该只是慢,重试一遍,居然真的跑起来了。
下次还是换个 aliyun 的仓库镜像吧。
|
|
第一次的编译时间特别久,老机器可能加重了这个情况。后续的修改用 hot reload 应该会快很多。
到这,一行代码都没写,但写代码前的准备工作,算是告一段落。不知不觉居然记了这么长流水账,前后断断续续折腾了两天多。时间大部分是被地理位置下访问技术资源的可达性及网速(简称 Qiang)拖累的,少部分是因为 flutter 有些小坑且文档有误导性。
在等网络和命令执行的间隙,快速扫了一眼 dart 的语法,从不同的地方看到了 Java、Python 还有 Go 的影子,当然也有一些比较原创的语法糖(如级联调用)。刚好这三门语言都还算熟,希望写起来不费劲。
这篇太长了,就此打住。
本文为本人原创,采用知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议进行许可。
本作品可自由复制、传播及基于本作品进行演绎创作。如有以上需要,请留言告知,在文章开头明显位置加上署名(Jayce Chant)、原链接及许可协议信息,并明确指出修改(如有),不得用于商业用途。谢谢合作。
请点击查看协议的中文摘要。