Dart 语言教程 ------ 写给前端开发者
本教程面向有 JavaScript / TypeScript 基础的前端工程师,全程用你熟悉的概念做类比。
Dart 是 Flutter 的编程语言,学会 Dart 就掌握了写 Flutter 的一半基础。
目录
- [Dart 是什么?和 JS 的关系](#Dart 是什么?和 JS 的关系)
- [Hello World 与运行方式](#Hello World 与运行方式)
- 变量与类型系统
- 内置类型
- 函数
- 控制流
- [集合:List / Map / Set](#集合:List / Map / Set)
- [空安全(Null Safety)](#空安全(Null Safety))
- 类与面向对象
- 继承、抽象类、接口、Mixins
- 泛型
- [异步编程:Future / Stream / async-await](#异步编程:Future / Stream / async-await)
- [Dart 3 新特性:Records 与模式匹配](#Dart 3 新特性:Records 与模式匹配)
- 其他常用语法糖
- [包管理 pub(对比 npm)](#包管理 pub(对比 npm))
- 前端概念对照速查表
- 动手练习
1. Dart 是什么?和 JS 的关系
| JavaScript / TypeScript | Dart | |
|---|---|---|
| 类型系统 | TS 是 JS 的超集,编译时擦除 | 强类型,类型检查贯穿始终 |
| 运行方式 | 解释执行 / JIT | 开发时 JIT(热重载快),发布时 AOT 编译成机器码 |
| 包管理 | npm / yarn / pnpm | pub |
| 包仓库 | npmjs.com | pub.dev |
| 配置文件 | package.json | pubspec.yaml |
| 编译目标 | 浏览器 / Node | 原生 App(Flutter)、Web、服务端 |
| 异步模型 | Promise / async-await | Future / Stream / async-await |
| this | 动态绑定,坑多 | 无独立 this 绑定问题,类成员直接引用 |
核心心法:把 Dart 当成"更像 Java 的 TypeScript"。 你会 TS,Dart 上手只需几天。
2. Hello World 与运行方式
dart
// main 是程序入口,类似 JS 里的入口文件
void main() {
print('Hello, Dart!');
}
运行方式(就像 node xxx.js):
bash
dart run bin/main.dart # 运行脚本
dart format . # 格式化(类似 prettier)
dart analyze . # 静态检查(类似 eslint + tsc)
在 Flutter 项目中,入口也是 lib/main.dart 里的 main() 函数。
3. 变量与类型系统(对比 TypeScript)
dart
void main() {
// var ------ 类型推断,和 TS 的 let + 类型推断一样
var name = 'xiaoming'; // 推断为 String,之后不能赋其他类型
// name = 123; // ❌ 编译报错
// 显式类型声明
String city = 'Shanghai';
int age = 25;
double price = 9.99;
// final ------ 运行时常量(类似 JS 的 const,赋值后不可变)
final now = DateTime.now();
// const ------ 编译期常量,必须在写代码时就能确定值
const pi = 3.14159;
const list = [1, 2, 3];
// dynamic ------ 关闭类型检查(类似 TS 的 any),尽量别用
dynamic anything = 'hello';
anything = 42;
}
final vs const(重点,JS 没有 const 的细分)
| JS | Dart | |
|---|---|---|
| 运行时确定、不可重新赋值 | const x = f() |
final x = f(); |
| 编译期就确定 | const x = 1 |
const x = 1; |
dart
final t = DateTime.now(); // ✅ 运行时求值
// const t2 = DateTime.now(); // ❌ 编译期无法确定
变量声明对照表
| TypeScript | Dart | 说明 |
|---|---|---|
let x: number = 1 |
int x = 1 |
Dart 数字不分 int/float 变量名 |
let x = 1 |
var x = 1 |
类型推断 |
const x = 1 |
final x = 1 |
运行时常量 |
const x = 1(字面量) |
const x = 1 |
编译期常量 |
any |
dynamic |
尽量避免 |
unknown |
Object? |
任意类型但需检查后使用 |
注意:Dart 没有
let。顶层和类成员可以省略var,局部变量必须有var/类型/final之一。
4. 内置类型
数字:只有 int 和 double
dart
int a = 42;
double b = 3.14;
num c = 10; // num 是 int 和 double 的父类型,类似联合类型 number
c = 3.14; // ✅ 可以再赋 double
// Dart 3.13 支持:数字用 _ 分隔(大数可读性)
int million = 1_000_000;
字符串:支持插值和三引号
dart
var name = 'Dart';
// 单引号双引号等价,和 JS 一样
var s1 = 'hello';
var s2 = "hello";
// 字符串插值:$变量 或 ${表达式}(类似 JS 模板字符串)
var greeting = 'Hello, $name!'; // Hello, Dart!
var sum = '1 + 2 = ${1 + 2}'; // 1 + 2 = 3
var prop = 'len = ${name.length}'; // len = 4
// 多行字符串(类似 JS 的 `` 换行)
var multi = '''
这是第一行
这是第二行
''';
// 原始字符串,不转义(类似 r'' 正则)
var path = r'C:\Users\test'; // \ 不转义
布尔
dart
bool ok = true;
// ⚠️ 重要差异:Dart 不会隐式转换真值!
var name = '';
// if (name) { ... } // ❌ 编译错误!
if (name.isEmpty) { ... } // ✅ 必须显式判断
if (value != null) { ... } // ✅ 判空要显式
这是 JS 转 Dart 最容易踩的坑 :0、''、null 在 JS 里都是 falsy,但在 Dart 里只有 bool 类型的 false 才是假,其他类型根本不能用在 if 里。
5. 函数
Dart 函数是一等公民,和 JS 一样。但参数系统更强。
dart
// 普通函数,返回类型写在前面
int add(int a, int b) {
return a + b;
}
// 箭头函数(类似 JS,但函数体只能是单个表达式)
int add2(int a, int b) => a + b;
void main() {
// 匿名函数 / lambda
var list = [1, 2, 3];
list.forEach((item) {
print(item);
});
list.map((n) => n * 2).toList(); // 类似 JS 的 map
}
参数:可选参数、命名参数(和 JS 差异较大,重点!)
dart
// 1. 位置可选参数:用 [] 包裹,可传可不传
String greet1(String name, [String? title]) {
return '${title ?? ''}$name';
}
greet1('Tom'); // Tom
greet1('Tom', 'Mr. '); // Mr. Tom
// 2. 命名参数:用 {} 包裹,调用时必须写参数名(类似 JS 对象解构传参,但是语法级的)
void createUser({
required String name, // required = 必填
int age = 18, // 有默认值
String? email, // 可空类型
}) {
print('$name, $age, $email');
}
createUser(name: 'Tom', age: 20); // ✅
createUser(email: 'a@b.com', name: 'Jerry'); // ✅ 顺序随意
// createUser('Tom'); // ❌ 必须写 name:
| JS | Dart |
|---|---|
function f(a, b = 1) {} |
void f(int a, [int b = 1]) {} 位置可选 |
function f({a, b}) {} + 解构调用 |
void f({required int a, int? b}) {} 命名参数 |
默认值 b = 1 |
一样,int b = 1 |
| 无 required 标记 | required 关键字强制必填 |
Flutter 里到处都是命名参数 ,比如
Text('hi', style: TextStyle(fontSize: 16)),先适应这种风格。
函数作为参数和返回值
dart
// 函数类型可以显式声明(类似 TS 的类型标注)
int Function(int, int) operation = add;
Function makeCounter() {
var count = 0;
return () => ++count; // 闭包,和 JS 完全一样
}
6. 控制流
大部分和 JS 一致,只讲差异。
dart
// if / else:条件必须是 bool(见第 4 节)
// for 循环:和 JS 一致
for (var i = 0; i < 3; i++) { ... }
for (var item in list) { ... } // 类似 for...of
// switch:Dart 3 的 switch 大升级,支持模式匹配和表达式
String describe(Object obj) {
return switch (obj) {
1 => 'one',
2 || 3 => 'two or three', // 多值匹配
int n when n > 10 => 'big int', // 条件匹配(when 守卫)
String s => 'string: $s',
_ => 'unknown', // _ 相当于 default
};
}
// while / do-while:和 JS 一致
// break / continue:和 JS 一致
7. 集合:List / Map / Set
| JS | Dart |
|---|---|
Array |
List |
Object(当字典用) / Map |
Map |
Set |
Set |
dart
// List ------ 类似 JS 数组
var list = [1, 2, 3];
list.add(4); // push
list.removeAt(0); // shift
list.length; // 长度
list[0]; // 索引访问
list.indexOf(2); // 同 JS
list.where((n) => n > 1).toList(); // filter(注意 where 返回的是惰性 Iterable,要 toList())
list.map((n) => n * 2).toList(); // map
list.reduce((a, b) => a + b); // reduce
list.any((n) => n > 2); // some
list.every((n) => n > 0); // every
list.first; list.last; list.isEmpty; list.isNotEmpty;
// 展开运算符和 null 感知展开(Flutter 布局里天天用)
var a = [1, 2];
var b = [0, ...a]; // [0, 1, 2]
List<int>? maybe;
var c = [0, ...?maybe]; // maybe 为 null 时不报错
// 集合 if / for(Flutter 布局利器)
var showAll = true;
var widgets = [
'header',
if (showAll) 'body', // 条件加入元素
for (var i = 0; i < 3; i++) 'item$i', // 循环生成元素
];
// Map ------ 类似 JS 对象/Map
var map = {'name': 'Tom', 'age': 18};
map['name']; // 访问
map['email'] = 'a@b.c'; // 添加/修改
map.containsKey('name');
map.keys; map.values;
map.forEach((k, v) => print('$k: $v'));
// Set
var set = {1, 2, 3};
set.add(3); // 重复元素自动去重
⚠️ JS 里
Array.isArray/typeof的习惯要改:Dart 用is关键字判断类型,如if (obj is List)。
8. 空安全(Null Safety)------最重要
Dart 3 默认开启空安全,规则和 TS 的 strictNullChecks 一样:null 是独立的类型,不能赋给非空类型。
dart
String a = 'hello';
// a = null; // ❌ 编译错误
String? b = null; // String? 表示可空(同 TS 的 string | null)
b = 'world';
// ── 处理可空值的各种姿势 ──
// 1. 安全调用 ?. (同 JS)
print(b?.length); // b 为 null 时返回 null,不报错
// 2. 空值合并 ?? (同 JS 的 ??)
var len = b?.length ?? 0; // b 为 null 时取 0
// 3. 断言 ! (同 TS 的 !,如果实际是 null 会抛异常,谨慎使用)
var force = b!.length;
// 4. if 判空后,类型自动提升(和 TS 一样的类型收窄)
if (b != null) {
print(b.length); // ✅ 这里 b 被"提升"为 String,不用 ?
}
// 5. 局部变量提升的限制:类成员属性不会提升!
class User {
String? name;
void show() {
// if (name != null) { print(name.length); } // ❌ name 是成员属性,Dart 无法保证中途没被改
if (name != null) {
final n = name!; // 方案1:!
print(n.length);
}
final n2 = name; // 方案2:先赋给局部变量(推荐)
if (n2 != null) print(n2.length);
}
}
// 6. late ------ "稍后初始化"的非空变量(Flutter 状态管理常用)
late String token;
void init() {
token = 'abc'; // 使用前必须先赋值,否则抛异常
}
| 场景 | TypeScript | Dart |
|---|---|---|
| 可空类型 | `string | null` |
| 安全访问 | a?.b |
a?.b |
| 默认值 | a ?? b |
a ?? b |
| 非空断言 | a! |
a! |
| 类型收窄 | if (a !== null) |
if (a != null)(仅局部变量/参数) |
9. 类与面向对象
Dart 的 class 比 ES6 class 更强(更像 TS + Java),所有成员默认都有隐式 getter/setter。
dart
class User {
// 实例字段(必须给类型或 final/var)
String name;
int age;
// 静态字段(同 JS static)
static const maxAge = 150;
// 构造函数:Dart 的经典写法是"参数直接赋给字段"的语法糖
// 等价于 JS 里:constructor(name, age) { this.name = name; this.age = age; }
User(this.name, this.age);
// 命名构造函数 ------ Dart 没有 JS 的函数重载,用命名构造函数代替!
User.guest() : name = 'Guest', age = 0;
// 从 Map 创建(后端返回 JSON 时常用)
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'];
// 方法
void greet() {
print('Hi, I am $name'); // 类内直接用字段名,不需要 this(Flutter 官方 lint 甚至禁止多余的 this)
}
// getter / setter
String get intro => '$name ($age)'; // 计算属性,像 TS 的 get
set nick(String n) => name = n;
// toString
@override
String toString() => 'User($name, $age)';
}
void main() {
var u = User('Tom', 18);
var g = User.guest(); // 调用命名构造函数
var j = User.fromJson({'name': 'Jerry', 'age': 20});
print(u.intro); // Tom (18)
u.nick = 'TOMMY';
}
构造函数进阶
dart
class Point {
final double x, y;
// 1. 命名参数构造函数 ------ Flutter 里最常见的写法
Point({required this.x, required this.y});
// 2. 可选:加默认值
// Point({this.x = 0, this.y = 0});
// 3. 重定向构造函数
Point.origin() : this(0, 0);
// 4. 常量构造函数(所有字段 final 时可用,Flutter 里大量使用)
const Point.zero() : x = 0, y = 0;
}
// const 构造函数配合 const 变量:编译期常量,Flutter 性能优化常用
const p = Point.zero();
// 5. 工厂构造函数 factory ------ 可以返回缓存/子类实例,不一定创建新对象
class Logger {
static final Logger _instance = Logger._internal();
Logger._internal(); // 私有构造函数(_ 开头 = 库内私有)
factory Logger() => _instance; // 单例模式
}
私有化:Dart 没有真正的 private,用 _ 前缀
dart
// Dart 的隐私是"库级别"的:同一个 .dart 文件内的代码都能互相访问
class Secret {
String _hidden = 'private'; // _ 开头 = 对其他文件不可见
String visible = 'public';
}
前端类比:
_前缀 ≈ 约定俗成的_private,但 Dart 有编译器强制(跨文件不可见)。同一项目里看到大量_开头的类/变量/文件,都表示"仅内部使用"。
10. 继承、抽象类、接口、Mixins
dart
// ── 继承 extends(同 JS)──
class Animal {
void eat() => print('eating');
}
class Dog extends Animal {
@override // 重写必须加 @override 注解(lint 强制)
void eat() {
super.eat(); // 同 JS 的 super
print('dog eating');
}
}
// ── 抽象类 abstract(TS 也有)──
abstract class Shape {
double get area; // 抽象成员,子类必须实现
void draw();
}
class Circle extends Shape {
@override
double get area => 3.14;
@override
void draw() => print('draw circle');
}
// ── 接口:Dart 没有 interface 关键字!──
// 每个类都隐式是一个接口,用 implements 实现"接口"
class Fake implements Shape {
@override
double get area => 0; // implements 要求实现所有成员(包括已有的具体实现)
@override
void draw() {}
}
// ── Mixin:多重继承的解决方案(TS 没有的重要特性,Flutter 里极其常用)──
mixin Flyer {
void fly() => print('flying');
}
mixin Swimmer {
void swim() => print('swimming');
}
class Duck extends Animal with Flyer, Swimmer {} // with 关键字混入
// mixin 也可以限制只能被某些类型使用
mixin Walk on Animal { // on Animal:只能混入 Animal 及其子类
void walk() => print('walking');
}
一句话总结:extends 继承一个类,with 混入多个 mixin,implements 实现接口(要求重写所有成员)。
Flutter 里的例子:class _MyHomePageState extends State<MyHomePage>,很多组件会 with SingleTickerProviderStateMixin。
11. 泛型
和 TS 泛型几乎一样。
dart
// 泛型函数
T first<T>(List<T> items) => items[0];
// 泛型类
class Box<T> {
T value;
Box(this.value);
}
// 泛型约束(类似 TS 的 extends 约束)
T max<T extends num>(T a, T b) => a > b ? a : b;
// 使用
var box = Box<int>(42);
var n = max(3, 5); // 类型自动推断
12. 异步编程:Future / Stream / async-await
好消息:async/await 语法和 JS 完全一样。区别在于类型名。
| JS | Dart | 说明 |
|---|---|---|
Promise<T> |
Future<T> |
表示"将来会有一个值" |
| --- | Stream<T> |
表示"将来会有多个值"(类似事件流/RxJS Observable) |
async/await |
async/await |
一模一样 |
Promise.all |
Future.wait |
并发等待多个 |
try/catch + await |
一样 | 错误处理一致 |
dart
// Future 基础
Future<String> fetchUser() async {
await Future.delayed(Duration(seconds: 1)); // 类似 sleep,模拟网络请求
return 'Tom';
}
void main() async {
var user = await fetchUser(); // 同 JS await
print(user);
}
// Future 的链式调用(类似 .then,不推荐,用 await 更清晰)
fetchUser().then((u) => print(u)).catchError((e) => print(e));
// 并发请求(类似 Promise.all)
var results = await Future.wait([fetchUser(), fetchUser()]);
// 错误处理(同 JS)
try {
await fetchUser();
} catch (e) {
print('error: $e');
} finally {
print('done');
}
// Stream:多个异步值(JS 没有直接对应物,可理解为内置的 Observable)
Stream<int> countStream() async* { // async* 生成器函数
for (var i = 0; i < 3; i++) {
yield i; // 类似 RxJS 的 next
await Future.delayed(Duration(seconds: 1));
}
}
void listen() async {
await for (var n in countStream()) { // await for:逐个消费
print(n);
}
// 或者
// countStream().listen((n) => print(n));
}
单线程事件循环 :Dart 和 JS 一样是单线程事件循环模型,
Future微任务/事件队列的调度思想和 JS 的宏任务/微任务非常接近,你的异步直觉可以直接复用。
13. Dart 3 新特性:Records 与模式匹配
(Dart 3 以后才有,本项目 SDK 3.13 支持)
Records:轻量元组(JS 没有对应物,TS 的 tuple 类型)
dart
// 返回多个值,不用定义类了
(String, int) getUserInfo() {
return ('Tom', 18);
}
void main() {
var (name, age) = getUserInfo(); // 解构
print('$name is $age');
// 具名字段
({String name, int age}) user = (name: 'Jerry', age: 20);
print(user.name);
}
模式匹配:解构 + 匹配
dart
// switch 表达式 + 解构
var json = {'name': 'Tom', 'age': 18};
switch (json) {
case {'name': String n, 'age': int a}:
print('$n $a');
default:
print('unknown');
}
// 列表模式
var list = [1, 2, 3];
var [first, second, ...rest] = list; // 类似 JS 数组解构
// 对象模式
if (user case User(name: var n, age: >= 18)) { // 匹配 + 条件
print('adult: $n');
}
14. 其他常用语法糖
dart
// ── 级联操作符 .. :对同一对象连续操作(JS 没有)──
var btn = Button()
..text = 'Click'
..width = 100
..onClick = () => print('clicked');
// 等价于 btn.text = ...; btn.width = ...; 每次都返回对象本身
// ── 条件成员访问与赋值 ──
map['count'] ??= 0; // 为 null 才赋值
// ── 三元运算符:同 JS ──
var msg = age >= 18 ? 'adult' : 'minor';
// ── 类型判断与转换 ──
if (obj is String) { ... } // 类似 typeof/instanceof,类型收窄
if (obj is! String) { ... } // 非
var s = obj as String; // 强制转换,类似 <string>obj
// ── 注解 ──
@override // 重写父类成员
@deprecated // 标记弃用
常用工具方法
dart
// DateTime
var now = DateTime.now();
var later = now.add(Duration(days: 1));
now.isBefore(later); // true
now.difference(later).inHours;
// Duration(时间长度)
Duration(seconds: 90);
// 字符串
'tom'.toUpperCase();
' a b '.trim();
'a,b,c'.split(',');
var joined = [1, 2].join('-'); // '1-2'
'int'.padLeft(5); // ' int'
// num
var x = 3.7;
x.round(); // 4
x.floor(); // 3
3.toString(); // '3'
int.parse('42'); // 42
double.tryParse('abc'); // null(不抛异常的 parse 系列)
15. 包管理 pub(对比 npm)
yaml
# pubspec.yaml ------ 相当于 package.json
name: flutter_application_1
environment:
sdk: ^3.13.5 # Dart 版本约束(同 npm 的 engines)
dependencies: # 类似 dependencies
http: ^1.2.0 # ^ 同 npm 语义:允许小版本更新
cupertino_icons: ^1.0.8
dev_dependencies: # 类似 devDependencies
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
| npm 命令 | pub 命令 |
|---|---|
npm install |
flutter pub get(或 dart pub get) |
npm install <pkg> |
在 pubspec.yaml 添加依赖后 pub get |
npm update |
flutter pub upgrade |
npm outdated |
flutter pub outdated |
npx <cmd> |
dart run <pkg>:<cmd> 或 flutter pub run |
| package.json | pubspec.yaml |
| node_modules | 无全局缓存目录(.dart_tool 存配置) |
| npmjs.com | pub.dev |
| lock 文件 package-lock.json | pubspec.lock |
| eslint | flutter_lints(在 analysis_options.yaml 配置) |
16. 前端概念对照速查表
| 你熟悉的 | Dart / Flutter 对应 |
|---|---|
let / var |
var(局部)/ 直接写字段 |
const(对象) |
final |
const(字面量) |
const |
=== |
==(Dart 没有三等号,== 默认就是严格比较;类可重写 ==) |
typeof / instanceof |
is |
| 类型断言 | as |
| `string | null` |
Array |
List |
| 对象当字典 | Map |
模板字符串 ${x} |
插值 '$x' / '${x.y}' |
Promise |
Future |
async/await |
async/await(一模一样) |
RxJS Observable |
Stream |
Promise.all |
Future.wait |
| npm | pub |
| package.json | pubspec.yaml |
class 私有字段 #x |
_x(库级私有) |
| 构造函数重载 | 命名构造函数 Class.name() |
mixin(TS 的 implements 混合) |
mixin + with(一等公民支持) |
| 解构赋值 | 记录解构 / 模式匹配 |
| 真值判断陷阱 | if 条件必须是 bool,改掉 JS 习惯 |
17. 动手练习
把下面的代码保存为 bin/practice.dart,用 dart run 运行验证学习成果:
dart
// 练习:实现一个简单的用户管理系统
// 要求:
// 1. 定义 User 类,包含 name(必填)、age(默认 18)、tags(可选列表)
// 2. 提供命名构造函数 User.fromJson
// 3. 提供 getter:isAdult
// 4. 写一个函数 findAdults(List<User>) 返回所有成年人
// 5. 用 async/await 模拟异步加载用户列表并打印
class User {
final String name;
final int age;
final List<String> tags;
User({required this.name, this.age = 18, this.tags = const []});
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'] ?? 18,
tags = List<String>.from(json['tags'] ?? []);
bool get isAdult => age >= 18;
@override
String toString() => 'User($name, $age)';
}
List<User> findAdults(List<User> users) =>
users.where((u) => u.isAdult).toList();
Future<List<User>> loadUsers() async {
await Future.delayed(const Duration(seconds: 1));
return [
User(name: 'Tom', age: 20, tags: ['admin']),
User.fromJson({'name': 'Jerry', 'age': 16}),
User.fromJson({'name': 'Spike'}),
];
}
void main() async {
final users = await loadUsers();
print('全部用户: $users');
print('成年人: ${findAdults(users)}');
}
下一步
学完本教程后,建议继续学习:
- Flutter 基础:Widget、State、BuildContext、布局系统(Row/Column/Stack)
- 状态管理:setState → Provider → Riverpod/BLoC
- 官方文档:https://dart.dev/language (语言之旅,交互式示例)
本教程基于 Dart 3.x(SDK ^3.13.5)编写,与项目 pubspec.yaml 环境一致。