Flutter-升级Flutter到3.0版本

前言

目前项目已经在2.x的版本上运行一段时间了,截止到目前Flutter稳定版本已经到3.7.0了,
但是Flutter3.0.x是最后支持iOS9、iOS10以及32位系统的版本,所以基于各方面考虑,决定把Flutter升级到3.0.5版本。
同时,因为空安全也已经出来很久了,且在dart 2.19版本后,可能不支持空安全迁移工具了,所以决定把项目也迁移到空安全。

主要两步:

  1. 升级Flutter版本,文档入口
  2. 升级依赖库版本至空安全版本,并迁移代码文档入口

准备工作

  1. 使用命令dart --version查看dart版本
kaye@KKdeMacBook-Pro app-flutter % dart --version
Dart SDK version: 2.12.2 (stable) (Wed Mar 17 10:30:20 2021 +0100) on "macos_x64"
  1. 使用命令flutter doctor查看flutter版本
kaye@KKdeMacBook-Pro app-flutter % flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.4, on macOS 11.5.1 20G80 darwin-x64, locale zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 2020.3)
[✓] VS Code (version 1.62.2)
[✓] Connected device (1 available)

• No issues found!

升级Flutter版本

//升级到支持的最新版本
flutter upgrade 
//如果你想指定版本,则在升级后可以进入到flutter的安装目录进行重置
//HEAD^就是你想要的版本的commit id
git reset --hard HEAD^ 

升级依赖三方库

  1. 使用命令dart pub outdated --mode=null-safety查看当前依赖的pakeage是否支持空安全。
    项目中所依赖的三方库,有一部分没有支持空安全,如那些版本前面有x的三方库,而那些打的就是已经支持的空安全版本。
kaye@KKdeMacBook-Pro app-flutter % dart pub outdated --mode=null-safety
Showing dependencies that are currently not opted in to null-safety.
[✗] indicates versions without null safety support.
[✓] indicates versions opting in to null safety.

Package Name         Current  Upgradable  Resolvable  Latest   

direct dependencies:
charts_flutter       ✗0.9.0   ✗0.9.0      ✓0.11.0     ✓0.12.0  
flutter_swiper       ✗1.1.6   ✗1.1.6      ✗1.1.6      ✗1.1.6   
keyboard_visibility  ✗0.5.6   ✗0.5.6      ✗0.5.6      ✗0.5.6   

1 dependency is constrained to a version that is older than a resolvable version.
To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
  1. 升级依赖库
dart pub upgrade --null-safety

如果你的依赖库全部支持空安全,这里会将所有依赖升级到空安全中,如果不是全部支持,命令行中会打印很多支持依赖的三方库,只需要将建议运行的命令拷贝并在命令行中运行即可。至于那些一直不支持空安全的三方库,需要考虑更换别的库进行代替了。

启动迁移

执行启动迁移命令, 如果在使用命令过程遇见了错误,可根据提示,参考下面的命令

//直接迁移
dart migrate
//跳过依赖的三方库是否支持空安全
dart migrate --skip-import-check 
//跳过依赖的三方库是否支持空安全且忽略异常情况
dart migrate --skip-import-check --ignore-exceptions 

执行命令,开始分析需要进行迁移的代码

kaye@KKdeMacBook-Pro app-flutter % dart migrate
Migrating /Users/xxxx/app-flutter

See https://dart.dev/go/null-safety-migration for a migration guide.

Analyzing project...
[--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------]No analysis issues found.

Generating migration suggestions...
[--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------]

Compiling instrumentation information...
[--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------]

View the migration suggestions by visiting:

  http://127.0.0.1:61098/xxxx/app-flutter?authToken=S12X5Ez93Iw%3D

Use this interactive web view to review, improve, or apply the results.
When finished with the preview, hit ctrl-c to terminate this process.

If you make edits outside of the web view (in your IDE), use the 'Rerun from
sources' action.
  • 左侧部分:是建议支持空安全的文件,选中每个文件会在中间展示这个文件的所有变化后的代码,如果你不想迁移某个文件,可以取消勾选,具体请看后面的文章,有介绍
  • 中间部分:是让我们看所有变化后的代码,我们可以在上面进行确认修改
  • 右侧部分:是相关个改变的结果详细注解,点击linexxx,下面的Edit Details可以看具体的原因
可视化工具.png

从上图我们可以看到,高亮部分,如late、?、!之类的都是迁移工具经过分析之后,给我们自动添加上去的,但是通过工具推导出来的类型也可能是错误的,我们就需要对迁移结果进行改进。下面,通过一个例子来说明。

迁移顺序

遵循一个原则,从依赖最少的文件开始迁移。
如:A依赖B,B依赖C,C不依赖别的文件,那么迁移顺序就是C、B、A。

改进迁移建议

  • 原来非空安全的代码
var intList = const <int>[0, null];
var zero = intList[0];
var one = zero + 1;
var zeroOne = <int>[zero, one];
  • 迁移工具迁移为空安全的结果
var intList = const <int?>[0, null];
var zero = intList[0];
var one = zero! + 1;
var zeroOne = <int?>[zero, one];

从上面的结果,我们可以看到 ,迁移工具认为zero变量的类型为int?,但是其实我们知道,这种情况下,zero不可能为null,且zeroOne中的元素也不可能为null,所以我们需要对迁移结果进行改进。此时我们还没有应用迁移结果,所以我们仍然可以在IDE中修改我们的代码,然后点击迁移界面右上角RERUN FROM SOURCES按钮进行预估迁移结果刷新。

因为在此时我们还没有完全支持空安全,所以在代码中,无法使用late、?、!这些关键字,如果你使用了,那么开发工具爆红,如下图

错误提示.png

那么我们怎么在非完全空安全的情况下,进行标记呢?官方给我们提供了一些表达式,供我们使用。

表达式 说明
expression /*!*/ 添加 ! 至代码中,将 表达式 转换为其基础类型对应的不可空的类型。
type /*!*/ 类型 标记为非空。
/*?*/ 将前面的类型标记为可空。
/*late*/ 将变量声明标记为 late,表示其不会第一时间进行初始化。
/*late final*/ 将变量声明标记为 late final,表示其不会第一时间进行初始化,且初始化后不可改变。
/*required*/ 将参数标记为required

所以,我们可以在我们的代码中使用/*?*/或者/*!*/等进行标记。

var intList = const <int>[0, null];
var zero = intList[0]/*!*/;
var one = zero + 1;
var zeroOne = <int>[zero, one];

点击迁移界面右上角RERUN FROM SOURCES按钮进行预估迁移结果刷新,结果如下:

改进后的结果.png

可以看到,因为在第2行末尾添加了/!/,导致迁移工具给出的迁移结果也是不一样的。

//原始代码迁移结果
var intList = const <int?>[0, null];
var zero = intList[0];
var one = zero! + 1;
var zeroOne = <int?>[zero, one];


//添加标记改进后迁移结果
var intList = const <int?>[0, null];
var zero = intList[0]/*!*/;
var one = zero + 1;
var zeroOne = <int >[zero, one];

应用迁移

如果我们觉得迁移结果没有什么问题,那么点击浏览器中迁移界面右上角APPLY MIGRATION就可以应用迁移结果,这个操作是不可逆的,所以我们需要确保完全认同迁移结果了,才可以点击。
注意:应用迁移后会修改pubspec.yaml文件中的environment sdk版本

image.png

点击确定,再看我们的代码,此时已经应用了空安全。
控制台输出哪些文件迁移到了空安全,哪些不是空安全。

Applying migration suggestions to disk...
Migrated 8 files:
    test/widget_test.dart
    lib/turn_box.dart
    lib/main.dart
    lib/my_process_bar.dart
    lib/my_painter.dart
    lib/demo.dart
    pubspec.yaml
    .dart_tool/package_config.json
Opted 1 file out of null safety with a new Dart language version comment:
    lib/demo2.dart

pubspec.yaml中修改结果

//迁移前的版本
environment:
  sdk: '>=2.10.0 <3.0.0'


//迁移后的版本
environment:
  sdk: '>=2.12.0 <3.0.0'

代码分析

迁移到空安全后,我们使用代码分析器,对代码进行分析,帮助我们进一步修改代码,比如有些变量从可空变成了非空,如果我们在代码中又判断了是否为空,就显得有些不必要。
使用dart analyze命令进行代码分析,控制台也给出了相应的提示,逐一修改即可。
当然,在Flutter开发中,如果我们使用了Android Studio的话,就可以直接使用可视化工具进行dart analyze如下图所示,双击就可以定位到相应的位置,按需修复即可。

dart analyze.png

如果你不想迁移某些包

在使用迁移工具迁移项目时,有可能因为项目巨大,一次性无法完全迁移,那么可以直接取消勾选,这样这些文件就不会被迁移,同时会在你的文件中头部插入一行类似下面的注释,这样你的文件就不会应用空安全。后续如果想迁移到空安全,就再次执行命令dart migrate即可。

// @dart=2.9
// 如果不想应用空安全,可以添加上面的代码

var intList = const <int>[0, null];
var zero = intList[0];
var one = zero + 1;
var zeroOne = <int>[zero, one];
再次启用迁移工具

过程中遇见的问题

迁移空安全常见问题列表

Q1. 第三方库不支持空安全,导致dart migrate命令执行错误

解决方案,执行命令 dart migrate --skip-import-check`

运行迁移命令.png
Q2. 代码异常Null check operator used on a null value at offset

解决方案:找出相应的错误代码所在文件,删除?
或者执行dart migrate --skip-import-check --ignore-exceptions

kk@dabaodeMacBook-Pro app-flutter % dart migrate --skip-import-check
Migrating /Users/qgg/workspace/app-flutter

See https://dart.dev/go/null-safety-migration for a migration guide.

Analyzing project...
[-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|]Warning: package has unmigrated dependencies.

Continuing due to the presence of `--skip-import-check`.  To see a complete
list of the unmigrated dependencies, re-run without the `--skip-import-check`
flag.

No analysis issues found.

Generating migration suggestions...
[-------------------------|                                                                                                                                                ]Aborting migration due to an exception.  This most likely is due to a
bug in the migration tool.  Please consider filing a bug report at:

https://github.com/dart-lang/sdk/issues/new
Please include the SDK version (2.17.6) in your bug report.

To attempt to perform migration anyway, you may re-run with
--ignore-exceptions.

Exception details:

Null check operator used on a null value at offset 4675 in /Users/qgg/workspace/app-flutter/lib/widget/trade/aip/aip_trade_target_rate_widget.dart (Offset?.zero)

#0      EdgeBuilder._handlePropertyAccessGeneralized (package:nnbd_migration/src/edge_builder.dart:3186:46)
#1      ....
......

Q2. FlutterBoost 4.x返回值的问题

在使用FlutterBoost进行push页面的时候,如果使用await或者then获取pop的返回值时,FlutterBoost对返回值进行了调整,4.x版本目前返回的结果如果没有指定的话,返回的是一个空Map,在接收的时候要格外小心,除非返回的是Map类型,否则不要写明类型,不然会抛出一个类型错误。具体在FlutterBoostApp._completePendingResultIfNeeded中体现,boost官方推荐返回值使用Map

case 1: BoostNavigator打开,result是个空的Map,如果指定了类型接收result,非Map类型可能会抛出类型错误的异常
A 页面:
var result = await BoostNavigator.instance.push(xxx, withContainer: true); 
B 页面:
BoostNavigator.instance.pop(); //这里没有传入pop的值

case 2: BoostNavigator打开,正常
A 页面:
bool result = await BoostNavigator.instance.push(xxx, withContainer: true); 
B 页面:
BoostNavigator.instance.pop(true); 

case 3:系统 Navigator打开,result可以是任何类型,包括null
A 页面:
var result = await Navigator.push(context, xxx);
B 页面:
Navigator.of(context).pop((bool/int/xxx); 或者
BoostNavigator.instance.pop(bool/int/xxx);非null值时,是可以指定具体的类型
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容