Flutter 迁移到空安全,延迟还是可空?

Flutter migrating to null safety, late or nullable?

我想正确地迁移到 null 安全,但我不确定到底什么时候使用 late 以及什么时候使用 nullable。我看过Flutter's Understanding null safety article and finished the Null Safety codelabs但我不知道我是否理解正确。 Flutter 和 Dart 的新手,试图在五个不同的场景中抓住这个 null 安全迁移曲线球:

  1. 从 JSON 解析到 dart
  2. 初始化变量
  3. 从 MaterialPageRoute 传递数据
  4. http 数据库助手class
  5. sqflite 数据库助手class

案例一:从JSON解析到dart

我正在使用 MySQL 数据库中的 http 获取数据,并使用 quicktype.io 解析 JSON 以飞镖。我的数据模型的非空安全代码如下所示:

// To parse this JSON data, do
//
//     final someList = someListFromJson(jsonString);

import 'dart:convert';

List<SomeList> someListFromJson(String str) =>
    List<SomeList>.from(json.decode(str).map((x) => SomeList.fromJson(x)));

String someListToJson(List<SomeList> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class SomeList {
  SomeList({
    this.someId,
    this.somebody,
  });

  String someId;
  String somebody;

  factory SomeList.fromJson(Map<String, dynamic> json) => SomeList(
      someId: json["someId"],
      somebody: json["somebody"],

  Map<String, dynamic> toJson() => {
        "someId": someId,
        "somebody": somebody,
      };
}

使用dart migrate,他们建议我Changed type 'String' to be nullable

但是,我知道一个事实,尽管在我的 json 数据中,someId 永远不会也不能为 null,而 somebody 可能为 null。为了初始化,我是否仍应使用 ? 可空类型?我的理解是我不应该对 somebody 使用 ! 空断言运算符,因为它在技术上还没有值。那么,这是否意味着我应该改用 late 关键字?

 String? someId;
 String? somebody;

late String someId;
late String somebody;

案例 2:初始化变量

我在我的一个屏幕上的有状态小部件中调用 SomeList 作为 Future。

旧代码:

class _SomeScreenState extends State<SomeScreen > {

  Future<List<VocabList>> futureList;

它再次建议我让它可以为空,如下所示:

Future<List<VocabList > >? futureList;

我的理解是否正确,我使用 ? 可空类型来初始化 Future<List<VocabList>>

案例三:从 MaterialPageRoute 传递数据

我正在从 MaterialPageRoute 传递数据:

MaterialPageRoute(
builder: (context) => SomeScreen(
someId: something[index].someId,
somebody: something[index].somebody,

在接收端,旧代码如下所示:

class SomeScreen extends StatefulWidget {
  final String someId;
  final String somebody;

  SomeScreen({
    Key? key,
    @required this.someId,
    @required this.somebody,

再次建议我将我的两个最终变量 someIdsomebody 设置为可为空,但它们应该为空还是只是迟到了?

我应该这样做吗?

class SomeScreen extends StatefulWidget {
      final String? someId;
      final String? somebody;
    
      SomeScreen({
        Key? key,
        @required this.someId,
        @required this.somebody,

class SomeScreen extends StatefulWidget {
  late final String someId;
  late final String somebody;

  SomeScreen({
    Key? key,
    @required this.someId,
    @required this.somebody,

案例4:http数据库助手class

我正在将带有按钮的变量 someName 传递给 http 请求。

import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:test/models/something_list.dart';

List<SomethingList >  _list = [];
String someName;

class Somebody {
  static const String  url = 'http://localhost/~idunno/api_search.php';

  static Future<List<SomeList > >  getData(String someName) async {
    try {
      http.Response  response =
          await http.get(Uri.parse(url + '?q=' + someName));

someName不能为空,否则http请求会失败。我是否仍应将其声明为可为空并像这样使用 on FormatException 处理失败?

 List<SomethingList >  _list = [];
        String? someName;
   // some code omitted
   on FormatException {
      throw InvalidFormatException('Something is not right here');

案例5:sqflite数据库助手class

旧代码:

static Database _database;

  Future<Database>  get database async {
    if (_database != null) return _database;
    // lazily instantiate the db the first time it is accessed
    _database = await _initDatabase();
    return _database;
  }

  // some code omitted

 Future<bool >  checkBookmark(String someId) async {
    Database  db = await instance.database;
    var bookmarked = await db.query(table,
        where: 'someId = ?',
        whereArgs: [someId]);
    return bookmarked.isEmpty ? false : true;
  }

这里有两个问题:(1)像上面提到的场景一样,我是否因为初始化而使DatabaseFuture<Database>可以为空? (2) Added a cast to an expression (non-downcast)是什么意思?

建议的空安全更改:

static Database? _database;

  Future<Database?>  get database async {
    if (_database != null) return _database;
    // lazily instantiate the db the first time it is accessed
    _database = await _initDatabase();
    return _database;
  }

  // some code omitted

 Future<bool >  checkBookmark(String? someId) async {
    Database  db = await (instance.database as FutureOr<Database>);
    var bookmarked = await db.query(table,
        where: 'someId = ?',
        whereArgs: [someId]);
    return bookmarked.isEmpty ? false : true;
  }

非常感谢任何对此的帮助,我希望这些答案也能帮助其他新编码人员迁移到 null 安全!

回答我自己的一个或多个问题...免责声明:进行以下更改后我没有出现 Dart 错误,但我的代码无法正常工作,因此解决方案可能不是 100% 正确。

案例一:从JSON解析到dart

自从 quicktype is null-safe yet, I have opted for json_serializable 做我的 JSON 到飞镖转换。

案例 2:初始化变量

我没有将 > 声明为可为空,而是向我的 FutureBuilder 添加了一个类型。

child: FutureBuilder<List<VocabList>>(

案例三:从 MaterialPageRoute 传递数据

我不再需要将变量声明为可为空。

案例 4:http 数据库助手 class

没有使用可为 null 的构造函数,而是使用了 late

late 字符串 someName;

案例5:sqflite数据库助手class

新代码在这里:

  // Initialise the database.
  // Only allow a single open connection to the database.
  static Database? _database; // Added ? for null-safety

  Future<Database> get database async {
    if (_database != null)
      return _database as Future<Database>; // Added 'as type' for null-safety
    _database = await _initDatabase();
    return _database as Future<Database>; // Added 'as type' for null-safety
  }

如果有人发现任何错误或知道我如何才能做得更好,请告诉我!