关于Flutter空安全升级方案整理

前言

Flutter 从 2.0 版本开始支持空安全(Null Safety)。dart 版本为:

dart 复制代码
 environment:
  sdk: ">=2.12.0 < 3.0.0"

升级到空安全后,由于语法的变动,基本上整个工程,代码都爆红,这对项目来说简直是灾难性的打击,不升级的话只是缓兵之计,因为随着时间的推移,flutter将不再维护非空安全的版本,同时一些三方库也将无法使用,因此空安全升级变成了一个不得不做的事情,项目越复杂需要的时间也就越持久,考虑到对项目的稳定性,和开发周期。选择一个合适自己项目的迁移方案非常重要。下面自己会讲下自己对空安全迁移的理解和实施方案,目前成功迁移几个项目,还是比较有经验的。

语法变动

空安全升级后dart语法是存在很大的变化的,但是理念很简单即:万物皆可为空 ,和以往不同,现在任意创建一个对象都需要判断对象是否为空,这样在使用的时候,便明确知道这个对象是否可能为空,可以避免很多空指针的情况。
Dart 空安全的关键语法

1.不可空类型

在启用空安全的 Dart 中,默认情况下所有类型都是不可空的。这意味着一个变量声明后,不能为 null。例如:

dart 复制代码
int x = 42; // x 不能为 null

如果尝试将 null 赋值给不可空类型,编译器将会报错。

2.可空类型

如果一个变量可以是 null,则必须在类型后加上 ?。例如:

dart 复制代码
int? y = null; // y 可以是整数或 null

这种语法清晰地表明 y 是可空的,开发者在使用时必须考虑它可能为 null 的情况。

3.非空断言操作符 !

当你确定一个可空变量不为 null 时,可以使用 ! 操作符进行非空断言。例如:

dart 复制代码
int? a = 5;
int b = a!; // 断言 a 不为 null,安全地将其赋值给 b

使用 ! 操作符时要小心,因为如果断言错误(即变量实际上为 null),程序会抛出异常。因此对于不确定的变量尽量不要强制!

4.空合并操作符 ??

空合并操作符提供了一种为可空变量指定默认值的便捷方式。例如:

dart 复制代码
String? name;
String displayName = name ?? 'Guest'; // 如果 name 为 null,则使用 'Guest'

这种操作符在开发中是非常常用的,我们经常会通过??来给一些可为空的对象创建兜底值

5.late 变量

有时候我们明确知道这个对象一定是不会为null的但是再创建的时候拿不到值,便可以通过late 来延迟初始化这个对象

dart 复制代码
late String description;

void initialize() {
  description = 'Dart is fun!';
}

使用 late 声明的变量在首次使用前必须被初始化,否则将导致运行时错误。

空安全迁移

Flutter 官方提供了空安全迁移方案:使用dart migrate 工具可以借助该工具

在开始迁移之前,请确保做好以下准备工作:

升级 Dart SDK:确保你使用的 Dart SDK 版本是 2.12 或更高版本。可以通过 dart --version 检查当前的 Dart 版本。

更新依赖:确保项目中的所有依赖包都支持空安全。可以使用以下命令查看哪些包需要更新:

dart 复制代码
dart pub outdated --mode=null-safety

可以根据输出的日志将不支持空安全的版本升级或者替换

以上都完成后,便可以使用 dart migrate 来进行代码的替换了

dart migrate工具会为你构建迁移后的代码建议,并启动一个交互式的迁移网页界面。

dart 复制代码
View the migration suggestions by visiting:

  http://127.0.0.1:60278/Users/you/project/mypkg.console-simple?authToken=Xfz0jvpyeMI%3D

以上便可以通过dart migrate进行简单的迁移
手动迁移

真实的开发场景哪有这么理想,因此使用dart migrate对很多大型项目并不是很使用,并且使用dart migrate会大量的添加** !** 导致项目一堆异常,无法真正的运行起来。

另外也没有那么多时间,让项目进度停下来做空安全迁移。

因为我们做的方案是:创建一个支持空安全语法的module

dart 复制代码
//需要安卓和iOS模块
flutter create --template=plugin --org com.xxxx.xxx --platforms android --platforms ios --project-name aaa bbb
dart 复制代码
flutter create --template=plugin --org com.xxxx.xxxxx  --project-name aaaa bbbb

将创建好的工程以module的形式,依赖到主项目中,此方法不会打破项目的连续性,可以边迁移,边做需求开发。需要注意的是:

需要将主项目(非空安全)和子module(空安全)以相对路径的方式进行依赖因此需要修改pubspec.yaml 中的依赖方式

UT和覆盖率需要以下面的方式进行run

dart 复制代码
flutter test --no-sound-null-safety

在整体空安全都迁移完成后,再将项目移会去,这样边可以,一部分同时进行正常的需求开发,一部分同时进行空安全整改,最大化的提高工作效率。

当然在迁移过程中会涉及到import的问题,这里自己写了脚本,用于批量修改导包

dart 复制代码
#!/bin/sh

echo "修改工程导包"

# 修改对应的一级文件夹
CHANGEFILE="xxxx"


# 修改以package开头的导包
package_name="package:xxxxx\/$CHANGEFILE\/"
new_string=""
old_string="\.\.\/"
search_string="\.\.\/$CHANGEFILE"
old_import="$CHANGEFILE\/"
# 从主项目迁移到空安全路径修改导包路径名称
new_import="package:mobile_cn_null_safety\/$CHANGEFILE\/"

find . -name "*.dart"|while read fname; do

 echo "The string is found on line(s): $fname"
#package 开头导包
 if grep -q "$package_name" "$fname"; then
    # 使用grep查找字符串所在行
    package_line_number=$(grep -n "${package_name}" "$fname" | cut -d: -f1)
    # 遍历行号
    for each1 in $package_line_number
     do
       if [ -n "$each1" ];then
         sed -i  '' "${each1}s/${package_name}/${new_import}/g" "$fname"
       fi
     done

 fi


# 相对路径导包
  if grep -q "$search_string" "$fname"; then
      # 使用grep查找字符串所在行
      line_number=$(grep -n "${search_string}" "$fname" | cut -d: -f1)
      for each in $line_number
       do
         if [ -n "$each" ];then
           sed -i  '' "${each}s/${old_string}//g" "$fname"
           sed -i  '' "${each}s/${old_import}/${new_import}/g" "$fname"
         fi
       done

  fi
done

以上差不多便是全部内容。

相关推荐
️ 邪神11 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】文本点击事件
flutter·ios·鸿蒙·reactnative·anroid
️ 邪神13 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】文本Text显示
flutter·ios·鸿蒙·reactnative·anroid
iFlyCai14 小时前
Flutter中有趣的级联语法
flutter
恋猫de小郭14 小时前
Flutter 小技巧之 Shader 实现酷炫的粒子动画
flutter
hello world smile17 小时前
Dart中List API用法大全
flutter·list·dart
lqj_本人1 天前
Flutter&鸿蒙next 使用 BLoC 模式进行状态管理详解
flutter·华为·harmonyos
Miketutu1 天前
flutter 项目初建碰到的控制台报错无法启动问题
flutter
lqj_本人1 天前
flutter&鸿蒙next 使用 InheritedWidget 实现跨 Widget 传递状态
flutter·华为·harmonyos
氤氲息1 天前
flutter 发版的时候设置版本号
flutter
潘敬1 天前
flutter 语法糖库 flutter_magic 发布 1.0.1
开发语言·前端·javascript·flutter·typescript