# Dart Null Safety

> 运用 Dart 健全空安全机制（Sound Null Safety），消除空引用异常并编写类型安全的代码。

- **Type:** Skill
- **Install:** `agentstack add skill-python51888-studydart-skills-dart-null-safety`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Python51888](https://agentstack.voostack.com/s/python51888)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** BSD-3-Clause
- **Upstream author:** [Python51888](https://github.com/Python51888)
- **Source:** https://github.com/Python51888/StudyDart-Skills/tree/main/skills/dart-null-safety
- **Website:** https://zread.ai/Python51888/StudyDart-Skills

## Install

```sh
agentstack add skill-python51888-studydart-skills-dart-null-safety
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 掌握 Dart 空安全

## Contents
- [核心原则](#核心原则)
- [类型系统中的可空性](#类型系统中的可空性)
- [空安全的语法与操作符](#空安全的语法与操作符)
- [流程分析与类型提升](#流程分析与类型提升)
- [Late 变量与延迟初始化](#late-变量与延迟初始化)
- [核心库的空安全变更](#核心库的空安全变更)
- [Dart 3 空安全迁移](#dart-3-空安全迁移)
- [Workflow: 实现空安全的 Dart 代码](#workflow-实现空安全的-dart-代码)
- [Examples](#examples)

## 核心原则

Dart 的空安全基于两条核心原则：

**默认不可空** — 除非将变量显式声明为可空，否则它一定是非空的类型。`String` 类型的变量必须包含一个字符串，不能为 `null`。

**完全可靠** — Dart 的类型系统保证：如果一个表达式有不可空的静态类型，那么它在运行时永远不会为 `null`。类型检查器会在编译时捕获所有潜在的空引用错误。

## 类型系统中的可空性

空安全从根本上改变了 Dart 的类型层级：

- `Null` 类型不再是所有类型的子类。`null` 只能赋值给 `Null` 或可空类型（如 `String?`）。
- 所有类型默认为非空。`int`、`String`、`List` 等不能包含 `null`。
- 可空类型通过在类型后加 `?` 声明：`String?` 等价于 `String | Null`。
- `Object` 不再是最顶层类型，改用 `Object?` 表示可以接受任何值的类型。
- 新的底层类型 `Never` 表示永远不会成功返回的表达式（如 `throw`）。

```dart
int a = 42;           // 非空，不能赋值为 null
int? b = null;         // 可空，可以赋值为 null
String? name;          // 默认为 null

// 可空类型只能访问 Null 类也定义的方法
// 即 toString()、==、hashCode
```

## 空安全的语法与操作符

### Null-aware 操作符

| 操作符 | 用法 | 说明 |
|--------|------|------|
| `?.` | `obj?.method()` | 如果 `obj` 为 null 则短路返回 null |
| `??` | `a ?? b` | 如果 `a` 为 null 则使用 `b` |
| `??=` | `a ??= b` | 如果 `a` 为 null 则赋值 `b` |
| `!` | `obj!.method()` | 断言 `obj` 不为 null（运行时检查） |

```dart
String? name;
print(name?.length);       // null（不会崩溃）
print(name?.length ?? 0);  // 0

List? list;
list?.add(1);              // 安全调用

// 空值断言：确信值不为 null 时使用
String? input = 'hello';
print(input!.length);      // 5
```

### 空值断言操作符 `!`

使用 `!` 将可空类型强制转换为非空类型。仅在确信值不为 null 时使用，否则运行时抛出 `TypeError`：

```dart
String? nullable = getValue();
String nonNullable = nullable!;  // 运行时检查
```

## 流程分析与类型提升

Dart 的流程分析可以在判断 `null` 检查后自动将可空类型提升为非空类型：

```dart
String? name;

if (name != null) {
  print(name.length);  // 类型提升：name 现在是 String
}

// 提前返回模式也支持类型提升
int getLength(String? value) {
  if (value == null) return 0;
  return value.length;  // 这里 value 已提升为 String
}
```

### 赋值分析

编译器跟踪变量的赋值状态，确保非空变量在使用前已被初始化：

```dart
int result;                    // OK，不需要立即初始化
if (condition) {
  result = 1;
} else {
  result = 2;
}
print(result);                 // OK，所有路径都已赋值
```

### Never 类型

`Never` 类型用于永远不会返回的函数，帮助流程分析：

```dart
Never throwError(String msg) {
  throw ArgumentError(msg);
}

int parse(String? value) {
  if (value == null) throwError('Cannot be null');
  return int.parse(value);     // value 已提升为 String
}
```

## Late 变量与延迟初始化

`late` 关键字用于延迟初始化非空变量：

```dart
class Database {
  late final Connection connection;  // 延迟初始化
  
  void init() {
    connection = Connection.open();  // 运行时检查
  }
}

// Late 变量在首次访问时初始化
late String greeting = _computeGreeting();  // 懒加载
```

**使用场景**：
- 在构造函数体中初始化 `final` 字段
- 懒加载开销大的计算
- 依赖注入模式

## 核心库的空安全变更

Dart 3 对核心库做了以下重要变更：

| 变更 | 说明 |
|------|------|
| `Map.[]` 返回可空 | `map[key]` 返回 `V?`，因为 key 可能不存在 |
| 移除 `List()` 构造 | 使用 `[]` 字面量或 `List.filled()` |
| `Iterator.current` 可空 | 迭代前后访问返回 `null` |
| `num.parse()` 移除 `onError` | 使用 `tryParse()` 代替 |
| 移除隐式转换 | 不再自动将 `dynamic` 转为具体类型 |

```dart
var map = {'a': 1, 'b': 2};
int? value = map['c'];           // null，而不是异常
print(map['c'] ?? 0);            // 0

// int.parse 替代方案
int? parsed = int.tryParse('abc'); // null
```

## Dart 3 空安全迁移

从 Dart 3 开始，空安全是强制性的，不再需要选择启用。

**迁移步骤**：

1. 更新 `pubspec.yaml` 的 SDK 约束：
```yaml
environment:
  sdk: '>=3.0.0 = 2.12.0）。

3. 运行分析检查：
```bash
dart analyze
```

4. 修复常见问题：
   - 可选参数的默认值使用 `=` 而非 `:`。
   - `class` 用作 mixin 时必须标记为 `mixin class`。
   - `switch` 语句的 `continue` 只能指向循环标签。

## Workflow: 实现空安全的 Dart 代码

### Task Progress

- [ ] **Step 1: 确定类型的可空性。** 分析变量是否可能为 null。默认使用非空类型。
- [ ] **Step 2: 声明可空类型。** 对可能为 null 的变量添加 `?`（如 `String?`、`int?`）。
- [ ] **Step 3: 处理可空值。** 使用 `??` 提供默认值，使用 `?.` 安全调用，使用 `if (x != null)` 进行类型提升。
- [ ] **Step 4: 使用 late 延迟初始化。** 对需要延迟赋值的非空字段使用 `late`。
- [ ] **Step 5: 使用 ! 断言非空。** 仅当你确信值不为 null 时使用 `!`。
- [ ] **Step 6: 运行分析器。** 执行 `dart analyze` 检查空安全错误。
- [ ] **Step 7: 运行测试。** 确保所有测试通过，没有空引用异常。
- [ ] **Step 8: Feedback Loop。** 审查分析器输出 → 修复空安全警告 → 重新运行测试 → 重复直到全部通过。

### 条件逻辑

- **如果创建 NEW 代码：** 所有变量默认非空，只在需要时加 `?`。优先使用 `final`。
- **如果编辑 EXISTING 代码：** 逐步迁移，从叶子类开始向上。先修复非空字段的初始化。
- **如果一个字段总是非空但构造函数中不能初始化：** 使用 `late final`。
- **如果一个函数可能无返回值：** 返回可空类型或用 `Never` 表示不返回。
- **如果与旧版库交互：** 使用 `!` 断言或 `??` 提供默认值处理可能的 null。

## Examples

### 完整示例：空安全的数据模型

```dart
class User {
  final String name;           // 非空
  final String? email;         // 可空
  final int? age;              // 可空
  late final DateTime createdAt;  // 延迟赋值
  
  User({required this.name, this.email, this.age}) {
    createdAt = DateTime.now();
  }
  
  String get displayName => name;
  
  String get emailOrFallback => email ?? 'No email provided';
  
  // 工厂方法：从 JSON 安全解析
  factory User.fromJson(Map json) {
    return User(
      name: json['name'] as String,
      email: json['email'] as String?,   // 可能为 null
      age: json['age'] as int?,
    );
  }
  
  Map toJson() {
    return {
      'name': name,
      if (email != null) 'email': email,
      if (age != null) 'age': age,
    };
  }
}

void main() {
  var user = User.fromJson({
    'name': 'Alice',
    'email': null,
  });
  
  print(user.emailOrFallback);   // "No email provided"
  print(user.name.length);       // 5（安全）
}
```

### 空安全与集合操作

```dart
List items = ['a', null, 'b', null];

// 过滤 null 值
List nonNull = items.whereType().toList();
print(nonNull);  // ['a', 'b']

// 或使用 where
var filtered = items.where((e) => e != null).toList();

// Map 访问
Map scores = {'Alice': 10, 'Bob': null};
int? bobScore = scores['Bob'];
int aliceScore = scores['Alice'] ?? 0;  // 10
int charlieScore = scores['Charlie'] ?? 0;  // 0（key 不存在）
```

### 类型提升的高级用法

```dart
String? fetchData() => DateTime.now().second % 2 == 0 ? 'data' : null;

void processData() {
  final data = fetchData();
  
  // 模式1: if-null 后返回
  if (data == null) {
    print('No data available');
    return;
  }
  // data 在这里已提升为 String
  print(data.toUpperCase());
}

// 使用 Never 辅助函数
Never fail(String msg) => throw StateError(msg);

int? tryParse(String value) {
  if (int.tryParse(value) case final parsed?) {
    return parsed;
  }
  return null;
}
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Python51888](https://github.com/Python51888)
- **Source:** [Python51888/StudyDart-Skills](https://github.com/Python51888/StudyDart-Skills)
- **License:** BSD-3-Clause
- **Homepage:** https://zread.ai/Python51888/StudyDart-Skills

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-python51888-studydart-skills-dart-null-safety
- Seller: https://agentstack.voostack.com/s/python51888
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
