← 返回首页

🎯 Dart 语言基础

一、变量与类型

1.1 变量声明

Dart 提供了多种变量声明方式,每种都有不同的用途和语义。理解它们的区别是写出高质量 Dart 代码的基础。

// var - 类型自动推断,可重新赋值
var name = 'Flutter';     // 推断为 String
name = 'Dart';              // ✅ 可以重新赋值
// name = 123;              // ❌ 不能改变类型

// final - 运行时常量,只能赋值一次
final createdAt = DateTime.now();  // 运行时确定值
final String title = '学习Dart';  // 可以显式指定类型

// const - 编译时常量,必须在编译期确定
const pi = 3.14159;          // 编译时就确定的值
const maxItems = 100;        // 整数常量
// const now = DateTime.now(); // ❌ 运行时值不能用 const

// late - 延迟初始化
late String description;     // 稍后初始化
late final String config = loadConfig();  // 懒加载,首次访问时才执行
提示:优先使用 final,只有需要重新赋值时才用 varconst 用于编译时就能确定的值,如配置常量。late 常用于需要延迟初始化或在构造函数体中初始化的场景。

1.2 基本数据类型

Dart 是强类型语言,所有变量都有类型。以下是最常用的内置类型:

// 数字类型
int age = 25;                // 整数
double price = 19.99;         // 浮点数
num value = 42;               // int 或 double 的父类型

// 字符串
String greeting = 'Hello Dart';
String multiLine = '''
这是一个
多行字符串
''';
String interpolation = '年龄是 $age, 明年 ${age + 1}';

// 布尔值
bool isActive = true;

// 动态类型 - 慎用!
dynamic anything = 'text';
anything = 42;               // ✅ dynamic 可以改变类型
anything.noSuchMethod();      // 编译通过,运行时报错

// Object - 所有类的基类
Object obj = 'text';
// obj.length;                // ❌ 编译错误,Object 没有 length

// 类型转换
int parsed = int.parse('42');
double d = 42.toDouble();
String s = 42.toString();

1.3 空安全(Null Safety)

Dart 的空安全是其类型系统的核心特性,帮助你在编译期捕获空引用错误。

// 默认不可为 null
String name = 'Dart';
// name = null;               // ❌ 编译错误

// ? 声明可空类型
String? nickname;             // 默认值为 null
print(nickname);              // 输出: null

// ! 断言非空(确定不为 null 时使用)
String sure = nickname!;      // 如果 nickname 为 null 会抛异常

// ?? 空合并运算符
String display = nickname ?? '匿名用户';  // nickname 为 null 时用默认值

// ??= 空值赋值
nickname ??= '默认昵称';     // 仅在 null 时赋值

// ?. 安全调用运算符
int? length = nickname?.length;  // nickname 为 null 时返回 null

// 实际应用示例
String getUserName(Map<String, dynamic> json) {
  return json['name']?.toString() ?? '未知用户';
}

// 类型提升(Flow Analysis)
void process(String? input) {
  if (input == null) return;
  // 此处 input 自动提升为 String(非空)
  print(input.length);        // ✅ 安全访问
}
最佳实践:尽量避免使用 ! 操作符,它会跳过空安全检查。优先使用 ?? 提供默认值,或使用 if 判断让编译器自动进行类型提升。

二、集合类型

2.1 List(列表)

List 是 Dart 中最常用的有序集合,类似于其他语言的数组。

// 创建列表
var fruits = ['苹果', '香蕉', '橙子'];  // 类型推断为 List<String>
List<int> numbers = [1, 2, 3, 4, 5];
var empty = <String>[];          // 空的类型列表

// 常用操作
fruits.add('葡萄');             // 添加元素
fruits.addAll(['芒果', '荔枝']);  // 添加多个
fruits.insert(0, '西瓜');      // 在指定位置插入
fruits.remove('香蕉');          // 删除元素
fruits.removeAt(0);             // 删除指定索引

// 函数式操作(非常重要!Flutter 中大量使用)
var lengths = fruits.map((f) => f.length).toList();
var longFruits = fruits.where((f) => f.length > 1).toList();
var hasApple = fruits.any((f) => f == '苹果');
var allLong = fruits.every((f) => f.length > 0);
var total = numbers.reduce((a, b) => a + b);  // 求和
var first = fruits.firstWhere((f) => f.startsWith('苹'));

// 不可变列表
final fixed = List.unmodifiable([1, 2, 3]);
const constList = [1, 2, 3];  // 编译时常量列表

2.2 Set(集合)与 Map(映射)

// Set - 无序不重复集合
var tags = {'Flutter', 'Dart', 'Mobile'};
tags.add('Flutter');    // 不会重复添加
tags.add('Web');

// Set 运算
var a = {1, 2, 3};
var b = {2, 3, 4};
print(a.union(b));          // {1, 2, 3, 4} 并集
print(a.intersection(b));   // {2, 3} 交集
print(a.difference(b));     // {1} 差集

// Map - 键值对
var user = {
  'name': '张三',
  'age': 25,
  'email': 'zhang@example.com',
};

// Map 操作
user['phone'] = '13800138000';      // 添加/修改
user.remove('email');                // 删除
var hasName = user.containsKey('name');

// 遍历 Map
user.forEach((key, value) {
  print('$key: $value');
});

// Map 转换
var upperKeys = user.map((k, v) =>
  MapEntry(k.toUpperCase(), v));

2.3 展开运算符与集合操作

// 展开运算符 ...
var base = [1, 2, 3];
var extended = [0, ...base, 4];  // [0, 1, 2, 3, 4]

// 空感知展开运算符 ...?
List<int>? maybeNull;
var safe = [0, ...?maybeNull];   // [0],不会报错

// 集合 if(在 Flutter UI 构建中非常实用)
var isLoggedIn = true;
var menu = [
  '首页',
  '发现',
  if (isLoggedIn) '个人中心',    // 条件添加
  if (!isLoggedIn) '登录',
];

// 集合 for
var items = [1, 2, 3];
var doubled = [
  for (var i in items) i * 2,   // [2, 4, 6]
];

// 组合使用 - Flutter 中的典型场景
var widgets = [
  'Header',
  for (var item in items)
    'Item: $item',
  if (items.length > 2) '查看更多...',
];
注意:集合 if 和集合 for 在 Flutter 的 Widget 树构建中大量使用,比如动态生成列表项或条件显示某些 Widget。务必掌握这些语法。

三、函数

3.1 函数定义与箭头函数

Dart 中函数是一等对象,可以赋值给变量、作为参数传递、从函数返回。

// 标准函数定义
int add(int a, int b) {
  return a + b;
}

// 箭头函数(函数体只有一个表达式时使用)
int multiply(int a, int b) => a * b;

// 函数作为变量
var greet = (String name) => '你好,$name!';
print(greet('Flutter'));   // 你好,Flutter!

// 函数作为参数
void execute(int Function(int, int) operation, int x, int y) {
  print('结果: ${operation(x, y)}');
}
execute(add, 3, 4);        // 结果: 7
execute(multiply, 3, 4);   // 结果: 12

3.2 参数类型

// 必需位置参数
String fullName(String first, String last) {
  return '$first $last';
}

// 可选位置参数 - 用 [] 包裹
String introduce(String name, [int? age, String city = '未知']) {
  var result = '我是$name';
  if (age != null) result += ',$age岁';
  result += ',来自$city';
  return result;
}
introduce('张三');                // 我是张三,来自未知
introduce('李四', 25, '北京');  // 我是李四,25岁,来自北京

// 命名参数 - 用 {} 包裹(Flutter 中最常用!)
void createUser({
  required String name,     // 必需命名参数
  int age = 0,               // 带默认值的命名参数
  String? email,             // 可选命名参数
}) {
  print('创建用户: $name, $age, $email');
}

// 调用时参数名清晰可读
createUser(name: '王五', age: 30, email: 'wang@test.com');
createUser(name: '赵六');   // age 默认 0, email 为 null

3.3 闭包与 typedef

// 闭包 - 函数可以捕获外部变量
Function makeCounter() {
  int count = 0;
  return () {
    count++;
    return count;
  };
}
var counter = makeCounter();
print(counter());  // 1
print(counter());  // 2
print(counter());  // 3

// 闭包在 Flutter 中的实际应用
List<Function()> buildCallbacks(List<String> items) {
  return items.map((item) {
    return () => print('点击了: $item');
  }).toList();
}

// typedef - 定义函数类型别名
typedef Comparator<T> = int Function(T a, T b);
typedef OnChanged<T> = void Function(T value);

// 使用 typedef
void sortList(List<int> list, Comparator<int> compare) {
  list.sort(compare);
}
sortList([3, 1, 2], (a, b) => a - b);  // 升序排列
Flutter 重点:Flutter 中的 Widget 构造函数大量使用命名参数。回调函数(如 onPressedonChanged)是闭包的典型应用。建议牢记命名参数的 required 语法。

四、控制流

4.1 条件与循环

// if-else
var score = 85;
if (score >= 90) {
  print('优秀');
} else if (score >= 60) {
  print('及格');
} else {
  print('不及格');
}

// 三元表达式
var status = score >= 60 ? '通过' : '未通过';

// switch 语句
var command = 'open';
switch (command) {
  case 'open':
    print('打开');
    break;
  case 'close':
    print('关闭');
    break;
  default:
    print('未知命令');
}

// for 循环
for (var i = 0; i < 5; i++) {
  print('索引: $i');
}

// for-in 遍历
var colors = ['红', '绿', '蓝'];
for (var color in colors) {
  print(color);
}

// while 和 do-while
var n = 5;
while (n > 0) {
  print(n--);
}

4.2 Dart 3 模式匹配(Pattern Matching)

Dart 3 引入了强大的模式匹配功能,让代码更简洁、更安全。

// switch 表达式(Dart 3 新语法)
String describe(int score) => switch (score) {
  >= 90 => '优秀',
  >= 80 => '良好',
  >= 60 => '及格',
  _    => '不及格',
};

// 解构赋值
var point = (3, 4);           // Record
var (x, y) = point;            // 解构: x=3, y=4

// 命名字段解构
var user = (name: '张三', age: 25);
var (:name, :age) = user;      // name='张三', age=25

// List 解构
var [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3,4,5]

// Map 解构
var {'name': userName, 'age': userAge} = {'name': '李四', 'age': 30};

// if-case 模式匹配
var data = {'type': 'success', 'value': 42};
if (data case {'type': 'success', 'value': int v}) {
  print('成功,值为: $v');     // v 自动绑定且类型为 int
}

// switch 匹配复杂类型
void handleResponse(Object response) {
  switch (response) {
    case String s:
      print('字符串: $s');
    case int n when n > 0:
      print('正整数: $n');
    case [int x, int y]:
      print('坐标: ($x, $y)');
    case {'error': String msg}:
      print('错误: $msg');
    default:
      print('未知类型');
  }
}
Dart 3 提示:模式匹配是 Dart 3 最重要的新特性之一。switch 表达式(用 =>)比传统 switch 语句更简洁,且编译器会检查是否覆盖了所有情况。

五、面向对象

5.1 类与构造函数

Dart 是纯面向对象语言,所有值都是对象。类是组织代码的核心方式。

class User {
  // 实例属性
  final String name;
  int age;
  String? _email;          // _ 开头表示私有

  // 默认构造函数 - 使用语法糖简化赋值
  User(this.name, this.age, [this._email]);

  // 命名构造函数
  User.guest() : name = '访客', age = 0;

  User.fromJson(Map<String, dynamic> json)
    : name = json['name'] as String,
      age = json['age'] as int;

  // getter 和 setter
  String? get email => _email;
  set email(String? value) {
    if (value != null && value.contains('@')) {
      _email = value;
    }
  }

  // 计算属性
  bool get isAdult => age >= 18;

  // 方法
  String greet() => '大家好,我是$name';

  // 重写 toString
  @override
  String toString() => 'User($name, $age)';
}

// 使用
var user = User('张三', 25, 'zhang@test.com');
var guest = User.guest();
var jsonUser = User.fromJson({'name': '李四', 'age': 30});

5.2 工厂构造函数与 const 构造函数

// 工厂构造函数 - 可以返回缓存实例或子类
class Logger {
  static final Map<String, Logger> _cache = {};
  final String name;

  // 私有构造函数
  Logger._internal(this.name);

  // 工厂构造函数 - 实现单例模式
  factory Logger(String name) {
    return _cache.putIfAbsent(
      name,
      () => Logger._internal(name),
    );
  }

  void log(String msg) => print('[$name] $msg');
}

// 两个变量指向同一个实例
var log1 = Logger('APP');
var log2 = Logger('APP');
print(identical(log1, log2));  // true

// const 构造函数 - 编译时常量对象
class Point {
  final double x;
  final double y;

  const Point(this.x, this.y);

  // const 构造函数中所有字段必须是 final
  double get distance => (x * x + y * y);
}

// const 对象在编译时创建,相同值共享实例
const p1 = Point(1, 2);
const p2 = Point(1, 2);
print(identical(p1, p2));    // true,共享同一实例

5.3 继承与抽象类

// 抽象类 - 不能直接实例化
abstract class Shape {
  // 抽象方法 - 子类必须实现
  double area();
  double perimeter();

  // 普通方法 - 子类可以继承
  String describe() => '面积: ${area().toStringAsFixed(2)}';
}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);

  @override
  double area() => 3.14159 * radius * radius;

  @override
  double perimeter() => 2 * 3.14159 * radius;
}

class Rectangle extends Shape {
  final double width, height;
  Rectangle(this.width, this.height);

  @override
  double area() => width * height;

  @override
  double perimeter() => 2 * (width + height);
}

5.4 Mixin 与扩展方法

// Mixin - 代码复用机制(不同于继承)
mixin Printable {
  void printInfo() => print(toString());
}

mixin Serializable {
  Map<String, dynamic> toJson();

  String serialize() => toJson().toString();
}

// 使用 with 关键字混入
class Product with Printable, Serializable {
  final String name;
  final double price;

  Product(this.name, this.price);

  @override
  Map<String, dynamic> toJson() => {
    'name': name,
    'price': price,
  };

  @override
  String toString() => '$name (¥$price)';
}

// 扩展方法 - 给现有类添加功能
extension StringX on String {
  // 检查是否为有效邮箱
  bool get isEmail =>
    RegExp(r'^[\w-\.]+@[\w-]+\.\w+$').hasMatch(this);

  // 首字母大写
  String get capitalize =>
    isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}';
}

// 使用扩展方法
print('test@mail.com'.isEmail);   // true
print('hello'.capitalize);        // Hello

// 增强枚举(Dart 2.17+)
enum Priority {
  low(1, '低'),
  medium(2, '中'),
  high(3, '高'),
  urgent(4, '紧急');

  final int level;
  final String label;
  const Priority(this.level, this.label);

  // 枚举可以有方法和 getter
  bool get isUrgent => level >= 3;

  @override
  String toString() => 'Priority.$name($label)';
}

var p = Priority.high;
print(p.label);        // 高
print(p.isUrgent);     // true
注意:Mixin 是 Dart 中非常灵活的代码复用方式,Flutter 框架中大量使用(如 TickerProviderStateMixin)。扩展方法可以让你给任何类添加方法,非常适合编写工具方法。增强枚举让枚举类型更加强大实用。

六、泛型

6.1 泛型类与泛型函数

泛型允许你编写类型安全且可复用的代码,避免重复编写类似逻辑。

// 泛型类 - 通用的结果包装器
class Result<T> {
  final T? data;
  final String? error;
  final bool isSuccess;

  Result.success(this.data)
    : error = null, isSuccess = true;

  Result.failure(this.error)
    : data = null, isSuccess = false;

  // 泛型方法 - 转换包装的数据类型
  Result<R> map<R>(R Function(T) transform) {
    if (isSuccess && data != null) {
      return Result.success(transform(data as T));
    }
    return Result.failure(error);
  }
}

// 使用泛型类
var result = Result<int>.success(42);
var strResult = result.map((n) => '数字是 $n');  // Result<String>

var error = Result<String>.failure('网络错误');

// 泛型函数
T firstOrDefault<T>(List<T> list, T defaultValue) {
  return list.isNotEmpty ? list.first : defaultValue;
}

var num1 = firstOrDefault([1, 2, 3], 0);      // int 类型
var str1 = firstOrDefault(<String>[], '空');   // String 类型

6.2 类型约束

// 使用 extends 约束泛型类型
class NumberList<T extends num> {
  final List<T> _items = [];

  void add(T value) => _items.add(value);

  // 因为 T 约束为 num,可以安全调用 num 的方法
  T get max => _items.reduce((a, b) => (a > b ? a : b) as T);

  double get average {
    if (_items.isEmpty) return 0;
    var sum = _items.fold<num>(0, (prev, e) => prev + e);
    return sum / _items.length;
  }
}

var intList = NumberList<int>();
intList.add(10);
intList.add(20);
print(intList.average);       // 15.0

// 泛型实际应用 - 仓库模式
abstract class Repository<T> {
  Future<List<T>> getAll();
  Future<T?> getById(String id);
  Future<void> save(T entity);
  Future<void> delete(String id);
}

// 具体实现
class UserRepository implements Repository<User> {
  @override
  Future<List<User>> getAll() async {
    // 从数据库或 API 获取用户列表
    return [];
  }
  // ... 其他方法
  @override
  Future<User?> getById(String id) async => null;
  @override
  Future<void> save(User entity) async {}
  @override
  Future<void> delete(String id) async {}
}
提示:泛型在 Flutter 中无处不在。ListView.builderFutureBuilder<T>ValueNotifier<T> 等都依赖泛型。掌握泛型是理解 Flutter 源码的前提。

七、异步编程

7.1 Future 与 async/await

异步编程是 Dart 的核心特性之一。网络请求、文件读写、数据库操作等都是异步的。

// 返回 Future 的函数
Future<String> fetchUserName(int userId) async {
  // 模拟网络请求延迟
  await Future.delayed(Duration(seconds: 2));
  return '用户_$userId';
}

// 使用 async/await
Future<void> loadData() async {
  try {
    var name = await fetchUserName(1);
    print('获取到: $name');
  } catch (e) {
    print('出错了: $e');
  } finally {
    print('加载完成');
  }
}

// 并行执行多个异步任务
Future<void> loadAll() async {
  // 同时发起多个请求,等待全部完成
  var results = await Future.wait([
    fetchUserName(1),
    fetchUserName(2),
    fetchUserName(3),
  ]);
  print(results);  // [用户_1, 用户_2, 用户_3]
}

// Future 链式调用
fetchUserName(1)
  .then((name) => print('用户: $name'))
  .catchError((e) => print('错误: $e'))
  .whenComplete(() => print('完成'));

// 带超时的 Future
var name = await fetchUserName(1)
  .timeout(Duration(seconds: 5),
    onTimeout: () => '超时默认值');

7.2 Stream(数据流)

// 创建 Stream
Stream<int> countStream(int max) async* {
  for (var i = 1; i <= max; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;  // 逐个产出值
  }
}

// 监听 Stream
Future<void> listenToCount() async {
  // 方式1: await for
  await for (var count in countStream(5)) {
    print('计数: $count');
  }

  // 方式2: listen
  countStream(3).listen(
    (data) => print('数据: $data'),
    onError: (e) => print('错误: $e'),
    onDone: () => print('完成'),
  );
}

// StreamController - 手动控制 Stream
import 'dart:async';

class EventBus {
  final _controller = StreamController<String>.broadcast();

  Stream<String> get events => _controller.stream;

  void emit(String event) => _controller.add(event);

  void dispose() => _controller.close();
}

// Stream 转换操作
countStream(10)
  .where((n) => n.isEven)        // 过滤偶数
  .map((n) => '偶数: $n')        // 转换
  .take(3)                       // 只取前3个
  .listen(print);

7.3 Isolate 与 compute()

import 'dart:isolate';
import 'package:flutter/foundation.dart';

// compute() - Flutter 提供的简便方式在隔离线程中执行计算
// 适用于耗时的纯计算任务,避免阻塞 UI 线程

// 必须是顶层函数或静态方法
int heavyComputation(int n) {
  // 模拟耗时计算(如大数据解析、图片处理)
  var sum = 0;
  for (var i = 0; i < n; i++) {
    sum += i;
  }
  return sum;
}

// 使用 compute
Future<void> runHeavyTask() async {
  // 在独立 Isolate 中执行,不会卡 UI
  var result = await compute(heavyComputation, 1000000);
  print('计算结果: $result');
}

// 直接使用 Isolate(更灵活但更复杂)
Future<void> useIsolate() async {
  // Dart 2.19+ Isolate.run 简化写法
  var result = await Isolate.run(() {
    // 这段代码在独立 Isolate 中执行
    return heavyComputation(1000000);
  });
  print('Isolate 结果: $result');
}

// JSON 解析实际示例
List<Map<String, dynamic>> parseJsonList(String jsonStr) {
  // 假设这是大量 JSON 数据的解析
  import 'dart:convert';
  return (jsonDecode(jsonStr) as List).cast();
}

// 在 Isolate 中解析大 JSON
Future<List<Map<String, dynamic>>> fetchAndParse() async {
  var jsonStr = await fetchFromNetwork();  // 网络请求
  return await compute(parseJsonList, jsonStr);  // 隔离线程解析
}
重要:在 Flutter 中,UI 运行在主 Isolate 上。耗时超过 16ms 的计算(如大 JSON 解析、图片处理)应该放在 compute() 或单独的 Isolate 中执行,否则会导致界面卡顿。async/await 不会创建新线程,它只是异步等待。

八、高级特性(Dart 3)

8.1 Records(记录)

Records 是 Dart 3 引入的轻量级不可变数据结构,适合返回多个值或临时数据组合。

// 位置 Record
(int, String) getUserInfo() {
  return (25, '张三');
}
var info = getUserInfo();
print(info.$1);  // 25 (位置从 $1 开始)
print(info.$2);  // 张三

// 命名 Record
({String name, int age, String email}) getUser() {
  return (name: '李四', age: 30, email: 'li@test.com');
}
var user = getUser();
print(user.name);   // 李四
print(user.age);    // 30

// 混合 Record(位置 + 命名)
(int, int, {String label}) point = (10, 20, label: '原点');

// Record 解构
var (age, name) = getUserInfo();  // age=25, name='张三'

// Record 的相等性比较(按值比较)
var r1 = (1, 'a');
var r2 = (1, 'a');
print(r1 == r2);    // true(值相等即相等)

// 实际应用 - 替代简单的数据类
Future<({List<String> items, bool hasMore})> fetchPage(int page) async {
  // 一次返回多个相关的值
  return (items: ['项目1', '项目2'], hasMore: true);
}

8.2 Sealed 类与穷尽匹配

// sealed 类 - 限制子类范围,支持穷尽匹配
sealed class AppState {}

class Loading extends AppState {}

class Loaded extends AppState {
  final List<String> data;
  Loaded(this.data);
}

class Error extends AppState {
  final String message;
  Error(this.message);
}

// 穷尽匹配 - 编译器确保处理所有情况
String renderState(AppState state) => switch (state) {
  Loading()       => '加载中...',
  Loaded(data: var items) => '共 ${items.length} 条数据',
  Error(message: var msg) => '错误: $msg',
  // 不需要 default!编译器知道已覆盖所有情况
};

// 状态管理实际应用
sealed class NetworkResult<T> {}

class Success<T> extends NetworkResult<T> {
  final T data;
  Success(this.data);
}

class Failure<T> extends NetworkResult<T> {
  final String error;
  final int? statusCode;
  Failure(this.error, [this.statusCode]);
}

// 在 Widget 中使用
Widget buildContent(NetworkResult<User> result) => switch (result) {
  Success(data: var user) => Text('欢迎, ${user.name}'),
  Failure(error: var msg, statusCode: 404) => Text('未找到: $msg'),
  Failure(error: var msg) => Text('错误: $msg'),
};

8.3 更多模式匹配技巧

// 对象模式 - 匹配对象的属性
class Rect {
  final double width, height;
  Rect(this.width, this.height);
  double get area => width * height;
}

String classifyRect(Rect r) => switch (r) {
  Rect(width: var w, height: var h) when w == h => '正方形 ($w x $h)',
  Rect(area: > 100)   => '大矩形',
  Rect(area: > 50)    => '中矩形',
  _                    => '小矩形',
};

// guard 子句 when
String checkAge(Object value) => switch (value) {
  int n when n < 0   => '无效年龄',
  int n when n < 18  => '未成年',
  int n when n < 60  => '成年人',
  int _              => '老年人',
  _                  => '不是数字',
};

// 逻辑模式组合
String describeNumber(int n) => switch (n) {
  0                 => '零',
  1 || 2 || 3       => '小数字',       // 或模式
  >= 4 && <= 10     => '中等数字',     // 且模式
  > 10              => '大数字',
  _                 => '负数',
};
进阶提示:Sealed 类 + 模式匹配是 Dart 3 中管理应用状态的最佳实践。编译器会强制你处理所有可能的状态,大大减少遗漏导致的 bug。这在 Flutter 状态管理中特别有用。

九、实践练习

练习一:数据模型与 JSON 序列化

实现一个完整的用户数据模型,包含 JSON 序列化、数据验证和工厂构造函数。

// 练习:补全以下代码并运行测试
class UserModel {
  final String id;
  final String name;
  final String email;
  final int age;
  final List<String> tags;

  const UserModel({
    required this.id,
    required this.name,
    required this.email,
    required this.age,
    this.tags = const [],
  });

  // 工厂构造:从 JSON 创建
  factory UserModel.fromJson(Map<String, dynamic> json) {
    return UserModel(
      id: json['id'] as String,
      name: json['name'] as String,
      email: json['email'] as String,
      age: json['age'] as int,
      tags: (json['tags'] as List?)
        ?.map((e) => e as String).toList() ?? [],
    );
  }

  // 转换为 JSON
  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'email': email,
    'age': age,
    'tags': tags,
  };

  // copyWith - 不可变对象的更新模式
  UserModel copyWith({
    String? name,
    String? email,
    int? age,
    List<String>? tags,
  }) {
    return UserModel(
      id: id,
      name: name ?? this.name,
      email: email ?? this.email,
      age: age ?? this.age,
      tags: tags ?? this.tags,
    );
  }

  // 数据验证
  bool get isValid =>
    name.isNotEmpty &&
    email.contains('@') &&
    age > 0 && age < 150;

  @override
  String toString() => 'UserModel($name, $email)';
}

// 测试代码
void main() {
  var user = UserModel.fromJson({
    'id': '001',
    'name': '张三',
    'email': 'zhang@test.com',
    'age': 25,
    'tags': ['Flutter', 'Dart'],
  });

  print(user.isValid);                     // true
  print(user.toJson());                     // JSON Map
  print(user.copyWith(name: '李四').name);  // 李四
}

练习二:异步任务管理器

实现一个简单的异步任务管理器,支持并发控制和结果收集。

// 练习:实现一个带并发控制的任务执行器
class TaskRunner<T> {
  final int maxConcurrent;
  final List<Future<T> Function()> _tasks = [];
  final List<T> _results = [];
  int _running = 0;

  TaskRunner({this.maxConcurrent = 3});

  // 添加任务
  void addTask(Future<T> Function() task) {
    _tasks.add(task);
  }

  // 执行所有任务(带并发控制)
  Future<List<T>> runAll() async {
    final completer = Completer<List<T>>();
    var index = 0;

    void runNext() async {
      if (index >= _tasks.length) {
        if (_running == 0) {
          completer.complete(_results);
        }
        return;
      }

      final currentIndex = index++;
      _running++;

      try {
        var result = await _tasks[currentIndex]();
        _results.add(result);
      } catch (e) {
        print('任务 $currentIndex 失败: $e');
      } finally {
        _running--;
        runNext();  // 完成一个就启动下一个
      }
    }

    // 启动初始批次
    var initial = _tasks.length < maxConcurrent
        ? _tasks.length
        : maxConcurrent;
    for (var i = 0; i < initial; i++) {
      runNext();
    }

    if (_tasks.isEmpty) return [];
    return completer.future;
  }
}

// 使用示例
void main() async {
  var runner = TaskRunner<String>(maxConcurrent: 2);

  for (var i = 0; i < 5; i++) {
    runner.addTask(() async {
      await Future.delayed(Duration(seconds: 1));
      return '任务$i完成';
    });
  }

  var results = await runner.runAll();
  print(results);  // [任务0完成, 任务1完成, ...]
}

练习三:Sealed 类实现状态机

使用 Dart 3 的 Sealed 类实现一个购物车状态管理。

// 练习:使用 sealed 类管理购物车状态
sealed class CartState {}

class CartEmpty extends CartState {}

class CartLoading extends CartState {}

class CartLoaded extends CartState {
  final List<CartItem> items;
  CartLoaded(this.items);

  double get totalPrice =>
    items.fold(0, (sum, item) => sum + item.price * item.quantity);

  int get itemCount =>
    items.fold(0, (sum, item) => sum + item.quantity);
}

class CartError extends CartState {
  final String message;
  CartError(this.message);
}

// 购物车项目
class CartItem {
  final String name;
  final double price;
  final int quantity;

  const CartItem({
    required this.name,
    required this.price,
    this.quantity = 1,
  });

  CartItem copyWith({int? quantity}) =>
    CartItem(name: name, price: price, quantity: quantity ?? this.quantity);
}

// 渲染购物车状态(穷尽匹配)
String renderCart(CartState state) => switch (state) {
  CartEmpty()     => '🛒 购物车为空,去逛逛吧!',
  CartLoading()   => '⏳ 加载中...',
  CartLoaded(items: []) => '🛒 购物车为空',
  CartLoaded(items: var items, totalPrice: var total)
    => '🛍 ${items.length} 种商品,合计: ¥${total.toStringAsFixed(2)}',
  CartError(message: var msg) => '❌ $msg',
};

// 测试
void main() {
  var states = [
    CartEmpty(),
    CartLoading(),
    CartLoaded([
      CartItem(name: 'Flutter 实战', price: 79.0, quantity: 2),
      CartItem(name: 'Dart 编程', price: 59.0),
    ]),
    CartError('网络连接失败'),
  ];

  for (var state in states) {
    print(renderCart(state));
  }
  // 输出:
  // 🛒 购物车为空,去逛逛吧!
  // ⏳ 加载中...
  // 🛍 2 种商品,合计: ¥217.00
  // ❌ 网络连接失败
}
练习建议:建议在 DartPad 上运行这些代码。尝试修改和扩展功能,比如给 UserModel 添加密码验证、给 TaskRunner 添加重试机制、给购物车添加优惠券功能等。

目录