Dart 入门

最后更新:2026-03-05 本文面向已有 Python 或 JavaScript 基础、想通过 Dart 进入 Flutter 开发的初学者。Dart 是谷歌开发的强类型、面向对象的编译型语言,是 Flutter入门(/flutter-ru-men/) 的唯一开发语言。 1. 基础语法(#%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95) 2. 流程控制(#%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6) 3. 函数(#%E5%87%BD%E6%95%B0) 4. 面向对象(#%E9%9D%A2%E5

分享

最后更新:2026-03-05

官方文档:https://dart.dev/guides
适用版本:Dart 3.x(2026-05-07 核实)

本文面向已有 Python 或 JavaScript 基础、想通过 Dart 进入 Flutter 开发的初学者。Dart 是谷歌开发的强类型、面向对象的编译型语言,是 Flutter入门 的唯一开发语言。


目录

  1. 基础语法
  2. 流程控制
  3. 函数
  4. 面向对象
  5. 异步编程
  6. 库与包
  7. Dart 3 新特性
  8. 综合实战:命令行 Todo 应用

基础语法

变量与类型

Dart 是强类型语言,但支持类型推断。与 Python 的动态类型和 JavaScript 的弱类型不同,Dart 在编译期就能捕获类型错误。

// var:类型推断,一旦赋值类型固定
var name = 'Alice';      // 推断为 String
var age = 25;            // 推断为 int
// name = 42;            // 编译错误:不能把 int 赋给 String

// 显式类型声明
String city = 'Beijing';
int count = 100;
double price = 9.99;

// final:运行时常量,只能赋值一次(类似 JavaScript 的 const)
final greeting = 'Hello';
// greeting = 'Hi';      // 错误:final 变量不能重新赋值

// const:编译期常量,值必须在编译时已知
const pi = 3.14159;
const maxCount = 100;

// Python 对比:Python 没有 final/const,所有变量都可重新赋值
// JS 对比:JS 的 let 对应 var,JS 的 const 类似 Dart 的 final(运行时)

var / final / const 区别对比:

关键字 类型推断 可重新赋值 赋值时机
var 任何时候
final 否(一次) 运行时
const 编译期
Type name 否(显式) 任何时候

内置类型

// int:整数,无大小限制(Web 端有精度限制)
int x = 42;
int hex = 0xFF;

// double:64位浮点数
double y = 3.14;
double scientific = 1.5e2;   // 150.0

// num:int 和 double 的父类
num n = 10;
n = 3.14;   // 合法

// String:Unicode 字符串,单引号双引号均可
String s1 = 'hello';
String s2 = "world";

// bool:只有 true 和 false,不像 JS 存在隐式真值转换
bool flag = true;
// if (1) {}    // Dart 编译错误,JS 中合法

// dynamic:放弃类型检查,类似 Python 动态类型(不推荐滥用)
dynamic anything = 42;
anything = 'now a string';   // 合法

// Object:所有 Dart 类的基类(可空版本是 Object?)
Object obj = 'hello';

空安全(Null Safety)

Dart 2.12 引入空安全,是 Dart 最重要的特性之一。默认情况下,变量不能为 null,必须显式声明可空类型。

// 非空类型:不能赋值为 null
String name = 'Alice';
// name = null;   // 编译错误

// 可空类型:在类型后加 ?
String? nullableName = null;    // 合法
nullableName = 'Bob';           // 也合法

// ? 条件成员访问:若为 null 则返回 null,不抛异常
// 类似 JavaScript 的 ?.(可选链)
int? length = nullableName?.length;

// ?? 空合并运算符:左侧为 null 时返回右侧
// 类似 JavaScript 的 ?? 和 Python 的 or(但更严格)
String displayName = nullableName ?? '匿名用户';

// ??= 空合并赋值:仅当变量为 null 时赋值
String? user;
user ??= '默认用户';   // user 现在是 '默认用户'

// ! 非空断言:告诉编译器"我确信这里不是 null"
// 若实际为 null 则运行时抛出 Null check operator used on a null value
String definitelyNotNull = nullableName!;

// late:延迟初始化,声明时不赋值,使用前必须赋值
late String lateVar;
// print(lateVar);   // 运行时错误:LateInitializationError
lateVar = '现在初始化了';
print(lateVar);       // 正常

空安全操作符汇总:

操作符 含义 示例
? 声明可空类型 String? name
?. 条件成员访问 name?.length
?.. 条件级联 obj?..method()
?? 空合并 name ?? '默认'
??= 空合并赋值 name ??= '默认'
! 非空断言 name!

字符串

// 字符串插值:$ 插入变量,${} 插入表达式
String name = 'Dart';
int version = 3;
print('Hello, $name!');               // Hello, Dart!
print('版本:${version + 1}');        // 版本:4
print('大写:${name.toUpperCase()}'); // 大写:DART

// Python 对比:Python 用 f'Hello, {name}'
// JS 对比:JS 用 `Hello, ${name}`(反引号模板字符串)

// 多行字符串:三引号(与 Python 相同)
String multiLine = '''
第一行
第二行
第三行
''';

// 原始字符串:r 前缀,不处理转义(类似 Python 的 r'')
String path = r'C:\Users\dog\Documents';   // \ 不转义
String regex = r'\d+';

// 常用字符串方法
String str = '  Hello, Dart!  ';

print(str.trim());                    // 'Hello, Dart!'(去除首尾空白)
print(str.toUpperCase());             // '  HELLO, DART!  '
print(str.toLowerCase());             // '  hello, dart!  '
print(str.contains('Dart'));          // true
print(str.startsWith('  Hello'));     // true
print(str.replaceAll('Dart', 'World')); // '  Hello, World!  '
print(str.split(', '));               // ['  Hello', 'Dart!  ']
print(str.trim().length);             // 12
print('hello'.padLeft(10));           // '     hello'
print('hello'.padRight(10, '-'));     // 'hello-----'

// 字符串拼接
String a = 'Hello' + ' ' + 'World';  // 用 + 拼接
String b = 'Ha' * 3;                  // 'HaHaHa'(类似 Python)

集合类型

List(列表)

// 创建 List(类似 Python 的 list,JavaScript 的 Array)
List<int> numbers = [1, 2, 3, 4, 5];
var names = <String>['Alice', 'Bob', 'Charlie'];  // 类型参数写法
var mixed = [1, 'hello', true];     // 推断为 List<Object>

// 空 List
List<int> empty = [];
List<int> empty2 = List.empty(growable: true);

List 常用方法参数表:

方法 参数 返回类型 说明
add(value) value: E void 末尾追加元素
addAll(iterable) iterable: Iterable<E> void 追加多个元素
insert(index, value) index: int, value: E void 在指定位置插入
remove(value) value: Object? bool 删除第一个匹配元素
removeAt(index) index: int E 删除指定索引的元素
indexOf(value) value: Object? int 返回第一次出现的索引
contains(value) value: Object? bool 是否包含该元素
sort([compare]) compare: int Function(E, E)? void 排序(原地)
sublist(start, [end]) start: int, end: int? List<E> 切片
join([separator]) separator: String String 拼接为字符串
where(test) test: bool Function(E) Iterable<E> 过滤
map(convert) convert: T Function(E) Iterable<T> 映射
forEach(action) action: void Function(E) void 遍历
var fruits = ['apple', 'banana', 'cherry'];

// 增删改查
fruits.add('date');                  // ['apple', 'banana', 'cherry', 'date']
fruits.insert(1, 'avocado');         // ['apple', 'avocado', 'banana', ...]
fruits.remove('banana');             // 删除第一个 'banana'
fruits.removeAt(0);                  // 删除索引 0

// 访问
print(fruits[0]);                    // 第一个元素
print(fruits.last);                  // 最后一个元素
print(fruits.length);                // 长度

// 遍历(for...in 类似 Python 的 for...in)
for (var fruit in fruits) {
  print(fruit);
}

// 展开运算符(类似 JS 的 ...)
var list1 = [1, 2, 3];
var list2 = [0, ...list1, 4];        // [0, 1, 2, 3, 4]

// 集合 if 和集合 for(Dart 特有)
bool showExtra = true;
var items = [
  'item1',
  'item2',
  if (showExtra) 'item3',            // 条件包含
  for (var i in [4, 5]) 'item$i',   // 循环展开
];
// items: ['item1', 'item2', 'item3', 'item4', 'item5']

Map(字典)

// 创建 Map(类似 Python 的 dict,JavaScript 的 Object/Map)
Map<String, int> scores = {'Alice': 95, 'Bob': 87, 'Charlie': 92};
var config = <String, dynamic>{
  'host': 'localhost',
  'port': 8080,
  'debug': true,
};

Map 常用方法参数表:

方法/属性 参数 返回类型 说明
map[key] key: K V? 取值(不存在返回 null)
map[key] = value key: K, value: V 设置值
containsKey(key) key: Object? bool 是否包含键
containsValue(value) value: Object? bool 是否包含值
remove(key) key: Object? V? 删除并返回对应值
putIfAbsent(key, ifAbsent) key: K, ifAbsent: V Function() V 键不存在时插入
keys Iterable<K> 所有键
values Iterable<V> 所有值
entries Iterable<MapEntry<K,V>> 键值对
forEach(action) action: void Function(K, V) void 遍历
var scores = {'Alice': 95, 'Bob': 87};

// 访问
print(scores['Alice']);              // 95
print(scores['Unknown']);            // null(不存在返回 null,不抛异常)

// 安全访问
int? score = scores['Unknown'];
print(score ?? 0);                   // 0

// 遍历(Python 对比:for k, v in d.items())
scores.forEach((key, value) {
  print('$key: $value');
});

// 用 entries 遍历
for (var entry in scores.entries) {
  print('${entry.key} 得了 ${entry.value} 分');
}

Set(集合)

// 创建 Set:无序,元素唯一(类似 Python 的 set)
Set<String> tags = {'dart', 'flutter', 'mobile'};
var nums = <int>{1, 2, 3, 2, 1};    // 实际:{1, 2, 3}

// 注意:{} 创建的是 Map,空 Set 必须用类型声明
var emptySet = <String>{};           // Set
var emptyMap = {};                   // Map<dynamic, dynamic>

// 常用操作
tags.add('android');
tags.remove('mobile');
print(tags.contains('dart'));        // true

// 集合运算
var a = {1, 2, 3, 4};
var b = {3, 4, 5, 6};
print(a.union(b));                   // {1, 2, 3, 4, 5, 6}
print(a.intersection(b));            // {3, 4}
print(a.difference(b));              // {1, 2}

运算符

// 算术运算符
print(10 ~/ 3);    // 3,整除(Python 的 //,JS 没有原生整除)
print(10 % 3);     // 1,取余
print(2.pow(3));   // 注意:Dart 没有 ** 运算符,用 pow() 函数

import 'dart:math';
print(pow(2, 10)); // 1024

// 级联运算符 ..(方法链,返回调用者本身)
// 类似 JavaScript 的链式调用,但更简洁
var list = []
  ..add(1)
  ..add(2)
  ..add(3);        // list 是 [1, 2, 3]

// 不用级联的写法:
var list2 = [];
list2.add(1);
list2.add(2);
list2.add(3);

// 类型测试运算符
var obj = 'hello';
print(obj is String);     // true(类似 Python 的 isinstance)
print(obj is! int);       // true(is not 的简写)

// 类型转换
num n = 3.7;
int i = n.toInt();        // 3(截断,不四舍五入)
double d = i.toDouble();  // 3.0
String s = i.toString();  // '3'
int parsed = int.parse('42');         // 42
double parsedD = double.parse('3.14'); // 3.14
int? tryParsed = int.tryParse('abc'); // null(不抛异常)

流程控制

if/else

int score = 85;

// 基本 if/else(语法与 JS 相同)
if (score >= 90) {
  print('优秀');
} else if (score >= 75) {
  print('良好');
} else {
  print('需要努力');
}

// 三元表达式
String result = score >= 60 ? '及格' : '不及格';

// 注意:Dart 没有 JS 的隐式类型转换
// if (1) {}   // 编译错误,必须是 bool

switch(含 Dart 3 模式匹配)

// 传统 switch(类似 Java/JavaScript)
String day = 'Monday';
switch (day) {
  case 'Monday':
  case 'Tuesday':
    print('工作日');
    break;
  case 'Saturday':
  case 'Sunday':
    print('周末');
    break;
  default:
    print('未知');
}

// Dart 3:switch 表达式(更简洁,类似函数式风格)
String dayType = switch (day) {
  'Saturday' || 'Sunday' => '周末',
  'Monday' || 'Tuesday' || 'Wednesday' || 'Thursday' || 'Friday' => '工作日',
  _ => '未知',                    // _ 是默认分支
};

// Dart 3:模式匹配(Pattern Matching)
Object value = 42;
switch (value) {
  case int n when n > 0:
    print('正整数:$n');
  case String s:
    print('字符串:$s');
  case null:
    print('空值');
  default:
    print('其他');
}

循环

// for 循环(经典 C 风格)
for (int i = 0; i < 5; i++) {
  print(i);   // 0 1 2 3 4
}

// for...in:遍历可迭代对象(类似 Python 的 for...in)
var fruits = ['apple', 'banana', 'cherry'];
for (var fruit in fruits) {
  print(fruit);
}

// while 循环
int count = 0;
while (count < 3) {
  print(count);
  count++;
}

// do...while:至少执行一次
int n = 0;
do {
  print(n);
  n++;
} while (n < 3);

// break 和 continue
for (int i = 0; i < 10; i++) {
  if (i == 3) continue;   // 跳过 3
  if (i == 7) break;      // 遇到 7 停止
  print(i);               // 0 1 2 4 5 6
}

// 带标签的 break(跳出多层循环,Python 没有此特性)
outer:
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    if (i == 1 && j == 1) break outer;  // 直接跳出外层循环
    print('$i,$j');
  }
}

函数

基本函数声明

// 返回类型 函数名(参数列表) { 函数体 }
int add(int a, int b) {
  return a + b;
}

// void:无返回值(类似 Python 不写 return 或 return None)
void greet(String name) {
  print('Hello, $name!');
}

// 返回类型可推断(但建议显式声明)
String getVersion() => 'Dart 3.0';   // 箭头函数

// 调用
print(add(3, 4));   // 7
greet('Alice');      // Hello, Alice!

可选参数

// 位置可选参数:用 [] 包裹,类型必须可空或有默认值
String greet(String name, [String? title, int count = 1]) {
  String prefix = title != null ? '$title ' : '';
  return '$prefix$name' * count;
}

print(greet('Alice'));               // Alice
print(greet('Bob', 'Mr.'));          // Mr. Bob
print(greet('Hi', null, 3));         // HiHiHi

// 命名参数:用 {} 包裹,调用时指定参数名(推荐,Flutter 中大量使用)
void createUser({
  required String name,              // required:调用时必须传
  int age = 0,                       // 有默认值,可选
  String? email,                     // 可空,可选
}) {
  print('$name, $age, $email');
}

// 调用命名参数函数
createUser(name: 'Alice');                           // Alice, 0, null
createUser(name: 'Bob', age: 25, email: '[email protected]'); // Bob, 25, [email protected]
createUser(age: 30, name: 'Charlie');                // 命名参数顺序可任意

// Python 对比:Python 用 def f(a, b=0, **kwargs)
// JS 对比:JS 用解构参数 function f({ name, age = 0 } = {})

命名参数规则总结:

参数写法 是否必须传 说明
{required String name} 调用时必须提供
{String? name} 可不传,默认为 null
{String name = '默认'} 可不传,有默认值
[String? name] 位置可选,按顺序传

箭头函数与匿名函数

// 箭头函数:单表达式函数的简写
int square(int x) => x * x;
bool isEven(int n) => n % 2 == 0;

// 匿名函数(Lambda):没有名字的函数
var multiply = (int a, int b) => a * b;
print(multiply(3, 4));    // 12

// 完整写法的匿名函数
var greet = (String name) {
  return 'Hello, $name!';
};

// 作为参数传递(高阶函数)
var numbers = [1, 2, 3, 4, 5];
numbers.forEach((n) => print(n));   // 打印每个数字

高阶函数

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// map:映射,返回新的 Iterable(类似 Python 的 map(),JS 的 Array.map())
var squares = numbers.map((n) => n * n).toList();
print(squares);   // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

// where:过滤(类似 Python 的 filter(),JS 的 Array.filter())
var evens = numbers.where((n) => n % 2 == 0).toList();
print(evens);     // [2, 4, 6, 8, 10]

// fold:折叠/归约(类似 Python 的 reduce(),JS 的 Array.reduce())
int sum = numbers.fold(0, (acc, n) => acc + n);
print(sum);       // 55

// reduce:类似 fold,但无初始值
int product = numbers.reduce((a, b) => a * b);
print(product);   // 3628800

// any:是否存在满足条件的元素(类似 Python 的 any())
bool hasEven = numbers.any((n) => n % 2 == 0);
print(hasEven);   // true

// every:是否全部满足条件(类似 Python 的 all())
bool allPositive = numbers.every((n) => n > 0);
print(allPositive);  // true

// take / skip:取前 N 个 / 跳过前 N 个
var first3 = numbers.take(3).toList();   // [1, 2, 3]
var skip3 = numbers.skip(3).toList();    // [4, 5, 6, 7, 8, 9, 10]

// 链式调用
var result = numbers
    .where((n) => n % 2 == 0)           // 保留偶数
    .map((n) => n * n)                  // 求平方
    .where((n) => n > 20)               // 大于 20 的
    .toList();
print(result);    // [36, 64, 100]

面向对象

基本类

class Person {
  // 属性(实例变量)
  String name;
  int age;
  String? email;   // 可空属性

  // 普通构造函数
  Person(this.name, this.age);   // this.name 语法自动赋值
  // 等价于:
  // Person(String name, int age) {
  //   this.name = name;
  //   this.age = age;
  // }

  // 方法
  void introduce() {
    print('我是 $name,今年 $age 岁');
  }

  // 重写 toString(类似 Python 的 __str__)
  @override
  String toString() => 'Person($name, $age)';
}

void main() {
  var p = Person('Alice', 25);   // 不需要 new 关键字(Dart 2+)
  p.introduce();                 // 我是 Alice,今年 25 岁
  print(p);                      // Person(Alice, 25)
}

命名构造函数与工厂构造函数

class Point {
  double x;
  double y;

  // 普通构造函数
  Point(this.x, this.y);

  // 命名构造函数:创建特殊实例(Python 对比:classmethod)
  Point.origin() : x = 0, y = 0;            // 初始化列表
  Point.fromJson(Map<String, double> json)
      : x = json['x']!,
        y = json['y']!;

  // 工厂构造函数:可以返回缓存对象或子类实例
  static final Map<String, Point> _cache = {};
  factory Point.cached(double x, double y) {
    final key = '$x,$y';
    return _cache.putIfAbsent(key, () => Point(x, y));
  }

  double distanceTo(Point other) {
    import 'dart:math';
    return sqrt(pow(x - other.x, 2) + pow(y - other.y, 2));
  }

  @override
  String toString() => 'Point($x, $y)';
}

void main() {
  var origin = Point.origin();          // (0, 0)
  var p1 = Point.fromJson({'x': 3.0, 'y': 4.0});   // (3, 4)
  var p2 = Point.cached(1.0, 2.0);     // 从缓存获取或新建
}

getter 与 setter

class Circle {
  double _radius;   // _ 前缀表示私有(仅在同一文件/库内私有)

  Circle(this._radius);

  // getter:像属性一样访问
  double get radius => _radius;
  double get area => 3.14159 * _radius * _radius;
  double get circumference => 2 * 3.14159 * _radius;

  // setter:像属性一样赋值
  set radius(double value) {
    if (value < 0) throw ArgumentError('半径不能为负');
    _radius = value;
  }
}

void main() {
  var c = Circle(5.0);
  print(c.radius);          // 5.0(调用 getter)
  print(c.area);            // 78.53975
  c.radius = 10.0;          // 调用 setter
  // c.radius = -1;         // 抛出 ArgumentError
}

继承与多态

// 基类
class Animal {
  String name;

  Animal(this.name);

  // 可被子类重写的方法
  void speak() {
    print('$name 发出声音');
  }

  String get description => '动物:$name';
}

// 子类(extends 类似 Python 的类继承,JS 的 extends)
class Dog extends Animal {
  String breed;

  // super 调用父类构造函数(类似 Python 的 super().__init__())
  Dog(String name, this.breed) : super(name);

  // @override 重写父类方法(Python 不需要此注解)
  @override
  void speak() {
    print('$name 汪汪叫!');
  }

  @override
  String get description => '${super.description},品种:$breed';

  void fetch() {
    print('$name 捡球!');
  }
}

class Cat extends Animal {
  Cat(String name) : super(name);

  @override
  void speak() {
    print('$name 喵喵叫~');
  }
}

void main() {
  List<Animal> animals = [Dog('旺财', '哈士奇'), Cat('咪咪'), Dog('来福', '金毛')];

  for (var animal in animals) {
    animal.speak();    // 多态:根据实际类型调用对应方法
  }

  // 类型检查与转换
  var first = animals[0];
  if (first is Dog) {
    first.fetch();     // 自动类型收窄(Smart Cast),不需要强制转换
  }

  // 强制转换
  var dog = animals[0] as Dog;
  dog.fetch();
}

抽象类、接口与混入

// 抽象类:不能直接实例化(类似 Python 的 ABC)
abstract class Shape {
  double get area;           // 抽象 getter,子类必须实现
  double get perimeter;

  void describe() {          // 普通方法,子类可直接继承
    print('面积:$area,周长:$perimeter');
  }
}

// Dart 中没有专门的 interface 关键字
// 每个类都隐式定义了接口,用 implements 实现(必须实现所有方法)
abstract class Drawable {
  void draw();
}

abstract class Resizable {
  void resize(double factor);
}

// implements:实现多个接口(类似 Java 的 implements)
// 注意:implements 必须实现接口的所有成员,包括普通方法
class Rectangle extends Shape implements Drawable, Resizable {
  double width;
  double height;

  Rectangle(this.width, this.height);

  @override
  double get area => width * height;

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

  @override
  void draw() {
    print('绘制矩形 ${width}x$height');
  }

  @override
  void resize(double factor) {
    width *= factor;
    height *= factor;
  }
}

// mixin:混入,向类添加功能(类似 Python 的 Mixin 模式)
// mixin 不能有构造函数
mixin Logger {
  void log(String message) {
    print('[LOG] $message');
  }
}

mixin Serializable {
  Map<String, dynamic> toJson();   // 抽象方法,要求使用方实现
  String serialize() => toJson().toString();
}

// with 使用混入
class User with Logger, Serializable {
  String name;
  int age;

  User(this.name, this.age);

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

  void save() {
    log('保存用户:$name');     // 来自 Logger
    print(serialize());          // 来自 Serializable
  }
}

枚举

// 普通枚举
enum Direction { north, south, east, west }

void main() {
  var dir = Direction.north;
  print(dir);           // Direction.north
  print(dir.name);      // north(Dart 2.15+)
  print(dir.index);     // 0

  switch (dir) {
    case Direction.north:
      print('向北');
    default:
      print('其他方向');
  }
}

// 增强枚举(Dart 2.17+):枚举值可以有属性和方法
enum Planet {
  mercury(3.303e+23, 2.4397e6),
  venus(4.869e+24, 6.0518e6),
  earth(5.976e+24, 6.37814e6);

  // 属性
  final double mass;
  final double radius;

  // 常量构造函数
  const Planet(this.mass, this.radius);

  // 方法
  double get surfaceGravity {
    const g = 6.67430e-11;
    return g * mass / (radius * radius);
  }
}

void main() {
  print(Planet.earth.surfaceGravity);    // 约 9.8
  for (var p in Planet.values) {
    print('${p.name}: ${p.surfaceGravity.toStringAsFixed(2)}');
  }
}

泛型

// 泛型类(类似 Python 的 Generic[T],Java/TS 的泛型)
class Box<T> {
  T value;

  Box(this.value);

  T getValue() => value;

  @override
  String toString() => 'Box<$T>($value)';
}

void main() {
  var intBox = Box<int>(42);
  var strBox = Box<String>('hello');
  print(intBox);    // Box<int>(42)
  print(strBox);    // Box<String>(hello)
}

// 泛型函数
T first<T>(List<T> list) {
  if (list.isEmpty) throw StateError('列表为空');
  return list.first;
}

print(first([1, 2, 3]));          // 1
print(first(['a', 'b', 'c']));    // a

// 泛型约束
class NumberBox<T extends num> {   // T 必须是 num 或其子类
  T value;
  NumberBox(this.value);
  T doubled() => (value * 2) as T;
}

异步编程

Future

Future 代表一个异步操作的最终结果,类似 JavaScript 的 Promise。

import 'dart:async';

// 创建 Future
Future<String> fetchData() {
  return Future.delayed(
    Duration(seconds: 2),
    () => '从服务器获取的数据',
  );
}

// Future.value:立即完成的 Future
Future<int> getNumber() => Future.value(42);

// Future.error:立即失败的 Future
Future<int> getError() => Future.error('出错了!');

// then/catchError/whenComplete 链式调用(类似 JS 的 .then().catch().finally())
void fetchWithChain() {
  fetchData()
      .then((data) {
        print('获取成功:$data');
        return data.toUpperCase();   // 可以继续 then
      })
      .then((upperData) {
        print('处理后:$upperData');
      })
      .catchError((error) {
        print('错误:$error');
      })
      .whenComplete(() {
        print('无论成功失败都执行(类似 finally)');
      });
}

// Future.wait:等待多个 Future 同时完成(类似 JS 的 Promise.all())
Future<void> fetchMultiple() async {
  var results = await Future.wait([
    Future.delayed(Duration(seconds: 1), () => '结果1'),
    Future.delayed(Duration(seconds: 2), () => '结果2'),
    Future.delayed(Duration(seconds: 1), () => '结果3'),
  ]);
  print(results);   // ['结果1', '结果2', '结果3'],约 2 秒后输出
}

// Future.any:任意一个完成就返回(类似 JS 的 Promise.race())
Future<void> raceExample() async {
  var first = await Future.any([
    Future.delayed(Duration(seconds: 3), () => '慢的'),
    Future.delayed(Duration(seconds: 1), () => '快的'),
  ]);
  print(first);   // '快的'
}

async/await

// async/await:让异步代码写起来像同步代码(与 JS/Python 的 async/await 几乎相同)
Future<String> getUserName(int id) async {
  // 模拟网络请求
  await Future.delayed(Duration(seconds: 1));
  return 'User_$id';
}

Future<void> main() async {
  print('开始');

  // await:等待 Future 完成,函数必须标记为 async
  String name = await getUserName(1);
  print('用户名:$name');

  print('结束');
}

// 异步错误处理(try/catch 与同步代码相同)
Future<void> safeOperation() async {
  try {
    var result = await Future.error('网络超时');
    print(result);
  } catch (e) {
    print('捕获错误:$e');
  } finally {
    print('清理资源');
  }
}

// 并发:同时启动多个异步操作(不等待)
Future<void> concurrentExample() async {
  // 错误写法:顺序等待,总耗时 = 各任务之和
  var r1 = await Future.delayed(Duration(seconds: 2), () => '任务1');
  var r2 = await Future.delayed(Duration(seconds: 2), () => '任务2');
  // 总耗时约 4 秒

  // 正确写法:并发启动,总耗时 = 最长任务
  var f1 = Future.delayed(Duration(seconds: 2), () => '任务1');
  var f2 = Future.delayed(Duration(seconds: 2), () => '任务2');
  var results = await Future.wait([f1, f2]);
  // 总耗时约 2 秒
}

Stream

Stream 是一系列异步事件,类似 Node.js 的 Stream 或 RxJS 的 Observable。

import 'dart:async';

// 创建简单 Stream
Stream<int> countStream(int max) async* {
  for (int i = 0; i < max; i++) {
    await Future.delayed(Duration(milliseconds: 500));
    yield i;   // yield 产生一个值(类似 Python 的 generator)
  }
}

// 监听 Stream
void listenToStream() {
  var stream = countStream(5);

  stream.listen(
    (data) => print('收到:$data'),
    onError: (error) => print('错误:$error'),
    onDone: () => print('Stream 完成'),
  );
}

// StreamController:手动控制 Stream
void streamControllerExample() {
  var controller = StreamController<String>();

  // 订阅
  controller.stream.listen((data) {
    print('收到消息:$data');
  });

  // 发送数据
  controller.add('消息1');
  controller.add('消息2');
  controller.add('消息3');
  controller.close();   // 关闭 Stream
}

// await for:在 async 函数中遍历 Stream
Future<void> processStream() async {
  await for (var value in countStream(5)) {
    print('处理:$value');
  }
  print('所有数据处理完毕');
}

// Stream 转换操作
void streamTransform() {
  Stream.fromIterable([1, 2, 3, 4, 5])
      .where((n) => n % 2 == 0)      // 过滤偶数
      .map((n) => n * n)             // 求平方
      .listen(print);                // 输出:4 16
}

// 广播 Stream:可以被多个监听器订阅
void broadcastStreamExample() {
  var controller = StreamController<int>.broadcast();

  controller.stream.listen((d) => print('监听器1:$d'));
  controller.stream.listen((d) => print('监听器2:$d'));

  controller.add(1);   // 两个监听器都会收到
  controller.close();
}

Flutter 中会大量使用 StreamBuilder 组件来根据 Stream 数据更新 UI,详见 Flutter入门


库与包

import 语句

// 导入 Dart 内置库(dart: 前缀)
import 'dart:math';           // 数学函数
import 'dart:async';          // 异步支持
import 'dart:io';             // 文件、网络 IO
import 'dart:convert';        // JSON、UTF-8 编解码

// 导入 pub.dev 包(在 pubspec.yaml 中声明依赖后才能用)
import 'package:http/http.dart';
import 'package:path/path.dart';

// 导入本地文件
import 'utils/helper.dart';
import '../models/user.dart';

// as:给库起别名,避免命名冲突
import 'dart:math' as math;
print(math.pi);               // 使用 math.pi 而不是直接 pi

// show:只导入指定成员
import 'dart:math' show pi, sqrt, pow;

// hide:导入除指定成员外的所有内容
import 'dart:math' hide Random;

// 导出:让本文件的内容对外可见
export 'src/model.dart';
export 'src/utils.dart' show helper;

pubspec.yaml

# pubspec.yaml:Dart/Flutter 项目的配置文件(类似 Python 的 pyproject.toml 或 JS 的 package.json)
name: my_app
description: 我的 Dart 应用
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'   # Dart SDK 版本约束

dependencies:
  # pub.dev 包
  http: ^1.1.0             # ^ 表示兼容性版本(>=1.1.0 <2.0.0)
  path: ^1.8.3
  json_annotation: ^4.8.0

dev_dependencies:          # 只在开发时使用的包
  test: ^1.24.0
  lints: ^2.1.0
  build_runner: ^2.4.6

# Flutter 特有配置
flutter:
  assets:
    - assets/images/
  fonts:
    - family: MyFont
      fonts:
        - asset: fonts/MyFont-Regular.ttf

常用 pub 命令:

命令 说明
dart pub get 安装 pubspec.yaml 中的依赖
dart pub add http 添加依赖并安装
dart pub remove http 移除依赖
dart pub upgrade 升级所有依赖
dart pub outdated 查看过期的依赖

Dart 3 新特性

模式匹配(Pattern Matching)

// switch 表达式(返回值)
int statusCode = 404;
String message = switch (statusCode) {
  200 => '成功',
  301 || 302 => '重定向',
  400 => '请求错误',
  404 => '未找到',
  500 => '服务器错误',
  _ => '未知状态码',
};

// 解构模式
var point = (x: 3, y: 4);   // 记录类型
var (x: px, y: py) = point;  // 解构
print('$px, $py');           // 3, 4

// 列表模式解构
var [first, second, ...rest] = [1, 2, 3, 4, 5];
print(first);   // 1
print(rest);    // [3, 4, 5]

// 类型模式
Object? value = 'hello';
if (value case String s when s.length > 3) {
  print('长字符串:$s');
}

记录类型(Records)

// Records:轻量级的不可变数据结构(类似 Python 的 namedtuple)
// 可以从函数返回多个值,不需要定义类

// 位置记录
(String, int) getNameAndAge() {
  return ('Alice', 25);
}

// 命名记录
({String name, int age}) getUser() {
  return (name: 'Bob', age: 30);
}

void main() {
  var (name, age) = getNameAndAge();   // 解构
  print('$name, $age');

  var user = getUser();
  print('${user.name}, ${user.age}');  // 命名字段访问
}

密封类(sealed class)

// sealed class:限制继承层次,配合模式匹配使用
// 所有子类必须在同一文件中定义
sealed class Shape {}

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

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

class Triangle extends Shape {
  double base, height;
  Triangle(this.base, this.height);
}

// 编译器知道 Shape 的所有子类,switch 可以穷举检查
double calculateArea(Shape shape) => switch (shape) {
  Circle c => 3.14159 * c.radius * c.radius,
  Rectangle r => r.width * r.height,
  Triangle t => 0.5 * t.base * t.height,
  // 无需 default:编译器确认已覆盖所有情况
};

综合实战:命令行 Todo 应用

下面是一个完整可运行的命令行 Todo 应用,演示类、集合、异步、文件读写。

// todo_app.dart
import 'dart:io';
import 'dart:convert';

// Todo 数据模型
class Todo {
  final int id;
  String title;
  bool isDone;
  final DateTime createdAt;

  // 命名构造函数:从 JSON 反序列化
  Todo({
    required this.id,
    required this.title,
    this.isDone = false,
    DateTime? createdAt,
  }) : createdAt = createdAt ?? DateTime.now();

  // 工厂构造函数:从 Map 创建
  factory Todo.fromJson(Map<String, dynamic> json) {
    return Todo(
      id: json['id'] as int,
      title: json['title'] as String,
      isDone: json['isDone'] as bool,
      createdAt: DateTime.parse(json['createdAt'] as String),
    );
  }

  // 序列化为 Map
  Map<String, dynamic> toJson() => {
        'id': id,
        'title': title,
        'isDone': isDone,
        'createdAt': createdAt.toIso8601String(),
      };

  @override
  String toString() {
    final status = isDone ? '[完成]' : '[待办]';
    return '$status #$id $title';
  }
}

// Todo 管理器
class TodoManager {
  final List<Todo> _todos = [];
  final String _filePath;
  int _nextId = 1;

  TodoManager(this._filePath);

  // 从文件加载(异步)
  Future<void> load() async {
    final file = File(_filePath);
    if (!await file.exists()) return;

    try {
      final content = await file.readAsString();
      final List<dynamic> jsonList = jsonDecode(content) as List;
      _todos.clear();
      for (var item in jsonList) {
        final todo = Todo.fromJson(item as Map<String, dynamic>);
        _todos.add(todo);
        if (todo.id >= _nextId) {
          _nextId = todo.id + 1;
        }
      }
      print('加载了 ${_todos.length} 条 Todo');
    } catch (e) {
      print('加载失败:$e');
    }
  }

  // 保存到文件(异步)
  Future<void> save() async {
    final file = File(_filePath);
    final jsonList = _todos.map((t) => t.toJson()).toList();
    await file.writeAsString(jsonEncode(jsonList));
  }

  // 添加 Todo
  Todo add(String title) {
    final todo = Todo(id: _nextId++, title: title);
    _todos.add(todo);
    return todo;
  }

  // 完成 Todo
  bool complete(int id) {
    final todo = _todos.where((t) => t.id == id).firstOrNull;
    if (todo == null) return false;
    todo.isDone = true;
    return true;
  }

  // 删除 Todo
  bool delete(int id) {
    final before = _todos.length;
    _todos.removeWhere((t) => t.id == id);
    return _todos.length < before;
  }

  // 获取所有 Todo
  List<Todo> getAll() => List.unmodifiable(_todos);

  // 获取待办
  List<Todo> getPending() => _todos.where((t) => !t.isDone).toList();

  // 获取已完成
  List<Todo> getCompleted() => _todos.where((t) => t.isDone).toList();

  // 统计信息
  Map<String, int> getStats() => {
        'total': _todos.length,
        'pending': getPending().length,
        'completed': getCompleted().length,
      };
}

// 打印帮助信息
void printHelp() {
  print('''
命令列表:
  add <标题>     添加 Todo
  list           查看所有 Todo
  done <id>      标记为完成
  delete <id>    删除 Todo
  stats          统计信息
  help           显示帮助
  exit           退出
''');
}

// 主函数(异步)
Future<void> main() async {
  final manager = TodoManager('todos.json');

  // 启动时加载数据
  await manager.load();

  print('=== Dart Todo 应用 ===');
  print('输入 help 查看命令');

  // 主循环
  while (true) {
    stdout.write('> ');
    final input = stdin.readLineSync()?.trim();

    if (input == null || input.isEmpty) continue;

    // 解析命令和参数
    final parts = input.split(' ');
    final command = parts[0].toLowerCase();
    final args = parts.skip(1).join(' ');

    switch (command) {
      case 'add':
        if (args.isEmpty) {
          print('用法:add <标题>');
          break;
        }
        final todo = manager.add(args);
        await manager.save();
        print('已添加:$todo');

      case 'list':
        final todos = manager.getAll();
        if (todos.isEmpty) {
          print('暂无 Todo');
        } else {
          for (var todo in todos) {
            print(todo);
          }
        }

      case 'done':
        final id = int.tryParse(args);
        if (id == null) {
          print('用法:done <id>');
          break;
        }
        if (manager.complete(id)) {
          await manager.save();
          print('已完成 #$id');
        } else {
          print('未找到 #$id');
        }

      case 'delete':
        final id = int.tryParse(args);
        if (id == null) {
          print('用法:delete <id>');
          break;
        }
        if (manager.delete(id)) {
          await manager.save();
          print('已删除 #$id');
        } else {
          print('未找到 #$id');
        }

      case 'stats':
        final stats = manager.getStats();
        print('总计:${stats['total']},待办:${stats['pending']},完成:${stats['completed']}');

      case 'help':
        printHelp();

      case 'exit':
        print('再见!');
        exit(0);

      default:
        print('未知命令:$command。输入 help 查看命令列表');
    }
  }
}

运行方法:

# 安装 Dart SDK 后直接运行
dart run todo_app.dart

# 编译为可执行文件
dart compile exe todo_app.dart -o todo_app
./todo_app

与 Python/JavaScript 速查对比

概念 Python JavaScript Dart
变量声明 x = 1 let x = 1 var x = 1
常量 (无原生支持) const x = 1 final x = 1 / const x = 1
类型标注 x: int = 1 TypeScript: let x: number = 1 int x = 1
空值 None null / undefined null
字符串插值 f'{name}' `${name}` '$name'
列表 list Array List
字典 dict Object / Map Map
集合 set Set Set
异步函数 async def f() async function f() Future<T> f() async
等待异步 await f() await f() await f()
继承 class A(B) class A extends B class A extends B
接口 ABC / Protocol (TypeScript interface) class A implements B
混入 Mixin 模式 (无原生支持) mixin M / with M
类型检查 isinstance(x, T) x instanceof T x is T
整除 // Math.floor(a/b) ~/
打印 print() console.log() print()
主函数 if __name__ == '__main__' (无,直接运行) void main()

踩坑与注意事项

1. 空安全陷阱

// 错误:可空类型直接调用方法
String? name = null;
// print(name.length);   // 运行时错误

// 正确:先检查
if (name != null) {
  print(name.length);    // 类型收窄,这里 name 是 String
}
// 或
print(name?.length);     // 返回 null
print(name?.length ?? 0); // 返回 0

2. const 与 final 的区别

// final:运行时常量,可以是运行时才知道的值
final now = DateTime.now();   // 合法

// const:编译期常量,必须是编译时已知的值
// const now2 = DateTime.now(); // 编译错误:DateTime.now() 是运行时值

3. List 的类型安全

// Dart 的泛型是具体化的(Reified),运行时保留类型信息
List<String> strings = ['a', 'b', 'c'];
// strings.add(1);   // 编译错误,不像 Python 列表可以混放任意类型

4. == 比较的是值,不是引用

// Dart 默认 == 比较值(对于基本类型)
String a = 'hello';
String b = 'hello';
print(a == b);          // true(不像 Java 需要 .equals())

// 比较引用用 identical()
print(identical(a, b)); // 可能是 true(字符串驻留)或 false

5. 异步函数返回类型

// async 函数自动包装返回值为 Future
Future<int> getNumber() async {
  return 42;   // 实际返回 Future<int>,不需要 Future.value(42)
}

// void async 函数的错误不会被捕获,应改为 Future<void>
Future<void> riskyOperation() async {
  throw Exception('出错了');
}
// 调用时必须 await 或 .catchError()

6. for...in 不能获取索引

var list = ['a', 'b', 'c'];
// 需要索引时,用 asMap() 或 enumerate
list.asMap().forEach((index, value) {
  print('$index: $value');
});

// 或者用普通 for 循环
for (int i = 0; i < list.length; i++) {
  print('$i: ${list[i]}');
}


最佳实践

优先用 finalconst:Dart 中 const 在编译期确定,final 在运行时赋值一次。能用 const 就用 const,减少运行时开销,IDE 会自动提示。

空安全(Null Safety)中用 ?.?? 替代手动判空

// 好
final name = user?.profile?.name ?? 'Guest';

// 差:冗长且容易遗漏
String name = 'Guest';
if (user != null && user.profile != null) {
  name = user.profile!.name;
}

异步优先用 async/await,避免 .thenasync/await 使控制流更线性,错误处理用 try/catch 而非 .catchError,可读性更高。

用命名构造函数区分不同初始化场景:Dart 支持多个命名构造函数,比重载参数更清晰:

class User {
  User.fromJson(Map<String, dynamic> json) : name = json['name'];
  User.anonymous() : name = 'Guest';
}

扩展方法(Extension)给现有类型添加功能:不需要继承或包装,直接为 StringList 等添加业务方法,保持调用链风格:

extension StringUtils on String {
  bool get isValidEmail => contains('@') && contains('.');
}

'[email protected]'.isValidEmail; // true

常见陷阱

陷阱:! 运算符在 null 值上抛出 Null check operator used on a null value

现象: 运行时崩溃,堆栈指向某个使用 ! 的位置。
原因:! 强制解包了实际为 null 的可空变量,编译器不报错但运行时抛出。
解决:?. 安全访问,或用 ?? 提供默认值;只在能确保不为 null 时使用 !,并加注释说明理由。

陷阱:ListforEach 中无法 break

现象: 尝试在 forEach 回调中 return 期望退出循环,但循环仍然继续。
原因: forEach 接受回调函数,return 只退出回调,不退出外层循环。
解决: 改用 for...in 循环,可以正常使用 break/continue

// 差:return 无法跳出 forEach
list.forEach((item) {
  if (item.id == targetId) return; // 只跳过当前回调,不退出
});

// 好:for...in 支持 break
for (final item in list) {
  if (item.id == targetId) break;
}

陷阱:async 函数中 Futureawait 导致异常被吞

现象: 调用异步函数后程序行为异常,但没有看到任何报错。
原因:awaitFuture 中抛出的异常不会传播到调用方,会被静默忽略(除非设置了全局 runZonedGuarded)。
解决: 所有 Future 调用都加 await,或用 unawaited(future) 明确表示有意不等待,并自行处理错误。


参见

Flutter入门
Flutter状态管理

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog