入门
Hello World 与注释
main() 是每个 Dart 程序的入口。使用 // 单行注释、/* */ 块注释、/// 文档注释。文档注释支持 Markdown,被 dartdoc 用于生成 API 文档。
// Hello World in Dart
void main() {
print('Hello, World!');
}
/* Multi-line
block comment */
/// Documentation comment
/// Supports Markdown
void greet(String name) {
print('Hi, $name');
}变量:var、final、const
用 var 进行类型推断,final 用于运行时常量(只赋值一次),const 用于编译时常量。const 更严格——DateTime.now() 不能是 const,因为其值在编译时未知。值不会改变时优先用 final/const 而非 var。
var name = 'Alice'; // type inferred
String city = 'NYC'; // explicit type
final age = 30; // runtime constant
const PI = 3.14; // compile-time constant
final now = DateTime.now(); // OK: runtime value
// const time = DateTime.now(); // ERROR: not compile-time
const list = [1, 2, 3]; // const list (immutable)
final list2 = [4, 5, 6]; // final ref, mutable content空安全基础
Dart 具有健全的空安全。在类型后加 ? 使其可空(int? 可为 null;int 不能)。用 ?? 提供默认值,??= 在为空时赋值,?. 安全访问,! 断言非空。非空类型保证不为 null,在编译期消除 NullPointerException。
int value = 42; // non-nullable
int? nullableValue; // nullable (default null)
print(nullableValue); // null
print(nullableValue ?? 0); // 0 (null coalescing)
nullableValue ??= 10; // assign if null
print(nullableValue); // 10
// int x = null; // ERROR: non-nullable
String name = 'Alice';
print(name.length); // safe, no null check输出与输入
print() 输出到 stdout 并换行,只接受一个参数。用 stdout.write() 不换行。stdin.readLineSync() 从 stdin 读取一行并返回可空 String。dart:io 在 web 平台不可用。
import 'dart:io';
void main() {
print('Hello'); // stdout with newline
stdout.write('no newline'); // no trailing newline
stderr.writeln('an error'); // stderr with newline
String? input = stdin.readLineSync(); // read a line
int? n = int.tryParse(input ?? ''); // safe parse
print('You entered: $n');
}类型推断与 dynamic
var 让 Dart 在编译期推断类型(类型之后不可变)。dynamic 禁用静态类型检查——慎用。Object 是所有非空类型的父类型。用 as 转型,但优先用类型安全的检查。dynamic 是唯一允许在编译期调用未知方法的类型。
var x = 10; // inferred as int
var y = 3.14; // inferred as double
var s = 'hi'; // inferred as String
dynamic d = 10; // type can change
d = 'now string'; // OK
// d.foo(); // compiles, may fail at runtime
Object o = 'hello'; // supertype of all non-null types
// o.length; // ERROR: Object has no length
print((o as String).length); // 5 (cast)数据类型
数字类型(int、double、num)
num 是 int 和 double 的父类型。~/ 是整数除法,% 或 remainder() 取模。/ 即使对 int 也总返回 double。int 有 bitLength、toRadixString();double 有 toStringAsFixed()。在 web 平台所有数字编译为 JS 数字。
int a = 42;
double b = 3.14;
num c = 10; // num is supertype of int & double
num d = 2.71;
print(a.bitLength); // 6
print(b.toStringAsFixed(2)); // '3.14'
print(10 ~/ 3); // 3 (integer division)
print(10.remainder(3)); // 1
print(0xFF); // 255 (hex)
print(1.5e3); // 1500.0 (scientific)字符串
字符串是不可变的 UTF-16 码元序列。单引号与双引号可互换。三引号允许多行。$var 插值变量,${expr} 插值表达式。r 前缀创建原始字符串(不处理转义)。插值优于拼接。
var s1 = 'single';
var s2 = "double";
var s3 = '''multi
line string''';
var name = 'Alice';
print('Hi, $name'); // interpolation
print('Length: ${name.length}'); // expression interpolation
var raw = r'No escape: \n'; // raw string (literal)
var escaped = 'It\'s ok'; // escaped quote
print(s1[0]); // 's' (index access)布尔类型
Dart 只有两个 bool 值:true 和 false。与 JavaScript 不同,没有 truthy/falsy 强制转换——条件必须是真正的 bool。对集合和字符串用 .isNotEmpty / .isEmpty 而非依赖 truthiness。
bool isTrue = true;
bool isFalse = false;
print(!isTrue); // false
print(isTrue && isFalse); // false
print(isTrue || isFalse); // true
// Only bool is allowed in conditions; no truthy/falsy
// if (1) {} // ERROR: must be bool
if ('text'.isNotEmpty) {
print('non-empty');
}列表(List)
列表是有序、可增长的集合(类似数组)。用 <Type>[] 指定元素类型。用 const 创建不可变列表。展开运算符 ... 将列表展开到另一个列表。列表使用从零开始的索引,是 Dart 最常用的集合。
var list = [1, 2, 3];
var typed = <String>['a', 'b'];
var constList = const [1, 2, 3];
list.add(4);
list.addAll([5, 6]);
print(list.length); // 6
print(list[0]); // 1
print(list.sublist(1, 3)); // [2, 3]
var spread = [...list, 7]; // spread
// constList.add(0); // ERROR: immutable映射(Map)
Map 是键值对。键和值可为任意类型;用 <KeyType, ValueType>{} 指定。通过 map[key] 访问,键不存在时返回 null——用 ?? 提供默认值。Map 保持插入顺序。entries、keys、values 暴露可迭代对象。
var map = {
'name': 'Alice',
'age': 30,
};
var typed = <String, int>{'a': 1, 'b': 2};
map['city'] = 'NYC'; // add entry
print(map['name']); // Alice
print(map.length); // 3
print(map.containsKey('age')); // true
map.forEach((k, v) => print('$k: $v'));
var keys = map.keys.toList(); // [name, age, city]集合(Set)与 Runes
Set 是无序的唯一元素集合——适用于去重和集合运算(并、交、差)。Runes 暴露字符串的 Unicode 码点,处理 emoji 和以代理对存储的非 BMP 字符时需要。
var set = {1, 2, 3};
set.add(2); // no duplicate added
set.add(4);
print(set); // {1, 2, 3, 4}
print(set.contains(2)); // true
print(set.intersection({2, 3, 5})); // {2, 3}
// Runes (Unicode code points)
var heart = '♥'; // ♥
print(heart); // ♥
print('A'.codeUnitAt(0)); // 65运算符
算术运算符
~/ 是 Dart 的整数除法运算符(截断为 int)。% 取模。/ 即使对 int 也返回 double。算术运算符适用于 num、int、double。需要整数除法结果时用 ~/。
print(5 + 3); // 8
print(5 - 3); // 2
print(5 * 3); // 15
print(5 / 3); // 1.6666... (double)
print(5 ~/ 3); // 1 (integer division)
print(5 % 3); // 2 (modulo)
print(-(5)); // -5 (unary minus)
print(2.toString()); // '2'自增与自减
++ 和 -- 有前缀和后缀形式。后缀(i++)返回原值后自增;前缀(++i)自增后返回新值。行为与 C/Java 一致。避免在复杂表达式中混用以提高可读性。
var i = 5;
print(i++); // 5 (postfix: use, then add)
print(i); // 6
print(++i); // 7 (prefix: add, then use)
print(i--); // 7
print(--i); // 5关系与相等运算符
== 对内置类型(字符串、数字)比较值。对列表和多数对象,== 默认比较引用,除非重写。用 package:collection 或 flutter/foundation.dart 的 listEquals() 做集合深度相等比较。
print(3 == 3); // true
print(3 != 4); // true
print(3 < 4); // true
print(3 > 4); // false
print(3 <= 3); // true
print(3 >= 4); // false
print('a' == 'a'); // true (content equality)
var l1 = [1, 2];
var l2 = [1, 2];
print(l1 == l2); // false (reference equality)逻辑运算符
&&(与)、||(或)、!(非)只对 bool 操作。&& 和 || 都短路:&& 在左侧为 false 时停止,|| 在左侧为 true 时停止。与某些语言不同,操作数必须是 bool——没有 truthy/falsy 强制。
bool a = true, b = false;
print(a && b); // false
print(a || b); // true
print(!a); // false
// short-circuit evaluation
bool check() { print('called'); return true; }
false && check(); // check() NOT called
true || check(); // check() NOT called类型测试:is、as
is 在运行时检查类型,并在分支内启用智能转换(无需显式转型)。is! 是否定形式。as 执行不安全转型,类型不匹配时抛出 TypeError。为安全起见优先用 is 检查而非 as 转型。
Object x = 'hello';
print(x is String); // true
print(x is! int); // true
if (x is String) {
print(x.length); // smart-cast to String
}
Object y = 42;
print((y as int) + 1); // 43 (cast)
// (y as String); // runtime TypeError级联(..)与空感知运算符
.. 是级联运算符——调用方法或设置字段并返回原对象,无需重复变量即可流畅链式调用。?. 是空感知访问运算符(目标为 null 时返回 null)。与 ?? 结合提供默认值。
class Builder {
String? name;
int? size;
Builder setName(String n) { name = n; return this; }
Builder setSize(int s) { size = s; return this; }
}
var b = Builder()
..setName('widget')
..setSize(10);
print(b.name); // widget
String? path;
print(path?.length); // null (safe access)
print(path?.length ?? 0); // 0控制流
if-else
if-else 条件必须求值为 bool——没有 truthy/falsy。三元运算符 ?: 与其他 C 族语言相同。else-if 链很常见。Dart 3 还支持在集合字面量内用 if 作为表达式(collection-if)。
int score = 85;
if (score >= 90) {
print('A');
} else if (score >= 80) {
print('B');
} else {
print('C');
}
// ternary expression
var grade = score >= 60 ? 'pass' : 'fail';
print(grade); // pass
// if (score) {} // ERROR: condition must be boolfor 与 for-in
经典 C 风格 for 循环用 init; condition; update。for-in 迭代任何 Iterable。对 Map 迭代 .entries、.keys 或 .values。不需要索引时优先用 for-in。${entry.key} 使用表达式插值。
for (var i = 0; i < 3; i++) {
print(i);
}
var list = ['a', 'b', 'c'];
for (var item in list) {
print(item);
}
// for-in with Map entries
var map = {'x': 1, 'y': 2};
for (var entry in map.entries) {
print('${entry.key}: ${entry.value}');
}while 与 do-while
while 在每次迭代前检查条件;do-while 在之后检查,因此循环体至少执行一次。两者都要求 bool 条件。迭代次数未知时用 while。
var i = 0;
while (i < 3) {
print('while $i');
i++;
}
var j = 0;
do {
print('do $j');
j++;
} while (j < 3);switch(Dart 3)
Dart 3 引入了 switch 表达式,返回值并每个 case 用 =>。模式支持 OR(||)、AND(&&)、关系(>=)和通配符(_)。经典 switch 语句需要 break(无 fall-through)。密封类型和枚举要求穷尽匹配。
// Classic switch statement
var color = 'red';
switch (color) {
case 'red':
print('stop');
break;
case 'green':
print('go');
break;
default:
print('unknown');
}
// Dart 3 switch expression
String describe(int n) => switch (n) {
0 => 'zero',
1 || 2 => 'small',
>= 3 && <= 10 => 'medium',
_ => 'large',
};
print(describe(5)); // mediumbreak 与 continue
break 退出最近的循环;continue 跳到下一次迭代。标签(outer:)允许 break/continue 指向外层循环——慎用因其降低可读性。break 也用于结束 switch case。
for (var i = 0; i < 5; i++) {
if (i == 2) continue; // skip 2
if (i == 4) break; // stop at 4
print(i); // 0, 1, 3
}
// labels for nested loops
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i + j > 2) break outer;
print('$i,$j');
}
}断言(assert)
assert(condition, message) 在开发期间检查条件。断言在 debug 模式启用,在 production(release)构建中移除。用于内部不变量和调试——不用于必须在生产中运行的输入校验。
void setAge(int age) {
assert(age >= 0, 'age must be non-negative');
// ...
}
assert(1 == 1); // passes in debug
// assert(1 == 2, 'math is broken'); // fails in debug
// Assertions are stripped in release/production builds函数
函数声明
在 Dart 中,函数是一等对象——可赋值给变量、作为参数传递、被返回。int Function(int) 等类型别名描述函数类型。函数声明包含可选返回类型和参数类型。
// named function with return type
int add(int a, int b) {
return a + b;
}
// functions are first-class objects
int Function(int) makeAdder(int n) {
return (int x) => x + n;
}
var add5 = makeAdder(5);
print(add5(3)); // 8
print(add(2, 3)); // 5箭头函数
=>(箭头)语法是 { return expr; } 的简写,用于单表达式函数。常用于简短方法、getter 和回调。箭头函数在不牺牲类型安全的前提下让代码简洁。
// single-expression function with =>
int square(int x) => x * x;
String greet(String name) => 'Hi, $name';
// arrow with nullable
String? firstChar(String? s) => s?.isEmpty ?? true ? null : s[0];
print(square(4)); // 16
print(greet('Al')); // Hi, Al
print(firstChar('hi')); // h可选位置参数
可选位置参数用方括号 [] 包裹,必须位于必填参数之后。默认为 null(或提供的默认值)。当参数顺序直观明显时使用。
// optional positional params wrapped in []
String greet(String name, [String? title]) {
if (title != null) {
return 'Hello, $title $name';
}
return 'Hello, $name';
}
print(greet('Alice')); // Hello, Alice
print(greet('Bob', 'Dr.')); // Hello, Dr. Bob命名参数
命名参数用 {} 包裹,在调用处按名称传递(顺序无关)。用 required 标记必填的命名参数。命名参数默认为 null,除非给定默认值。适用于有大量可选参数的 API。
// named params wrapped in {}, required marks mandatory
void createUser({
required String name,
int age = 0,
String? email,
}) {
print('$name, $age, $email');
}
createUser(name: 'Alice', age: 30);
createUser(name: 'Bob', email: '[email protected]');
// named params are passed by name, order-independent默认值
可选位置参数和命名参数都可以用 = 指定默认值。默认值必须是编译时常量。默认值通过在存在合理默认值时避免 null,使可选参数更安全。
// default values for optional positional
double calc(double a, [double b = 1.0, double c = 0.0]) {
return a * b + c;
}
// default values for named
void config({String host = 'localhost', int port = 8080}) {
print('$host:$port');
}
print(calc(5)); // 5.0
print(calc(5, 2)); // 10.0
config(port: 3000); // localhost:3000匿名函数与闭包
匿名函数(lambda)没有名称,常赋值给变量或作为回调传递。闭包捕获其外围作用域的变量并保持其存活。参数列表可带类型(int a, int b)或不带类型(a, b)。
// anonymous function assigned to a variable
var multiply = (int a, int b) => a * b;
print(multiply(3, 4)); // 12
// closure capturing a variable
Function counter() {
int count = 0;
return () => ++count;
}
var c = counter();
print(c()); // 1
print(c()); // 2
// used as callbacks
[1, 2, 3].forEach((n) => print(n));类与对象
类定义
类将字段(状态)和方法(行为)组合在一起。构造函数 Person(this.name, this.age) 使用参数初始化简写直接赋值字段。与 Java 不同,Dart 类只有一个构造函数体;用命名构造函数创建变体。
class Person {
String name;
int age;
// constructor
Person(this.name, this.age);
void greet() {
print('Hi, I am $name');
}
}
var p = Person('Alice', 30);
p.greet(); // Hi, I am Alice
print(p.name); // Alice实例变量与 this
实例变量(字段)保存每个对象的状态。this 仅在需要与参数区分或为清晰时使用。所有非空字段必须被初始化——通过构造函数初始化器、this.x 简写或默认值。字段会生成隐式 getter/setter。
class Point {
double x;
double y;
Point(this.x, this.y);
double distanceTo(Point other) {
return ((x - other.x) * (x - other.x) +
(y - other.y) * (y - other.y));
}
void moveBy(double dx, double dy) {
this.x += dx; // 'this' is optional when unambiguous
y += dy;
}
}Getter 与 Setter
用 get/set 关键字定义的 getter 和 setter,让你向调用者暴露看起来像字段的计算属性。用于校验输入或计算派生值而不改变公共 API。final 字段不能有 setter。
class Rectangle {
double width, height;
Rectangle(this.width, this.height);
// computed getter
double get area => width * height;
set size(double v) {
width = v;
height = v;
}
}
var r = Rectangle(3, 4);
print(r.area); // 12 (accessed like a field)
r.size = 10;
print(r.area); // 100静态成员
静态成员(字段和方法)属于类本身而非实例。通过 ClassName.member 访问。用于在所有实例间共享的工具函数和常量。静态成员在没有实例的情况下不能引用非静态成员。
class MathUtils {
static const double PI = 3.14159;
static double circleArea(double r) => PI * r * r;
}
// accessed via the class, not an instance
print(MathUtils.PI); // 3.14159
print(MathUtils.circleArea(2)); // 12.566
// static members belong to the class, not instances工厂构造函数
工厂构造函数(factory 关键字)不一定创建新实例——可返回缓存实例、子类型或预构建对象。适用于单例、缓存和返回子类。工厂构造函数像普通构造函数一样使用类名。
class Logger {
final String name;
static final Map<String, Logger> _cache = {};
// factory may return a cached instance
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
Logger._internal(this.name);
}
var a = Logger('app');
var b = Logger('app');
print(identical(a, b)); // true (same cached instance)Object 与 toString
每个类都继承自 Object,后者提供 toString()、==、hashCode 和 runtimeType。重写 toString() 以便调试可读。如果重写了 ==,也必须重写 hashCode 以维护在 Map 和 Set 中使用的契约。
class Book {
String title;
Book(this.title);
@override
String toString() => 'Book($title)';
@override
bool operator ==(Object other) =>
other is Book && other.title == title;
@override
int get hashCode => title.hashCode;
}
print(Book('Dart')); // Book(Dart)
print(Book('A') == Book('A')); // true构造函数
默认与生成构造函数
如果不定义任何构造函数,Dart 提供默认的无参构造函数。生成构造函数创建新实例。参数中的 this.field 简写直接赋值字段。一旦定义了构造函数,默认构造函数就不再生成。
class Animal {
String species;
// generative constructor
Animal(this.species);
// if no constructor is written, Dart provides:
// Animal() : species = 'unknown';
}
var a = Animal('cat');
print(a.species); // cat命名构造函数
命名构造函数(ClassName.name)让一个类有多种创建模式。初始化列表(: field = value)在构造函数体之前运行,可初始化 final 字段。适用于工厂式创建和转换构造函数。
class Point {
double x, y;
Point(this.x, this.y);
// named constructor
Point.origin() : x = 0, y = 0;
Point.fromList(List<double> l) : x = l[0], y = l[1];
}
var p1 = Point.origin();
var p2 = Point.fromList([3, 4]);
print('${p1.x},${p1.y}'); // 0.0,0.0
print('${p2.x},${p2.y}'); // 3.0,4.0初始化列表
初始化列表(: expr, expr)在构造函数体之前运行,可设置 final 字段并运行断言。这是初始化 final 字段的唯一位置(this.field 简写除外)。用于校验和计算字段值。
class Temperature {
final double celsius;
// initializer list runs before body
Temperature(double c) : celsius = c;
Temperature.fromFahrenheit(double f)
: celsius = (f - 32) * 5 / 9;
// assert in initializer list
Temperature.clamped(double c)
: assert(c >= -273.15),
celsius = c < -273.15 ? -273.15 : c;
}
print(Temperature.fromFahrenheit(32).celsius); // 0.0重定向构造函数
重定向构造函数用 this(...) 转发到同类中的另一个构造函数。它没有自己的函数体和初始化列表。用于提供委托给主构造函数的便捷构造函数。
class Point {
double x, y;
Point(this.x, this.y);
// redirect to another constructor with 'this'
Point.alongX(double x) : this(x, 0);
Point.origin() : this(0, 0);
Point.fromDouble(double n) : this.alongX(n);
}
print(Point.alongX(5).y); // 0.0
print(Point.origin().x); // 0.0常量构造函数
const 构造函数(const ClassName)创建编译时常量实例。所有字段必须为 final。相同的 const 实例会被规范化(共享)。const 构造函数支持深度不可变对象和编译时常量集合。
class ImmutablePoint {
final double x;
final double y;
// const constructor: creates compile-time constant instances
const ImmutablePoint(this.x, this.y);
static const origin = ImmutablePoint(0, 0);
}
const p = ImmutablePoint(1, 2);
const o = ImmutablePoint.origin;
print(identical(o, ImmutablePoint(0, 0))); // true工厂与缓存
工厂构造函数可返回 const 或缓存实例,并可使用 Dart 3 switch 表达式。它们不同于总是创建新实例的生成构造函数。当构造逻辑必须选择返回什么时使用工厂。
class Shape {
final String type;
const Shape._(this.type);
factory Shape(String kind) {
return switch (kind) {
'circle' => const Shape._('circle'),
'square' => const Shape._('square'),
_ => const Shape._('unknown'),
};
}
}
print(Shape('circle').type); // circle
print(identical(Shape('circle'), Shape('circle'))); // true继承
extends 与 super
用 extends 继承单一父类(Dart 是单继承)。子类构造函数必须调用父类构造函数(通常通过初始化列表中的 super(...))。用 @override 标记替换父类实现的方法。
class Animal {
String name;
Animal(this.name);
void speak() => print('$name makes a sound');
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() => print('$name barks');
}
var d = Dog('Rex');
d.speak(); // Rex barks@override 与 super 调用
@override 是元数据注解,标记方法覆盖父类成员。它触发编译期检查父类方法是否存在。super.method() 从被覆盖方法内调用父类实现,允许扩展行为。
class Vehicle {
int speed = 0;
void accelerate(int by) => speed += by;
void describe() => print('Vehicle at $speed');
}
class Car extends Vehicle {
@override
void accelerate(int by) {
super.accelerate(by); // call parent method
print('Car now at $speed');
}
}
Car().accelerate(10); // Car now at 10构造函数链
在继承中,父类构造函数在子类函数体之前运行。初始化列表中的 super(...) 调用向上传递参数。执行顺序:初始化列表 -> 父类构造函数 -> 本类构造函数体。这保证父类先被完全初始化。
class Base {
String tag;
Base(this.tag) {
print('Base created: $tag');
}
}
class Derived extends Base {
Derived(String tag) : super(tag) {
print('Derived created');
}
}
// order: super initializer -> super body -> derived body
Derived('x');
// Base created: x
// Derived creatednoSuchMethod
noSuchMethod 在代码尝试调用对象上不存在的方法或访问不存在的字段时被调用。重写它可实现动态代理或 mock 对象。返回值让调用继续;默认抛出 NoSuchMethodError。
class Proxy implements Object {
@override
dynamic noSuchMethod(Invocation inv) {
print('Called: ${inv.memberName}');
return null;
}
}
var p = Proxy();
p.someMissingMethod(); // Called: Symbol("someMissingMethod")密封类(Dart 3)
密封类(Dart 3)是封闭层次结构:所有直接子类型必须在同一库中。编译器对密封类型的子类型强制穷尽 switch 匹配,使模式匹配安全。非常适合建模固定的变体集合(如 Result/Either 类型)。
sealed class Shape {
const Shape();
}
class Circle extends Shape {
final double r;
const Circle(this.r);
}
class Square extends Shape {
final double s;
const Square(this.s);
}
// switch is exhaustive over sealed subtypes
double area(Shape s) => switch (s) {
Circle(:var r) => 3.14 * r * r,
Square(:var s) => s * s,
};
print(area(const Circle(2))); // 12.56抽象类与接口
抽象类
抽象类(abstract class)不能直接实例化,可包含抽象方法(无函数体)由子类实现。也可有具体方法。用抽象类定义子类共享的部分实现。
abstract class Animal {
// abstract method: no body, must be overridden
void makeSound();
// concrete method: inherited as-is
void breathe() => print('breathing');
}
class Cat extends Animal {
@override
void makeSound() => print('meow');
}
var c = Cat();
c.makeSound(); // meow
// Animal(); // ERROR: cannot instantiate abstract class隐式接口
每个 Dart 类都隐式定义一个接口,包含其所有成员。用 implements 实现该接口——必须为每个成员提供函数体(不继承代码)。一个类可实现多个接口,用逗号分隔。
class Television {
void turnOn() => print('on');
void turnOff() => print('off');
}
// every class implicitly defines an interface
class SmartTV implements Television {
@override
void turnOn() => print('smart on');
@override
void turnOff() => print('smart off');
}
SmartTV().turnOn(); // smart on实现多个接口
与 extends 只允许一个父类不同,一个类可实现多个接口。用 implements 只继承类型契约——每个成员都必须重写,但可实现多个接口。这实现了类型的「多继承」。
abstract class Flyer {
void fly();
}
abstract class Swimmer {
void swim();
}
// implement multiple interfaces
class Duck implements Flyer, Swimmer {
@override
void fly() => print('flying');
@override
void swim() => print('swimming');
}
Duck().fly(); // flying
Duck().swim(); // swimmingextends 与 implements 对比
extends 复用父类实现(单继承)。implements 只继承类型契约——每个成员都必须重新实现,但可实现多个接口。需要代码复用的「是一个」关系用 extends;「能做」契约用 implements。
class Base {
void greet() => print('hello');
void wave() => print('waving');
}
// extends: reuse implementation, single parent
class A extends Base {
@override
void greet() => print('A says hi');
}
// implements: contract only, must override all
class B implements Base {
@override
void greet() => print('B says hi');
@override
void wave() => print('B waving');
}抽象类与接口使用指南
用抽象类在紧密相关的类型间共享实现(代码继承)。用接口(通过 implements 使用的抽象类)定义不相关类型可满足的能力或契约。Dart 合并了这些概念:抽象类可同时充当两者。
// Abstract class: share code among related types
abstract class Repository<T> {
T? find(int id); // abstract
void save(T item) => print('saved'); // shared
}
// Interface: define a capability
abstract class Comparable<T> {
int compareTo(T other);
}
class Product extends Repository<Product>
implements Comparable<Product> {
@override
Product? find(int id) => null;
@override
int compareTo(Product other) => 0;
}Mixin
定义 Mixin
mixin(mixin 关键字)是一个可复用的行为单元,可用 with 混入类中。mixin 不能有构造函数,不能直接实例化。适合在不相关的类间共享横向功能。
mixin Greeter {
String get name;
void greet() => print('Hello, $name!');
}
class User with Greeter {
@override
String name;
User(this.name);
}
User('Alice').greet(); // Hello, Alice!使用 Mixin(with)
用 with 关键字后跟一个或多个 mixin(逗号分隔)应用 mixin。一个类可混入多个 mixin,当 mixin 互相覆盖时顺序很重要。mixin 线性地应用到类层次结构中。
mixin Walker {
void walk() => print('walking');
}
mixin Talker {
void talk() => print('talking');
}
// mix in multiple mixins
class Person extends Object with Walker, Talker {
String name;
Person(this.name);
}
var p = Person('Bob');
p.walk(); // walking
p.talk(); // talkingmixin 约束(on)
on 子句将 mixin 约束为只能用于继承(或实现)指定父类型的类。这让 mixin 能调用该父类型的方法。约束类型充当 mixin 行为的必需基类。
mixin Musician on Performer {
void playNote() => print('playing note');
}
abstract class Performer {
void perform();
}
class Singer extends Performer with Musician {
@override
void perform() => print('singing');
}
Singer().playNote(); // playing note
// Musician can only be mixed into Performer subtypesmixin class(Dart 3)
Dart 3 引入了 mixin class,既可作为 mixin(with)使用,也可作为普通类(extends/implements)。普通 mixin 不能被 extends,普通 class 不能被 with——mixin class 桥接两者。适用于需要同时具备正常类语义的共享行为。
// 'mixin class' can be both extended and mixed in
mixin class Counter {
int _count = 0;
int get count => _count;
void increment() => _count++;
}
class App extends Counter {}
class Tool with Counter {}
print(App().count); // 0
App().increment();
Tool().increment();
print(App().count); // 0 (separate instance)mixin 方法解析
当多个 mixin 定义相同方法时,with 子句中最右(最后)的 mixin 胜出,因为 mixin 从左到右应用,后面的覆盖前面的。类自身的方法覆盖所有 mixin。这种线性化决定方法解析。
mixin A {
void hello() => print('A');
}
mixin B {
void hello() => print('B');
}
class X with A, B {}
class Y with B, A {}
X().hello(); // B (later mixin wins)
Y().hello(); // A (later mixin wins)
// resolution order: class -> last mixin -> ... -> first mixin泛型
泛型类
泛型允许类和方法在任何类型上操作同时保持类型安全。<T> 声明类型参数。同一个 Stack 类适用于 int、String 或任何类型而无需转型。Dart 泛型是具体化的(运行时可用类型信息),不同 于 Java 的擦除。
class Stack<T> {
final List<T> _items = [];
void push(T item) => _items.add(item);
T pop() => _items.removeLast();
bool get isEmpty => _items.isEmpty;
}
var s = Stack<int>();
s.push(1);
s.push(2);
print(s.pop()); // 2
var names = Stack<String>();
names.push('Al');泛型方法
方法可有独立于类的类型参数。类型参数通常从参数推断。泛型方法提供类型安全的工具,如 firstOrDefault,其返回类型取决于输入类型。
// generic method with its own type parameter
T firstOrDefault<T>(List<T> list, T defaultValue) {
return list.isEmpty ? defaultValue : list.first;
}
print(firstOrDefault<int>([1, 2, 3], 0)); // 1
print(firstOrDefault([], 'none')); // none (type inferred)
// type often inferred from arguments泛型约束(extends)
用 extends 将类型参数约束到某父类型(上界)。约束让你在泛型内调用上界类型的方法。T extends num 确保 T 支持算术。无约束时 T 被视为 Object?,可用方法很少。
class Comparable<T> {
int compareTo(T other);
}
// constrain T to subtypes of Comparable<T>
T max<T extends Comparable<T>>(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
// number sum constraint
num sum<T extends num>(List<T> nums) =>
nums.fold(0, (a, b) => a + b);
print(sum([1, 2.5, 3])); // 6.5泛型集合
Dart 集合是泛型的,类型信息在运行时保留(具体化)。is List<int> 有效因为泛型未被擦除。Dart 集合是协变的:List<int> 可传给需要 List<num> 的地方,方便但写入时可能运行时报错。
List<int> nums = [1, 2, 3];
Map<String, int> scores = {'a': 1, 'b': 2};
Set<double> uniq = {1.1, 2.2, 1.1};
// runtime type checks work (reified generics)
print(nums is List<int>); // true
print(nums is List<String>); // false
print(scores is Map); // true
// generic variance
void process(List<num> list) => print(list);
process(nums); // OK: int is a num泛型 typedef
typedef 可以是泛型的,为泛型函数类型或类类型创建可复用的类型别名。Transformer<T> 为函数 T->T 起别名。这对在库中声明回调契约非常有价值。泛型 typedef 让复杂类型签名更可读。
// generic function type alias
typedef Transformer<T> = T Function(T input);
int doubler(int x) => x * 2;
String upper(String s) => s.toUpperCase();
Transformer<int> dt = doubler;
Transformer<String> ut = upper;
print(dt(5)); // 10
print(ut('hi')); // HI
// generic class alias
typedef IntList = List<int>;
IntList xs = [1, 2, 3];集合(List/Set/Map)
List 操作
List 支持丰富的函数式操作:where(过滤)、map(转换)、fold/reduce(累积)、sort、reversed。这些返回 Iterable——调用 .toList() 物化。在 Iterable 上链式调用时操作是惰性的。List 是 Dart 主力有序集合。
var nums = [3, 1, 2];
nums.sort();
print(nums); // [1, 2, 3]
print(nums.reversed.toList()); // [3, 2, 1]
print(nums.indexOf(2)); // 1
print(nums.contains(3));// true
print(nums.where((n) => n > 1).toList()); // [2, 3]
print(nums.map((n) => n * 2).toList()); // [2, 4, 6]
print(nums.fold(0, (a, b) => a + b)); // 6Set 操作
Set 提供数学集合运算:并、交、差。将 List 转 Set 去重,再用 toList() 转回 List。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}
var dedup = [1, 1, 2, 3, 3].toSet();
print(dedup.toList()); // [1, 2, 3]
print(dedup.contains(2)); // trueMap 操作
Map 将 keys 和 values 暴露为 Iterable。update() 原地修改值;map() 将条目转换为新 Map。forEach 迭代键值对。Map 保持插入顺序。用 putIfAbsent 实现「不存在才设置」语义。
var ages = {'Alice': 30, 'Bob': 25};
ages['Carol'] = 28;
print(ages.keys); // (Alice, Bob, Carol)
print(ages.values); // (30, 25, 28)
print(ages.length); // 3
ages.update('Bob', (v) => v + 1);
print(ages['Bob']); // 26
ages.remove('Alice');
ages.forEach((k, v) => print('$k=$v'));
var mapped = ages.map((k, v) => MapEntry(k, v + 100));展开与空安全展开
展开运算符 ... 将集合元素展开到另一个集合。...? 是空感知展开——优雅处理 null(展开为空)。展开适用于 List、Set 和 Map,使集合组合更简洁。
var a = [1, 2];
var b = [0, ...a, 3]; // [0, 1, 2, 3]
print(b);
List<int>? maybe;
var c = [0, ...?maybe, 4]; // [0, 4] (null-spread is safe)
print(c);
var m1 = {'a': 1};
var m2 = {'b': 2, ...m1}; // {b: 2, a: 1}
print(m2);collection-if 与 collection-for
collection-if 和 collection-for 是 Dart 特有语法,内联地按条件或迭代构建集合。避免单独的 add() 调用和中间变量。可组合和嵌套,产生简洁的声明式集合构造。
var promo = true;
var menu = [
'home',
'products',
if (promo) 'sale',
'about',
];
print(menu); // [home, products, sale, about]
var nums = [1, 2, 3];
var doubled = [
for (var n in nums) n * 2,
];
print(doubled); // [2, 4, 6]
// combine: [for (var x in xs) if (x > 0) x]高阶方法
List/Iterable 支持 any、every、firstWhere、reduce、fold、skip、take、expand 等。fold 很强大——可携带任意类型的累加器。这些高阶方法支持无显式循环的声明式函数风格数据处理。
var nums = [1, 2, 3, 4, 5];
print(nums.any((n) => n > 4)); // true
print(nums.every((n) => n > 0)); // true
print(nums.firstWhere((n) => n > 2)); // 3
print(nums.reduce((a, b) => a + b)); // 15
var byParity = nums.fold(<bool, List<int>>{}, (m, n) {
m[n.isOdd] = [...?m[n.isOdd], n]; return m;
});
print(byParity); // {true: [1,3,5], false: [2,4]}异步(Future / async / await)
Future 基础
Future 表示将在未来某个时刻可用的值(或错误)。Future.delayed 在一段时长后完成。用 .then() 注册 Future 完成时的回调。Future 是 Dart 异步模型的基础。
Future<String> fetchUser() {
return Future.delayed(Duration(seconds: 1), () => 'Alice');
}
void main() {
fetchUser().then((name) {
print('Got: $name'); // Got: Alice (after 1s)
});
print('waiting...');
}async 与 await
标记函数 async 以在其中使用 await。async 函数总是返回 Future。await 暂停执行直到所等待的 Future 完成,然后返回其值——不阻塞事件循环。这让异步代码读起来像同步代码。
Future<String> fetchUser() async {
await Future.delayed(Duration(seconds: 1));
return 'Alice';
}
Future<void> main() async {
print('start');
String name = await fetchUser();
print('Got: $name');
print('done');
}异步错误处理
async 函数中抛出的错误以失败的 Future 传播。在 await 周围用 try/catch/finally 处理——与同步错误处理相同。catch 捕获错误对象;用 on ExceptionType 捕获特定类型。
Future<int> divide(int a, int b) async {
if (b == 0) throw Exception('divide by zero');
return a ~/ b;
}
Future<void> main() async {
try {
var result = await divide(10, 0);
print(result);
} catch (e) {
print('Error: $e'); // Error: Exception: divide by zero
} finally {
print('done');
}
}Future.then 与 catchError
不用 await 时,可用 .then()、.catchError()、.whenComplete() 链式 Future。每个 .then() 返回新 Future,支持管道。catchError 处理链中任何失败。可读性优先用 async/await;简单场景用链式。
Future<int> compute() async => 42;
compute()
.then((v) => v * 2)
.then((v) => print(v)) // 84
.catchError((e) => print('err: $e'))
.whenComplete(() => print('cleanup'));
// chaining transforms the result type
Future<String> fetch() async => 'data';
fetch().then((s) => s.length).then(print); // 4Future.wait 与 Future.any
Future.wait 并行运行多个 Future,全部完成时完成(返回结果 List)。Future.any 以第一个完成的 Future 的结果完成。wait 用于并行,any 用于竞速(如请求 + 超时)。
Future<int> task(int n) async {
await Future.delayed(Duration(milliseconds: n));
return n;
}
// run in parallel, wait for all
var all = await Future.wait([task(100), task(50), task(200)]);
print(all); // [100, 50, 200]
// resolve with the first to complete
var first = await Future.any([task(100), task(50), task(200)]);
print(first); // 50 (fastest)Completer
Completer 让你手动创建并完成 Future。调用 complete(value) 或 completeError(error) 完成。适用于将回调式 API 包装为 Future,或 Future 完成 由你控制的外部事件触发时。
import 'dart:async';
Completer<String> completer = Completer<String>();
// complete the future from elsewhere
Future<String> get value => completer.future;
completer.complete('resolved!');
void main() async {
print(await value); // resolved!
}
// useful when bridging callback-based APIs to FuturesStream
创建 Stream(async*)
async* 生成器函数返回 Stream 并用 yield 发射值。每次 yield 向流发射一个值。生成器是惰性的——值在被消费时才产生。async* 是 sync*(Iterable)的异步对应。
Stream<int> countDown(int from) async* {
while (from > 0) {
await Future.delayed(Duration(seconds: 1));
yield from;
from--;
}
}
void main() async {
await for (var n in countDown(3)) {
print(n); // 3, 2, 1 (one per second)
}
}监听 Stream
listen() 订阅 Stream 并注册数据、错误和完成的回调。它返回 StreamSubscription,可暂停、恢复或取消。单订阅流只允许一个监听器;广播流允许多个。
var sub = countDown(3).listen(
(n) => print('got $n'),
onDone: () => print('done'),
onError: (e) => print('err: $e'),
);
// pause/resume/cancel
sub.pause();
sub.resume();
// sub.cancel(); // stop listening
Stream<int> countDown(int from) async* {
while (from > 0) yield from--;
}Stream 方法
Stream 支持 where、map、take、skip、expand 等函数式转换,返回新 Stream。也可用 first、last、length、isEmpty 作为 Future。这些方法使 Stream 处理声明式——类似 List 但异步。
var stream = Stream.fromIterable([1, 2, 3, 4]);
// transform like an Iterable
var evens = stream.where((n) => n.isEven);
var doubled = stream.map((n) => n * 2);
await for (var n in Stream.fromIterable([1,2,3]).map((n) => n * 10)) {
print(n); // 10, 20, 30
}
print(await stream.first); // 1
print(await stream.last); // 4
print(await stream.length); // 4StreamController
StreamController 提供一个 sink 手动向 Stream 推送数据、错误和完成事件。用于从非流源创建自定义流。默认是单订阅;传 broadcast: true 创建多监听器广播流。
import 'dart:async';
var controller = StreamController<int>();
// add events manually
controller.add(1);
controller.add(2);
controller.addError('oops');
controller.close();
controller.stream.listen(
(n) => print(n), // 1, 2
onError: (e) => print(e), // oops
onDone: () => print('done'),
);
// use controller.addError/sink.add for errorsawait for 与广播流
await for 异步迭代 Stream,在事件到达时处理,直到流关闭。广播流(用 .broadcast() 或 StreamController.broadcast 创建)允许多个同时监听器,单订阅流只允许一个。
// await for consumes a stream like a loop
Future<int> sumStream(Stream<int> s) async {
var total = 0;
await for (var n in s) {
total += n;
}
return total;
}
print(await sumStream(Stream.fromIterable([1, 2, 3]))); // 6
// broadcast stream: multiple listeners
var bc = StreamController<int>.broadcast();
bc.stream.listen(print);
bc.stream.listen((n) => print('got $n'));
bc.add(5); // both listeners receive 5异常处理
throw
throw 抛出异常,中断正常执行。Dart 可抛出任何非空对象,但习惯上抛出 Exception 或 Error 子类型。常见内置类型:ArgumentError、StateError、FormatException、RangeError。不鼓励抛出字符串。
void checkAge(int age) {
if (age < 0) {
throw ArgumentError('age cannot be negative');
}
if (age > 150) {
throw StateError('unrealistic age: $age');
}
}
// you can throw any non-null object
void fail() => throw 'something went wrong';try-catch-finally
用 on Type catch (e) 捕获特定异常类型,或 catch (e, stackTrace) 捕获任意异常并访问堆栈跟踪。finally 无论是否发生异常都执行。顺序重要:特定 on 子句放在通用 catch 之前。
try {
checkAge(-5);
} on ArgumentError catch (e) {
print('argument error: $e');
} on StateError catch (e) {
print('state error: $e');
} catch (e, stackTrace) {
print('unknown: $e');
print(stackTrace);
} finally {
print('always runs');
}自定义异常
通过实现 Exception 接口创建自定义异常。提供描述性消息并重写 toString() 以便可读。自定义异常使错误处理更精确——调用者可捕获特定失败模式。约定:命名为 *Exception。
class InvalidCredentialsException implements Exception {
final String message;
InvalidCredentialsException(this.message);
@override
String toString() => 'InvalidCredentialsException: $message';
}
void login(String user, String pass) {
if (user.isEmpty) {
throw InvalidCredentialsException('username required');
}
}
try {
login('', 'x');
} on InvalidCredentialsException catch (e) {
print(e); // InvalidCredentialsException: username required
}rethrow
rethrow 重新抛出当前捕获的异常,同时保留原始堆栈跟踪。在记录日志或清理但未完全处理错误的中间件/包装函数中使用。与 throw e 不同,rethrow 保留原始堆栈跟踪以便调试。
Future<void> logErrors(Future<void> Function() action) async {
try {
await action();
} catch (e) {
print('logging error: $e');
rethrow; // re-throw the caught exception
}
}
void main() async {
try {
await logErrors(() async => throw Exception('fail'));
} catch (e) {
print('handled upstream: $e');
}
}Error 与 Exception 对比
Exception 用于程序可合理捕获并恢复的运行时条件(如网络失败、错误输入)。Error 表示编程错误(类型错误、断言失败、索引越界),应在代码中修复而非运行时捕获。不鼓励捕获 Error。
// Exception: recoverable, expected to be caught
class MyException implements Exception {}
// Error: programming bugs, not meant to be caught
class MyError extends Error {
@override
String toString() => 'MyError: invalid state';
}
void risky() {
throw MyError(); // bug: should fix the code
throw MyException(); // runtime condition: catch it
}
// assert failures, type errors, range errors are Errors类型定义(Typedef)
函数 typedef
typedef 为函数类型创建别名,使签名可读且可复用。IntOperator 现在命名了 (int,int)->int 函数类型。没有 typedef 就要到处重复 int Function(int, int)。可将兼容的函数赋给 typedef 类型的变量。
// alias for a function type
typedef IntOperator = int Function(int, int);
int add(int a, int b) => a + b;
int mul(int a, int b) => a * b;
IntOperator op = add;
print(op(2, 3)); // 5
op = mul;
print(op(2, 3)); // 6
// pass as a parameter
void apply(IntOperator f, int a, int b) => print(f(a, b));泛型 typedef
typedef 可以是泛型的,带自己的类型参数。Mapper<T, R> 为函数 T->R 起别名。这让你一次表达丰富的函数契约并复用。泛型 typedef 对 Dart 中的类型化函数式编程模式至关重要。
// generic function type alias
typedef Mapper<T, R> = R Function(T input);
String stringify(int n) => n.toString();
int lenOf(String s) => s.length;
Mapper<int, String> intToStr = stringify;
Mapper<String, int> strToLen = lenOf;
print(intToStr(42)); // '42'
print(strToLen('hi')); // 2新式 typedef(非函数)
自 Dart 2.13 起,typedef 可为任何类型起别名——包括类,不仅是函数。这提高了复杂泛型类型的可读性。Dart 3 记录类型也可被起别名。别名与原类型完全可互换——运行时无差异。
// Dart 2.13+: alias for any type, not just functions
typedef IntList = List<int>;
typedef StringMap<V> = Map<String, V>;
IntList nums = [1, 2, 3];
StringMap<int> scores = {'a': 1};
// alias for a record type (Dart 3)
typedef Point = ({double x, double y});
Point p = (x: 1.0, y: 2.0);
print(p.x); // 1.0在 API 中使用 typedef
typedef 在 API 签名中大放异彩:它们清晰地命名回调契约。Predicate<T> 比 bool Function(T) 更易读。库作者用它们暴露干净、有文档的函数类 型契约。调用者可传递任何兼容的函数或 lambda。
typedef Predicate<T> = bool Function(T);
bool isEven(int n) => n.isEven;
List<T> filter<T>(List<T> list, Predicate<T> test) {
return list.where(test).toList();
}
print(filter([1, 2, 3, 4], isEven)); // [2, 4]
print(filter(['', 'a', ''], (s) => s.isNotEmpty)); // [a]
// typedef makes callback contracts explicittypedef 与内联函数类型对比
typedef 只是别名——在运行时和类型检查上与内联函数类型完全相同。typedef 提高可读性并集中契约,使修改在一处完成。对在多处使用的函数类型优先用 typedef。
// these two are equivalent
typedef Handler = void Function(String event);
class EventBus {
// using typedef
void on(Handler handler) {}
// using inline type
void on2(void Function(String) handler) {}
}
// both accept the same functions
void myHandler(String e) => print(e);
EventBus().on(myHandler);
EventBus().on2(myHandler);枚举
基本枚举
枚举声明一组固定的命名常量。每个值有 name(String)和 index(int)。values 按声明顺序返回所有枚举值列表。枚举隐式是 static 和 const。枚举的 switch 受益于穷尽性检查。
enum Color { red, green, blue }
var c = Color.red;
print(c); // Color.red
print(c.name); // 'red'
print(c.index); // 0
print(Color.values); // [Color.red, Color.green, Color.blue]
print(Color.green.index); // 1增强枚举(Dart 3)
Dart 3 增强枚举可有字段、构造函数、方法和 getter——像类一样。构造函数必须是 const。这让枚举携带数据和行为,取代许多类层次结构的用法。前几个值仍自动获得 name 和 index 属性。
enum Vehicle {
car('Car', 4),
bike('Bike', 2),
truck('Truck', 6);
final String label;
final int wheels;
const Vehicle(this.label, this.wheels);
int get axles => wheels ~/ 2;
}
print(Vehicle.car.label); // Car
print(Vehicle.bike.wheels); // 2
print(Vehicle.truck.axles); // 3遍历与 switch
枚举值可通过 .values 迭代。对枚举 switch 是穷尽的——如果覆盖所有 case 则不需要 default(Dart 3 对密封类型和枚举强制)。这让新增枚举值成为编译期信号,提醒更新每个 switch。
enum Status { pending, active, done }
// iterate all values
for (var s in Status.values) {
print(s.name);
}
// exhaustive switch (no default needed)
String label(Status s) => switch (s) {
Status.pending => 'Waiting',
Status.active => 'Running',
Status.done => 'Finished',
};
print(label(Status.active)); // Running带方法的枚举
增强枚举可定义使用 this 引用当前值的方法和 getter。枚举方法内对 this 的 switch 无需 default 即穷尽。这种模 式干净地封装每个枚举值的行为,常取代工具类。
enum HttpStatus {
ok(200),
notFound(404),
serverError(500);
final int code;
const HttpStatus(this.code);
bool get isSuccess => code >= 200 && code < 300;
String get reason => switch (this) {
ok => 'OK',
notFound => 'Not Found',
serverError => 'Internal Server Error',
};
}
print(HttpStatus.ok.isSuccess); // true
print(HttpStatus.notFound.reason); // Not Found枚举比较
每个枚举值是单例——每个值每个程序只有一个实例。== 实际上按身份比较。index 允许按声明位置排序。用 == 比较相等;identical() 也可用因为值被规范化。枚举是很好的 Map 键和 Set 元素。
enum Priority { low, medium, high }
var a = Priority.low;
var b = Priority.high;
print(a == b); // false
print(a == Priority.low);// true
print(a.index < b.index);// true
print(identical(a, Priority.low)); // true
// enums are singletons: only one instance per value
// use == for equality, not identical (though both work)注解(元数据)
内置注解
注解以 @ 开头,为库、类、字段、参数和方法附加元数据。@override 标记方法覆盖(编译期检查)。@deprecated/@Deprecated 标记过时 API。@protected、@visibleForTesting、@visibleForOverriding 控制可见性提示。
class Animal {
@override
String toString() => 'Animal';
@Deprecated('use newName instead')
String oldName = 'x';
String newName = 'x';
@protected
void internalMethod() {}
@visibleForTesting
String testHook() => 'test';
}@override 与 @Deprecated
@override 告诉编译器验证方法确实覆盖父类成员——在编译期捕获拼写错误。@Deprecated('message') 标记 API 过时;分析器在每个调用点用提供的消息发出警告,引导迁移到替代方案。
class Base {
void greet() {}
String name = 'base';
}
class Derived extends Base {
@override
void greet() => print('hi');
// @override verifies the parent method exists
// typo here would be a compile error:
// @override void greeet() {}
}
// @Deprecated emits a warning at the call site
@Deprecated('use bar()')
void foo() {}
void bar() {}自定义注解
任何带 const 构造函数的类都可用作注解。定义自定义注解为工具、文档或代码生成标记代码。读取注解需要 dart:mirrors(仅 VM,Flutter 中禁用)或 build_runner 代码生成。常见于 JSON 序列化包。
// a custom annotation is just a const constructor class
class Todo {
final String msg;
const Todo(this.msg);
}
class Service {
@Todo('refactor to use cache')
void fetchData() {}
@Todo('add tests')
void process() {}
}
// annotations are accessed via dart:mirrors (VM) or
// code generation (build_runner) in practice@immutable 与 @JsonSerializable
@immutable(来自 package:meta)标记构造后不应改变的类;子类和字段应为 final。@JsonSerializable(来自 json_serializable)通过 build_runner 触发 JSON 转换的代码生成。注解驱动许多 Dart/Flutter 生态。
import 'package:meta/meta.dart';
// requires the 'meta' package
@immutable
class User {
final String name;
final int age;
const User(this.name, this.age);
}
// with package:json_annotation / json_serializable
// @JsonSerializable()
// class Product {
// final String id;
// Product(this.id);
// factory Product.fromJson(Map<String, dynamic> j) => ...;
// }参数上的注解
注解也可放在参数、库声明和 typedef 上。空安全前 @required 标记必填命名参数;现代等价物是 required 关键字。参数上的注解被序列化和 DI 框架广泛使用。
class Required {
const Required([this.reason]);
final String? reason;
}
class Service {
// annotation on a parameter
void create({
@Required('name is mandatory') String? name,
@protected int? internal,
}) {
print(name);
}
}
// @required is built into Dart (the 'required' keyword
// is preferred in Dart 2.12+ for null safety)库与可见性
import 与 show/hide
import 将库的公共 API 引入作用域。用 show 只导入特定名称,hide 排除名称,as 给前缀(避免冲突)。dart: 是 SDK 库;package: 是 pub 包;相对路径用于本地文件。
// import an entire library
import 'dart:io';
import 'package:http/http.dart';
// import only specific names
import 'dart:math' show Random, pi;
// hide specific names
import 'dart:async' hide Timer;
// prefix to avoid name clashes
import 'package:http/http.dart' as http;
http.get(Uri.parse('https://example.com'));export 与 part
part 将单个库拆分到多个文件(它们共享库的私有命名空间)。export 重新导出另一个库的符号,使导入你库的人也能获得——用于创建单一公共入口。多数模块化设计优先用 export 而非 part。
// library.dart
library my_lib;
// split implementation across files
part 'src/widget_a.dart';
part 'src/widget_b.dart';
// re-export another library's API
export 'src/utils.dart' show formatDate, parseDate;
// users import library.dart and get everything可见性(_下划线)
Dart 的可见性是基于库而非类的:以 _ 开头的标识符对声明它们的库(文件或 part-of 组)私有。同一库可访问其中定义的类的私有成员。没有 protected 或包私有关键字。
// names starting with _ are library-private
class _Internal {
void _helper() {}
}
class Public {
String _secret = 'hidden'; // private field
String name = 'visible'; // public field
String _process() => 'internal';
String reveal() => _process();
}
// _secret is accessible anywhere in the SAME library/file
// but not from other libraries that import this file延迟(惰性)加载
延迟加载(惰性 import)只在首次调用 loadLibrary() 时下载库——在 web 上可减少初始包大小。延迟库的符号通过前缀访问。web 支持;原生平台急切加载。适合很少使用的功能。
// load a library on demand (web only)
import 'package:heavy_lib/heavy.dart' deferred as heavy;
Future<void> main() async {
// library is NOT loaded until this call
await heavy.loadLibrary();
heavy.SomeClass().doWork();
}
// useful for splitting large web bundles and
// loading rarely-used features only when neededlibrary 指令
library 指令为库命名,在现代 Dart 中是可选的。主要在使用 part/part-of 将库拆分到多文件时相关。多数 Dart 文件省略该指令,被视为匿名库。名称有助于工具和文档。
// explicit library declaration
library my_package.utils;
import 'dart:math';
part 'src/helper.dart';
const double version = 1.0;
// a 'library' name is optional in modern Dart;
// it's mainly used with part/part-of and tooling
// most files omit it and are treated as anonymous librariespart 与 part of
part/part-of 将一个库拆分到多个文件:主文件声明 part 'file.dart';部分文件声明 'part of library;'。部分共享库作用域包括私有(_name)成员。新代码优先用独立库加 export——part 用于紧密耦合的实现。
// shapes.dart
library shapes;
part 'circle.dart';
part 'square.dart';
class Shape {}
// circle.dart
part of shapes;
class Circle extends Shape {}
// files in the same library share private members
// (_name visible across all parts)空安全
可空与非空类型
健全的空安全:int 永远不能为 null;int? 可以。编译器强制执行此规则,在编译期捕获 NullPointerException。使用前必须初始化非空变量。在任意类型后加 ? 使其可空。这是 Dart 自 2.12 起的核心安全特性。
int a = 42; // non-nullable: cannot be null
int? b; // nullable: can be null
print(b); // null
b = 10;
print(b); // 10
String name = 'Al'; // non-nullable
String? middle; // nullable
// non-nullable types are guaranteed non-null
// int x = null; // ERROR
// print(a.length); // safe: a is non-null空断言(!)
!(空断言)运算符告诉编译器「我知道这是非空的;相信我」。如果值实际为 null 则运行时抛出。慎用——优先用 ?. 或空检查。过度使用 ! 会破坏空安全。当框架保证值已设置时合理使用。
String? maybeName;
// int len = maybeName.length; // ERROR: maybeName is nullable
maybeName = 'Alice';
int len = maybeName!.length; // ! asserts non-null
print(len); // 5
// throws if null at runtime:
// String? n; print(n!.length); // NoSuchMethodError/null空感知运算符
?. 是空感知访问(目标为 null 时返回 null)。?? 是空合并运算符(提供默认值)。??= 仅在当前值为 null 时赋值。这些让你无需显式 if-null 检查即可优雅处理可空值。
String? name;
print(name?.length); // null (safe access)
print(name?.length ?? 0);// 0 (default if null)
name = 'Alice';
print(name?.length); // 5
name ??= 'Bob'; // assign only if null
print(name); // Alice (already set)
List<int>? list;
print(list?.first); // null
print(list?.first ?? -1);// -1类型提升
类型提升:在空检查(x != null)或类型检查(x is String)后,编译器在该分支内收窄类型——无需显式转型。非空赋值后也会提升。局部变量提升良好;字段可能需要显式局部副本。
String? name;
if (name != null) {
// name is promoted to non-nullable String here
print(name.length); // safe, no ! needed
}
// promoted via is check
Object obj = 'hello';
if (obj is String) {
print(obj.length); // smart-cast to String
}
// promoted via assignment
int? x;
x = 5;
print(x.abs()); // x is non-null after assignmentlate 与 required
late 标记在声明后、首次使用前初始化的非空变量——延迟初始化。late final 初始化一次(若有初始化器则惰性)。required 标记必填命名参数。它们与空安全干净地集成。
class Config {
// late: non-nullable, initialized later
late final String value = _load();
// late without initializer: assign before first use
late final int computed;
Config() {
computed = expensive();
}
String _load() => 'loaded';
int expensive() => 42;
}
// required: mandatory named parameter
void build({required String name}) {}
build(name: 'Al'); // OK
// build(); // ERROR: missing requiredlate 惰性初始化
带初始化器的 late 字段是惰性的——初始化器在首次访问时运行,而非构造时。结果被缓存供后续访问。非常适合昂贵初始化、循环引用和依赖 this 完全构造的字段。late final 使其成为一次性计算。
class Service {
// lazy: _expensive runs only on first access
late final int cache = _expensive();
int _expensive() {
print('computing...');
return 42;
}
}
var s = Service();
print('created');
print(s.cache); // computing... 42
print(s.cache); // 42 (cached, no recompute)
// late fields with initializers are evaluated lazily相关 Dart 代码片段
Copy-paste ready code for common tasks.
这篇内容对您有帮助吗?