Flutter Getx 02 - 空安全、更健全的代碼

本節目標

  • 空安全意味着什麼
  • 如何遷移代碼
  • 如何禁用空安全
  • 代碼規範示例

視頻

https://www.bilibili.com/vide...git

代碼

https://github.com/ducafecat/...github

參考

正文

空安全意味着什麼

  • 默認不可空
String title = 'ducafecat';
  • type? 操做符
String? title = null;
  • value! 操做符
String? title = 'ducafecat';
String newTitle = title!;
  • value? 操做符
String? title = 'ducafecat';
bool isEmpty = title?.isEmpty();
  • value?? 操做符
String? title = 'ducafecat';
String newTitle = title ?? 'cat';
  • late 會在運行時檢查。因此請您僅在肯定它被使用前必定會被初始化的狀況下使用
late String? title;
title = 'ducafecat';
  • List、泛型
類型 集合是否可空 數據項是否可空
List<String> no no
List<String>? yes no
List<String?> no yes
List<String?>? yes yes
  • Map
類型 集合是否可空 數據項是否可空
Map<String, int> no no*
Map<String, int>? yes no*
Map<String, int?> no yes
Map<String, int?>? yes yes
* 可能返回空
// 有可能返回 null
int value = <String, int>{'one': 1}['one']; // ERROR

// 須要加上 type?
int? value = <String, int>{'one': 1}['one']; // OK

// 或者 value!
int value = <String, int>{'one': 1}['one']!; // OK

帶來的好處

  • 代碼更健康
  • 用戶體驗好
  • 運行更快
  • 編譯文件更小

開啓和遷移

  • pubspec.yaml
environment:
  sdk: ">=2.12.0 <3.0.0"
  • 遷移順序

咱們強烈建議您按順序遷移代碼,先遷移依賴關係中的處於最末端的依賴。例如,若是 C 依賴了 B,B 依賴了 A,那麼應該按照 A -> B -> C 的順序進行遷移。編程

  • 檢查依賴項目
# Dart 版本是否爲 2.12 或更高
> dart --version

# 依賴包的遷移狀態
> dart pub outdated --mode=null-safety
  • 升級依賴
# 該命令會更改您的 pubspec.yaml 文件
> dart pub upgrade --null-safety

# 升級包
> dart pub upgrade
  • 遷移工具
> dart migrate
  • 分析
> dart analyze

禁用空安全

  • cli 命令
> dart --no-sound-null-safety run
> flutter run --no-sound-null-safety
  • .vscode/launch.json
{
  // 使用 IntelliSense 瞭解相關屬性。
  // 懸停以查看現有屬性的描述。
  // 欲瞭解更多信息,請訪問: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "getx_quick_start",
      "request": "launch",
      "type": "dart",
      "program": "lib/main.dart",
      "args": ["--no-sound-null-safety"]
    }
  ]
}

範例、規範

https://dart.cn/null-safety/u...json

  • 明確處理空狀態
makeCoffee(String coffee, [String? dairy]) {
  if (dairy != null) {
    print('$coffee with $dairy');
  } else {
    print('Black $coffee');
  }
}
  • 頂層變量和靜態字段必須包含一個初始化方法。因爲它們能在程序裏的任何位置被訪問到,編譯器沒法保證它們在被使用前已被賦值。惟一保險的選項是要求其自己包含初始化表達式,以確保產生匹配的類型的值。
int topLevel = 0;

class SomeClass {
  static int staticField = 0;
}
  • 實例的字段也必須在聲明時包含初始化方法,能夠爲常見初始化形式,也能夠在實例的構造方法中進行初始化。
class SomeClass {
  int atDeclaration = 0;
  int initializingFormal;
  int initializationList;

  SomeClass(this.initializingFormal)
      : initializationList = 0;
}
  • 局部變量的靈活度最高。一個非空的變量 不必定須要 一個初始化方法。
int tracingFibonacci(int n) {
  int result;
  if (n < 2) {
    result = n;
  } else {
    result = tracingFibonacci(n - 2) + tracingFibonacci(n - 1);
  }

  print(result);
  return result;
}
  • 流程分析,在這裏 Dart 將 object 的類型從它聲明的 Object 提高到了 List。在空安全引入之前,下面的程序沒法運行。
// With (or without) null safety:
bool isEmptyList(Object object) {
  if (object is List) {
    return object.isEmpty; // <-- OK!
  } else {
    return false;
  }
}

->

// Without null safety:
bool isEmptyList(Object object) {
  if (object is! List) return false;
  return object.isEmpty;
}
  • 絕對的賦值分析
int tracingFibonacci(int n) {
  final int result;
  if (n < 2) {
    result = n;
  } else {
    result = tracingFibonacci(n - 2) + tracingFibonacci(n - 1);
  }

  print(result);
  return result;
}
  • 無用代碼的警告
String checkList(List list) {
  if (list?.isEmpty) {
    return 'Got nothing';
  }
  return 'Got something';
}
  • 懶加載的變量, late 修飾符是「在運行時而非編譯時對變量進行約束」。這就讓 late 這個詞語約等於 什麼時候 執行對變量的強制約束。
// Using null safety:
class Coffee {
  String? _temperature;

  void heat() { _temperature = 'hot'; }
  void chill() { _temperature = 'iced'; }

  String serve() => _temperature! + ' coffee';
}

->

// Using null safety:
class Coffee {
  late String _temperature;

  void heat() { _temperature = 'hot'; }
  void chill() { _temperature = 'iced'; }

  String serve() => _temperature + ' coffee';
}
  • latefinal 結合使用,與普通的 final 字段不一樣,您不須要在聲明或構造時就將其初始化。您能夠稍後在運行中的某個地方加載它。可是您只能對其進行 一次 賦值,而且它在運行時會進行校驗。
// Using null safety:
class Coffee {
  late final String _temperature;

  void heat() { _temperature = 'hot'; }
  void chill() { _temperature = 'iced'; }

  String serve() => _temperature + ' coffee';
}
  • 畢傳參數,這裏的全部參數都必須經過命名來傳遞。參數 ac 是可選的,能夠省略。參數 bd 是必需的,調用時必須傳遞。在這裏請注意,是否必需和是否可空無關。
// Using null safety:
function({int? a, required int? b, int? c, required int? d}) {}
  • 抽象字段
abstract class Cup {
  Beverage get contents;
  set contents(Beverage);
}

->

abstract class Cup {
  abstract Beverage contents;
}
  • 一些賦值計算能夠移動到靜態的初始化中。
// Initalized without values
ListQueue _context;
Float32List _buffer;
dynamic _readObject;

Vec2D(Map<String, dynamic> object) {
  _buffer = Float32List.fromList([0.0, 0.0]);
  _readObject = object['container'];
  _context = ListQueue<dynamic>();
}

->

// Initalized with values
final ListQueue _context = ListQueue<dynamic>();
final Float32List _buffer = Float32List.fromList([0.0, 0.0]);
final dynamic _readObject;

Vec2D(Map<String, dynamic> object) : _readObject = object['container'];
  • 可能返回 null 的工廠方法
factory StreamReader(dynamic data) {
  StreamReader reader;
  if (data is ByteData) {
    reader = BlockReader(data);
  } else if (data is Map) {
    reader = JSONBlockReader(data);
  }
  return reader;
}

->

factory StreamReader(dynamic data) {
  if (data is ByteData) {
    // Move the readIndex forward for the binary reader.
    return BlockReader(data);
  } else if (data is Map) {
    return JSONBlockReader(data);
  } else {
    throw ArgumentError('Unexpected type for data');
  }
}

© 貓哥api

本視頻文檔

https://ducafecat.tech/2021/0...安全

GetX Quick Start 代碼

https://github.com/ducafecat/...微信

新聞客戶端代碼

https://github.com/ducafecat/...編程語言

strapi 手冊譯文

https://getstrapi.cnide

微信討論羣 ducafecat

往期視頻

相關文章
相關標籤/搜索