一、前言:为什么值得花时间学 Dart
几年前问"要不要学 Dart",答案通常取决于"你做不做 Flutter"。到 2026 年,这个绑定关系松了不少。Dart 现在有自己的服务端框架、命令行工具链,既能把代码编译成原生机器码,也能编译成 JavaScript。当然,它最出圈的舞台依然是 Flutter——Google 的跨平台 UI 框架。
这篇文章写给两类人。一类是有其他语言基础、想快速把 Dart 摸个遍的开发者;另一类是正准备写 Flutter、却被 Dart 的语法细节绊住的人。整体思路是:先说清它是门什么语言,再讲怎么写,最后讲怎么用得顺手、用得好。文中代码都可以直接丢进 DartPad 或本地环境跑起来。
按照惯例交代一下时效:截至 2026 年 9 月,Dart 的稳定主版本是 3.13(8 月正式发布,SDK 补丁已跟进到 3.13.x),配套 Flutter 3.47 系列。本文以 3.13 为准,同时会把 3.x 一路走来的关键变化串起来讲。
二、Dart 是一门什么样的语言
2.1 出身与定位
Dart 由 Google 开发,2011 年首次发布。它是一门面向对象、类 C 风格、单继承的静态强类型语言。说人话就是:语法跟 Java、JavaScript、C# 有几分像,有类型检查兜底,但写起来比 Java 轻快。
它有几个绕不开的特点:
- 静态类型 + 类型推断。你可以写全类型,也可以让编译器自己猜,安全和效率兼顾。
- 双编译模式。开发时用 JIT,改完代码热重载,秒级看到效果;发布时用 AOT,编译成机器码,跑得快。
- 一切皆对象。连数字、函数、
null都是对象,所有类型最终都继承自Object。 - 空安全。从 Dart 2.12 起默认开启,非空变量不能塞
null,把空指针异常挡在编译期。 - 跨平台。同一套代码可以跑在移动端、Web、桌面端、服务端和嵌入式设备上。
2.2 双编译模式:JIT 与 AOT
这是 Dart 区别于很多语言的地方,值得单独说。
JIT(Just-In-Time,即时编译) 在程序运行时编译代码,代价是启动慢一点,好处是支持热重载。你改一行 UI,界面立刻更新,状态还能保留。这个特性撑起了 Flutter 的开发体验。
AOT(Ahead-Of-Time,提前编译) 在打包时就把代码编译成目标平台的机器码。启动快、运行快,代价是没有热重载。上线发布用的就是它。
同一门语言,两套编译路径,各管一段——这是 Dart 设计里比较讨巧的一步。
2.3 空安全:把空指针挡在门外
空指针异常大概是最让人头疼的运行时错误之一。Dart 的做法很直接:变量默认不可为空,你要么保证它有值,要么显式声明它"可以为空"。
String name = 'Dart'; // 非空,永远是字符串
String? nickname = null; // 可空,可能是字符串,也可能没有声明成可空类型之后,编译器就会盯着你,逼你在使用前先处理 null。这套机制一上来会让人觉得别扭,写久了反而省心——因为那些原本会在半夜炸掉的 bug,很多在敲代码时就暴露了。
三、准备好你的环境
3.1 安装 Dart SDK
装 Dart 有两条路,看你的目标选。
如果专注 Flutter,装 Flutter SDK 就够了,它会自带一份匹配版本的 Dart SDK,不用单独折腾。
如果只想先学语言本身,或者做后端、命令行工具,那就单独装 Dart SDK:
# macOS(Homebrew)
brew install dart
# Windows(Chocolatey)
choco install dart-sdk
# Linux(Debian/Ubuntu 官方源)
sudo apt-get update
sudo apt-get install dart装完验证一下:
dart --version能打印出类似 Dart SDK version: 3.13.x (stable) 的信息,就说明成了。
3.2 不想装软件?用 DartPad
如果只是想快速试一段代码,不必安装任何东西。打开浏览器访问 dartpad.dev(国内可用 dartpad.cn),在线写、在线跑。本文里不少片段都可以直接粘过去试。
3.3 编辑器
首推 VS Code,装官方 Dart 插件就行,代码补全、错误提示、调试都有。做 Flutter 的话再补一个 Flutter 插件。Android Studio / IntelliJ IDEA 同样支持,适合习惯了 JetBrains 全家桶的人。
3.4 第一个程序
按传统,先来一句问候。新建 hello.dart:
void main() {
print('Hello, Dart!');
}在终端里跑:
dart hello.dart几点说明:main() 是程序入口,每个 Dart 程序都从它开始;print() 往控制台输出内容;每行语句以分号结尾。就这么多,你已经跑通第一个 Dart 程序了。
四、语法基础
4.1 变量与常量
Dart 声明变量有好几种方式,用在什么场景是有讲究的。
// 显式声明类型:清晰,适合团队协作和大型项目
String name = 'Alice';
int age = 25;
double height = 1.75;
bool isStudent = true;
// var:让编译器推断类型,最常用
var city = 'Beijing'; // 推断为 String
// final:运行时常量,只能赋一次值
final now = DateTime.now();
// const:编译时常量,值必须在编译期就确定
const double pi = 3.14159;var 和 dynamic 容易搞混,但差别很大。var 只是省去了你手写类型,一旦推断出来,类型就固定了;dynamic 则是真的关掉类型检查,变量运行时能装任何东西。后者用多了会丢掉类型安全,容易招来运行时错误,没事别碰。
var value = 10;
// value = 'hello'; // 报错:类型不匹配,String 不能赋给 int
dynamic flexible = 'hello';
flexible = 123; // 运行期允许,但类型安全没了final 和 const 的区别也常被问到。简单记:final 是"运行期确定、只赋一次",const 是"编译期就确定、完全不可变"。所以 final now = DateTime.now(); 合法,const now = DateTime.now(); 不行——因为 DateTime.now() 的值编译时算不出来。
4.2 内置类型
Dart 的内置类型不算多,常用的就下面这些:
int count = 0; // 整数
double price = 19.99; // 浮点数
String title = 'Dart'; // 字符串
bool done = false; // 布尔值
List<int> numbers = [1, 2, 3]; // 有序列表(类似数组)
Set<String> tags = {'dart', 'flutter'}; // 无序、元素唯一
Map<String, int> scores = {'math': 90, 'english': 85}; // 键值对int 和 double 都是 num 的子类型,两者能一起参与算术运算。List、Set、Map 这三个集合类型,在 Dart 里出现频率极高,后面写业务代码基本离不开它们。
late 关键字值得提一句,用来延迟初始化:
late String description;
void init() {
description = '稍后才会赋值';
}它适合"确定会在使用前赋值、但没法在声明时给值"的场景。不过要小心——如果在赋值前就访问 late 变量,会抛 LateInitializationError。
4.3 字符串与插值
Dart 的字符串支持单引号也支持双引号,日常用单引号更常见。真正方便的是字符串插值:
String user = 'Tom';
String greeting = 'Hello, $user!'; // 简单插值
String detail = '名字长度:${user.length}'; // 表达式插值
String multi = '''
第一行
第二行
''';$变量名 直接取值,${表达式} 可以塞进更复杂的计算。多行字符串用三个引号包起来,写模板、写 SQL 都很顺手。
4.4 运算符与空安全
Dart 有几个跟空安全配套的运算符,用熟了能省很多 if:
String? nickname;
print(nickname?.length); // 安全访问:nickname 为 null 时整体返回 null,不报错
print(nickname ?? '匿名'); // 空值合并:左边为 null 就用右边
nickname ??= '默认昵称'; // 空值赋值:仅当左边为 null 时才赋值
int? maybe;
int value = maybe!; // 非空断言:告诉编译器"它一定不为空"! 这个非空断言要谨慎用。它相当于对编译器说"别管了,我保证有值",一旦你保证错了,运行时照样崩。能用 if 判断或者 ?? 兜底的地方,就别图省事用 !。
另外两个常用符号是展开运算符 ... 和级联运算符 ..:
var base = [1, 2, 3];
var more = [0, ...base, 4]; // 展开后是 [0, 1, 2, 3, 4]
final buffer = StringBuffer()
..write('Hello')
..write(', ')
..write('Dart'); // 级联:连续调用,不用反复写变量名4.5 控制流
if、for、while 这些跟大多数语言差不多。switch 在 Dart 3 里被大幅强化,可以直接当表达式返回结果:
String describe(int code) {
return switch (code) {
200 => '成功',
404 => '未找到',
_ => '未知状态',
};
}_ 是通配符,兜住所有没列举的情况。这种写法比一串 if-else 干净不少,而且因为要覆盖所有分支,漏了情况编译器会提醒你。
五、函数
5.1 基本写法与箭头函数
int add(int a, int b) {
return a + b;
}
// 单表达式可以简写成箭头函数
int multiply(int a, int b) => a * b;5.2 可选参数
Dart 的可选参数分两种,位置可选和命名可选。
// 位置可选参数:用方括号包起来
void greet(String name, [String? title]) {
print('Hello, $title $name');
}
// 命名可选参数:用花括号包起来,调用时必须写参数名
void createUser({String name = 'Guest', int age = 18}) {
print('$name, $age');
}
void main() {
greet('Tom');
greet('Tom', 'Mr.');
createUser();
createUser(name: 'Alice', age: 30);
}命名参数配上默认值,可读性非常好,尤其是在参数比较多的时候。Flutter 里几乎满屏都是这种写法。如果某个命名参数不给默认值又必须传,用 required 标记:
void update({required String id, String? note}) {
// id 必传,note 可选
}5.3 函数也是对象
在 Dart 里,函数是一等公民,可以赋值给变量、当参数传、当返回值返回。
final nums = [1, 2, 3, 4, 5];
final even = nums.where((n) => n.isEven).toList(); // 传入匿名函数做过滤
int apply(int x, int Function(int) transform) => transform(x);这种把函数当参数传递的风格,在集合操作里特别常见。where、map、reduce 之类的链式调用,写起来比手写循环清楚得多。
六、面向对象
6.1 类与构造函数
class User {
final String name;
int age;
// 构造函数的语法糖:直接给字段赋值
User(this.name, this.age);
// 命名构造函数
User.guest()
: name = 'Guest',
age = 0;
@override
String toString() => 'User($name, $age)';
}
void main() {
final u = User('Alice', 25);
print(u);
}User(this.name, this.age) 这种简写省掉了构造函数体里一堆 this.name = name,Dart 里非常常用。
6.2 继承、抽象与接口
Dart 是单继承,一个类只能 extends 一个父类,但可以实现多个接口。
abstract class Shape {
double area(); // 抽象方法,没有方法体
void describe() {
print('面积是 ${area()}'); // 抽象类里也可以有具体方法
}
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
@override
double area() => 3.14 * radius * radius;
@override
void describe() => print('圆的面积是 ${area()}');
}extends 是"继承并复用父类的实现",implements 是"只认规矩、不复用实现"。如果子类想用父类的现成逻辑,用 extends;如果只是把抽象类当接口用,就 implements。
6.3 mixin:组合能力的利器
Dart 不支持多继承,但提供了 mixin 来"混入"功能,把一组能力插到多个毫不相干的类里。
mixin Walker {
void walk() => print('walking');
}
mixin Swimmer {
void swim() => print('swimming');
}
class Duck with Walker, Swimmer {}
void main() {
final d = Duck();
d.walk();
d.swim();
}一个类可以 with 多个 mixin。当多个 mixin 有同名方法时,写在后面的会覆盖前面的——顺序是有意义的。Flutter 框架里到处能看到 mixin 的影子,很多"功能组合"就是靠它实现的。
6.4 扩展方法
扩展方法能在不改动原类的前提下,给它"加"方法。这招对付第三方库特别管用。
extension StringX on String {
bool get isEmail => contains('@') && contains('.');
}
void main() {
print('a@b.com'.isEmail); // true
}6.5 泛型
泛型把"类型"也变成参数,保证类型安全的同时复用代码。
T first<T>(List<T> items) => items.first;
class Box<T> {
final T value;
Box(this.value);
}
void main() {
final b = Box<String>('hello');
print(b.value);
}泛型还能加约束,限制 T 必须是某个类的子类:
abstract class Animal {
void eat();
}
void feed<T extends Animal>(T animal) => animal.eat();这样写的好处是,在 feed 里可以直接调用 Animal 才有的方法,编译期就把类型错误拦住了。
七、Dart 3 以来的现代特性
Dart 3 是这门语言历史上改动最大的一次。它一口气带来了 Records、Patterns、Class modifiers 等特性。这些不是纯粹的语法糖,它们实实在在改变了组织代码的方式。我挑几个最值得掌握的讲。
7.1 Records:函数返回多个值不再尴尬
以前一个函数想返回多个值,要么专门造个类,要么用 Map 凑合,要么引第三方 Tuple 库。Dart 3 的 Record 直接把这个痛点解决了。
// 位置记录
(String, int) getUser() => ('Alice', 25);
// 命名记录,字段有名字,可读性更好
({String name, int age}) getProfile() => (name: 'Bob', age: 30);
void main() {
final user = getUser();
print(user.$1); // Alice
print(user.$2); // 25
final profile = getProfile();
print(profile.name); // Bob
}位置记录用 .$1、.$2 取值,命名记录直接用字段名。返回多个值又不想为此定义类的时候,Record 是首选。
7.2 Patterns:解构与匹配
模式匹配让解构数据变得非常自然,尤其是配合 Record 和集合使用。
var json = {'code': 200, 'data': [1, 2, 3]};
if (json case {'code': 200, 'data': List<int> data}) {
print('数据长度 ${data.length}');
}
final list = [1, 2, 3];
final [a, b, c] = list; // 直接解构
print('$a $b $c');上面那段 if ... case 的做法,等于一边检查结构、一边把值取出来。以前得写好几句判空和类型转换,现在一行就能搞定。
7.3 Sealed class:让状态建模更严谨
sealed 密封类常用来表示"有限的几种可能状态",每个子类可以携带完全不同的数据。
sealed class Result {}
class Success extends Result {
final String data;
Success(this.data);
}
class Failure extends Result {
final String message;
Failure(this.message);
}
String render(Result r) => switch (r) {
Success(:var data) => 'OK: $data',
Failure(:var message) => 'Error: $message',
};妙处在于:因为 Result 是密封的,编译器知道它只有 Success 和 Failure 两种子类,switch 一旦漏掉某一种,就会在编译期报错。这种"穷尽性检查"能把大量的状态遗漏问题提前暴露出来。
7.4 Class modifiers:把 API 的边界说清楚
Dart 3 给类加了一组修饰符,用来表达"这个类允许别人怎么用"。
// 不允许被继承、也不允许被实现,就是个完整实现
final class Config { ... }
// 不允许被实现,但可以被继承
base class Service { ... }
// 纯接口,只允许被实现
interface class Repository { ... }这些修饰符看似不起眼,用在库的设计上却很关键——它让"哪些类能被继承、哪些只能被实现"变成写死在代码里的约定,而不是靠文档口口相传。
7.5 3.12 的私有命名参数
写不可变数据类时有个老麻烦:字段是私有的(下划线开头),但命名参数又不能以 _ 开头,于是只能加一层公开参数再手动赋值。Dart 3.12 把这个别扭劲消掉了:
class User {
final String _name;
final int _age;
// 字段是私有的,调用方却能用干净的 name、age 传参
User({required this._name, required this._age});
}
void main() {
final u = User(name: 'Asher', age: 18);
}保留私有字段的同时,构造 API 依然整洁,对数据模型、DTO、配置类这类场景很实用。
7.6 3.13 的 Primary Constructors
这是 3.13 里我个人觉得最顺手的一项。以前写个普通数据类,字段和构造函数总要把同一套信息写两遍:
// 老写法
class Point {
final int x;
final int y;
Point(this.x, this.y);
}现在可以一行搞定:
// 3.13 新写法
class Point(final int x, final int y);final int x 既声明了构造参数,也顺手定义了实例字段;改成 var 就是可变字段。3.13 还顺带给了构造函数的简写形式——在类里可以用 new 代替重复的类名:
class Point {
double x;
double y;
new(this.x, this.y); // 等价于 Point(this.x, this.y)
new origin() : x = 0, y = 0; // 等价于 Point.origin()
}官方还配了一堆 lint 和 IDE 重构,帮你把旧构造自动迁到新写法上。要不要立刻全换,看团队习惯就好,但这项能力值得先了解。
八、异步编程
Dart 是单线程语言,但它的异步能力相当扎实。理解这一块,是写好 Flutter 应用的关键。
8.1 事件循环
Dart 内部跑着一个事件循环,处理两类队列:
- 微任务队列(microtask queue),优先级高,主要装 Dart 内部生成的小任务。
- 事件队列(event queue),装 I/O、网络请求、定时器、用户输入这些外部事件。
每一轮循环,Dart 先把微任务清空,再取一个事件队列的任务执行,如此往复。这也解释了一个常见现象:如果微任务堆得太多,就会拖慢后续的界面渲染,造成卡顿。
8.2 Future 与 async、await
Future 表示"一个将来才会有结果的计算"。async 和 await 是用来写异步代码的语法糖,让异步逻辑看起来像同步的。
Future<String> fetchName() async {
await Future.delayed(const Duration(seconds: 1));
return 'Dart';
}
Future<void> main() async {
print('开始');
final name = await fetchName();
print('拿到: $name');
}关键点:await 会暂停当前函数的执行,但不会阻塞整个程序——事件循环该处理别的事情照样处理。等 Future 完成,函数再从暂停处继续。这就是为什么异步代码能让界面保持流畅。
8.3 异步里的错误处理
异步代码的错误,用 try-catch 抓就行:
Future<int> parse(String raw) async {
try {
return int.parse(raw);
} on FormatException catch (e) {
print('解析失败: $e');
rethrow;
} finally {
print('完成');
}
}有一点容易踩坑:await 之前抛出的错误不会被这个 try 捕获,所以异步逻辑要尽量写在 try 里面。
8.4 Stream:处理连续的事件流
Future 对应"一次性的结果",Stream 对应"一串陆续到达的事件"。读文件、监听传感器、接收用户输入,都是 Stream 的典型场景。
Stream<int> countUp(int max) async* {
for (var i = 1; i <= max; i++) {
await Future.delayed(const Duration(milliseconds: 200));
yield i;
}
}
Future<void> main() async {
await for (final n in countUp(3)) {
print(n);
}
}async* 加 yield 用来生成流,await for 用来消费流。也可以用 listen 订阅:
final controller = StreamController<int>();
controller.stream
.where((n) => n.isEven)
.map((n) => n * 10)
.listen((value) => print(value));
controller.add(1);
controller.add(2);Stream 分两种:单订阅流(默认,只能被监听一次,适合文件读取这类不能丢数据的场景)和广播流(可以被多个监听器订阅,适合事件分发)。
九、并发与 Isolate
异步解决的是"等待外部结果",但它并不会把计算搬到别的线程。如果有个 CPU 密集任务——解析超大 JSON、加密、图像处理——放在主 Isolate 里跑,界面照样会卡。
Dart 的解法是 Isolate。它是一种独立的执行单元,有自己的内存和事件循环,Isolate 之间不共享可变内存,只能通过消息传递通信。代价是通信有成本,收益是彻底避开了共享状态带来的竞态问题。
创建 Isolate 最省事的方式是 Isolate.run:
import 'dart:isolate';
Future<void> main() async {
final result = await Isolate.run(() {
var sum = 0;
for (var i = 0; i < 1000000; i++) {
sum += i;
}
return sum;
});
print(result);
}如果需要在 Isolate 之间长期通信,就用底层的 Isolate.spawn,手动管理收发端口:
import 'dart:isolate';
void worker(SendPort mainPort) {
final receive = ReceivePort();
mainPort.send(receive.sendPort);
receive.listen((msg) => mainPort.send('收到: $msg'));
}
Future<void> main() async {
final receive = ReceivePort();
await Isolate.spawn(worker, receive.sendPort);
final sendPort = await receive.first as SendPort;
sendPort.send('hello');
}在 Flutter 里还有个 compute() 函数,适合一次性的 CPU 密集任务,会自动创建和销毁 Isolate,连端口都不用管。
判断标准很简单:一个任务耗时明显超过一帧(约 16 毫秒),就该考虑丢进 Isolate。
十、包管理与工程化
10.1 pubspec.yaml:项目的心脏
Dart 用 pub 管理依赖,配置文件是 pubspec.yaml。
name: my_app
description: 一个演示项目
version: 1.0.0
environment:
sdk: ^3.13.0
dependencies:
http: ^1.2.0
dev_dependencies:
test: ^1.25.0
lints: ^4.0.0版本号前面的 ^ 表示"兼容这个主版本下的最新版",比如 ^1.2.0 允许装到 1.x 的最新,但不会越到 2.0。dependencies 是生产依赖,dev_dependencies 只在开发和测试时用。公共包都托管在 pub.dev 上。
常用命令:
dart pub get # 拉取依赖
dart pub add http # 添加一个依赖
dart pub upgrade # 升级到允许范围内的最新版本10.2 库的组织与导入
Dart 用 import 引入代码。常见几种写法:
import 'dart:async'; // 引入 SDK 内置库
import 'dart:math' as math; // 起别名,避免命名冲突
import 'package:http/http.dart' as http; // 引入第三方包
import 'src/utils.dart' show formatDate; // 只引入指定内容
import 'src/legacy.dart' hide oldApi; // 排除指定内容以 dart: 开头的是 SDK 库,以 package: 开头的是包,其余按相对或绝对路径找。一个库里的私有标识符以下划线 _ 开头,外部访问不到。
10.3 测试、格式化与静态分析
Dart 的工具链是它的一大加分项,几乎开箱即用。
写测试:
import 'package:test/test.dart';
void main() {
test('加法正确', () {
expect(1 + 1, 2);
});
}跑测试和做检查:
dart test # 运行测试
dart format . # 统一代码风格
dart analyze # 静态分析,提前发现潜在问题dart format 会按官方风格把代码排版好,团队协作时省掉大量格式争论。dart analyze 则能揪出不少隐藏问题,建议纳入日常流程。
10.4 编译与部署
dart compile exe bin/main.dart # 编译成原生命令行可执行文件
dart compile js web/main.dart # 编译成 JavaScript,跑在浏览器
dart compile aot-snapshot main.dart # 编译成 AOT 快照dart compile exe 产出的可执行文件自带运行时,双击就能跑,不需要目标机器另外装 Dart。发布服务端工具或者命令行程序时很方便。
十一、学习路线:从入门到精通
把上面这些串成一条路线,大致可以分四个阶段。
第一阶段:写出能跑的代码。 把环境装好,搞懂变量、类型、控制流、函数。这个阶段的重点是"手感",多敲多改,别只看不练。
第二阶段:把面向对象吃透。 类、继承、抽象类、接口、mixin、扩展方法、泛型,这些是读懂 Dart 代码库的基础。Flutter 代码里到处都是它们的影子。
第三阶段:掌握异步与并发。 Future、async/await、Stream、Isolate,这是从"会写"到"写得好"的分水岭。很多卡顿、崩溃、内存问题,根子都在异步。
第四阶段:拥抱现代特性和工程化。 Records、Patterns、sealed class、class modifiers,加上依赖管理、测试、静态分析、编译发布。到了这一步,你面对的不只是语法,而是一整套工程习惯。
如果目标是 Flutter,那在第二、三阶段之后,就该把重心逐步挪到 widget 体系、状态管理和平台能力上了。Dart 是地基,Flutter 是楼。
十二、Dart 的生态与未来
聊到最后,绕不开 2026 年的大背景:AI。
Flutter 和 Dart 这两年明显在往 AI 工程化方向靠。官方的 Dart MCP 让 AI 助手能读懂项目结构、执行热重载调试、管理依赖、跑测试;Genkit-Dart 支持在 Dart 里编排 Flow、Tool、Prompt 和 RAG;再叠加上 Flutter Skills 之类的能力,AI 辅助开发的体验提升了不少。官方在 2025 年 Flutter 用户调查里给出的数字是:79% 的 Flutter 开发者会用 AI 助手。这股趋势短期内不会停。
当然,Dart 的根基还是那套跨平台能力。移动端、桌面端、Web、服务端,一套代码覆盖多个平台,配合成熟的工具链和活跃的包生态,这是它这些年能把位置坐稳的原因。语言本身也在往前走——3.x 系列每年都在加实用的语法糖和编译优化,节奏比前几年活跃得多。
十三、结语
Dart 不是那种一眼惊艳的语言。它更像一个踏实的手艺人:类型系统稳妥,异步模型清晰,工具链齐全,遇到问题查文档基本能解决。真要说门槛,大概就是空安全和异步这两块需要花点时间适应——但这两块恰恰是它能帮你少踩坑的地方。
如果你从这篇文章里只带走一句话,我希望是:先把基础语法和空安全写熟,再啃异步和并发,最后用 Dart 3 的现代特性把代码写得又短又稳。 剩下的,交给手上的项目去磨。
顺手记一下手边常用的命令,需要的时候翻出来用:
dart --version # 查看版本
dart create -t console my_app # 新建控制台项目
dart run # 运行当前项目
dart test # 跑测试
dart format . # 格式化
dart analyze # 静态分析
dart compile exe bin/main.dart # 编译成可执行文件愿你在 Dart 和 Flutter 的世界里,少踩几个坑,多留几分从容。