Flutter项目如何迁移至空安全?

591 阅读1分钟

Dart SDK,2.12之前的都不是空安全的,2.12之后都是空安全的。

如何查看项目中哪些依赖没有支持空安全?

以这个项目为例:使用Android Studio 打开starter项目

在starter项目中执行下面命令查看:
dart pub outdated --mode=null-safety
打印如下:
└─[0] <> 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:
intl          ✗0.16.1  ✗0.16.1     ✓0.18.0     ✓0.18.0  

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`.

执行完命令,将会显示intl库没有支持空安全,✗0.16.1 这个是不支持空安全的版本,✓0.18.0是支持空安全的版本

在pubspec.yaml文件中找到对应的库,设置为支持空安全的版本号

//不支持空安全
dependencies:
  flutter:
    sdk: flutter
  intl: ^0.16.1

//支持空安全  
  dependencies:
  flutter:
    sdk: flutter
  intl: ^0.18.0  

修改后然后执行 flutter pub upgrade

注意:目前这个项目中只有一个依赖库intl,如果存在多个依赖库,如果 C 依赖了 B,B 依赖了 A,那么应该按照 A -> B -> C 的顺序进行迁移。

如何合并空安全?

执行下面命令:
dart migrate
打印如下:
─[0] <> dart migrate
Migrating /Users/zhouluyao/Downloads/video-dns-materials-versions-1.0/06-migrate-to-null-safe-code/starter

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:62907/Users/zhouluyao/Downloads/video-dns-materials-versions-1.0/06-migrate-to-null-safe-code/starter?authToken=5FVRDl8f1Ec%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.

1、在浏览器里面浏览这个链接,效果如下图(注意这个是我调试的链接,需要在上面的打印中,找到你的项目控制台打印的链接进行浏览) http://127.0.0.1:62907/Users/zhouluyao/Downloads/video-dns-materials-versions-1.0/06-migrate-to-null-safe-code/starter?authToken=5FVRDl8f1Ec%3D

Snip20230314_3.png

2、需要提示的是,现在项目中的代码还没有改变,目前在浏览器查看的只是一些修改建议

3、你检查过之后没有问题了,可以点击上方的按钮,APPLY MIGRATE修改代码

具体讲解一下项目中的空安全用法:

服务器返回的JSon格式如下,可以看到第一、第三个国家的Json信息中,population字段、currencyCode字段没有返回,也就是说这两个字段是可空的
{
    "countries": [
        {
            "countryCode": "AD",
            "countryName": "Andorra",
            "currencyCode": "EUR",
            "capital": "Andorra la Vella",
            "continentName": "Europe"
        },
        {
            "countryCode": "AE",
            "countryName": "United Arab Emirates",
            "currencyCode": "AED",
            "population": "4975593",
            "capital": "Abu Dhabi",
            "continentName": "Asia"
        },
        {
            "countryCode": "AF",
            "countryName": "Afghanistan",
            "population": "29121286",
            "capital": "Kabul",
            "continentName": "Asia"
        },]
}
下面的注释是项目中的一些具体修改建议
class Country {
  late final String countryCode; //如果一个final 变量没有被赋值,会报final 变量需要initialize的错误,需要把final变量使用late修饰,在使用它之前对他进行设置
  late final String countryName;
  String? currencyCode; //服务器返回的Json字段可能缺少,需要使用String?
  String? population;
  late final String capital;
  late final String continentName;

  Country(
      {required this.countryCode,
      //this.countryName,(修改前)这样写countryName就是option类型的,我们不允许可选参数在这,因为这个final变量是非空的,解决办法是在初始化变量的前面加上require关键字
      required this.countryName,
      required this.currencyCode,
      required this.population,
      required this.capital,
      required this.continentName});

  Country.fromMap(Map<String, dynamic> map) {
    countryCode = map['countryCode'] as String;
    countryName = map['countryName'] as String;
    currencyCode = map['currencyCode'] as String?; //如果服务器返回的json缺少该字段,或者该字段为空,需要转换成String?
    population = map['population'] as String?;
    capital = map['capital'] as String;
    continentName = map['continentName'] as String;
  }
}

//对于可选类型的值可以给一个默认值
   var  population = country.population ?? "";

//对空值进行默认处理
  String formatPopulation(String? population) {
    final format = NumberFormat.compact(locale: 'it-it');
    population ??= '0';
    final number = int.parse(population);
    return format.format(number);
  }