Dart 语法速查
Flutter 使用 Dart 语言开发。本文按照从简单到复杂的顺序,逐一讲解 Dart 最核心的语法,每一条都配有「是什么 → 怎么写 → 什么时候用」的说明。
1. 变量:存数据的容器
1.1 声明方式
Dart 有三种声明变量的关键字,选择逻辑很简单:
| 关键字 | 含义 | 何时用 |
|---|---|---|
var | 让 Dart 自动猜类型 | 大多数场景(推荐) |
final | 赋值一次后不能再改 | 运行时才能确定的值(如网络请求结果) |
const | 编译时就确定的常量 | 永远不变的值(如数学常量) |
// var —— 自动推断类型,最常用
var name = 'Flutter'; // Dart 自动知道 name 是 String
var count = 42; // Dart 自动知道 count 是 int
// final —— 只能赋值一次,之后不能改
final now = DateTime.now(); // 运行时才有值,用 final
// const —— 编译时就确定,永远不变
const pi = 3.14; // π 永远不变,用 const💡 简单记忆:能用
const就用const,否则用final,都不合适再用var。
1.2 基本数据类型
Dart 常用的基础类型只有四个:
String text = '你好'; // 字符串,用单引号或双引号
int age = 18; // 整数
double pi = 3.14; // 小数(没有 float,只有 double)
bool ok = true; // 布尔值,只有 true / false1.3 空安全(Null Safety)
这是 Dart 和很多语言不同的地方:默认情况下变量不能为 null。
String name = 'Tom';
// name = null; ❌ 编译报错!String 不允许为空
// 如果确实需要"可能没有值",加个问号
String? nickname; // ✅ String? 表示"字符串或空"
nickname = null; // ✅ 允许
nickname = 'Jack'; // ✅ 也允许使用可空变量时,需要安全处理:
String? nickname;
// 方式一:用 ?. 安全调用(如果是 null 就不执行,返回 null)
int? len = nickname?.length; // nickname 为 null 时,len 也为 null
// 方式二:用 ?? 提供默认值(如果是 null 就用后面的值)
int len = nickname?.length ?? 0; // nickname 为 null 时,len = 0
// 方式三:用 ! 强制断言非空(确定不为 null 时才用,否则报错)
// String upper = nickname!.toUpperCase(); ⚠️ 确定不为 null 才能用💡 记住一句话:加
?表示"可以为空",不加就不能为空。这是 Dart 防止空指针错误的核心机制。
2. 字符串:最常用的操作
var name = 'Flutter';
var version = 3;
// 字符串插值:用 $ 变量名 或 ${表达式}
print('Hello $name'); // Hello Flutter
print('Version: $version'); // Version: 3
print('Upper: ${name.toUpperCase()}'); // Upper: FLUTTER
// 多行字符串
var message = '''
第一行
第二行
第三行
''';
// 字符串拼接
var full = 'Hello' + ' ' + 'World'; // 'Hello World'3. 集合:装多个数据
3.1 List —— 有序列表(最常用)
// 创建
var fruits = ['苹果', '香蕉', '橘子']; // 自动推断为 List<String>
// 增删改查
fruits.add('葡萄'); // 末尾添加
fruits.remove('香蕉'); // 按值删除
fruits[0]; // 取第一个 → '苹果'
fruits.length; // 长度 → 3
// 遍历
for (var fruit in fruits) {
print(fruit);
}3.2 Map —— 键值对(类似字典)
// 创建
var scores = {
'Alice': 90,
'Bob': 85,
};
// 增删改查
scores['Charlie'] = 95; // 添加 / 修改
scores.remove('Bob'); // 删除
scores['Alice']; // 取值 → 90
// 遍历
scores.forEach((key, value) {
print('$key: $value');
});3.3 Set —— 不重复的集合
var numbers = {1, 2, 3, 3, 4}; // 自动去重 → {1, 2, 3, 4}
numbers.add(5);
numbers.contains(3); // true3.4 常用集合操作
这些方法在 Flutter 开发中非常常用,建议记住:
var fruits = ['apple', 'banana', 'avocado'];
// 过滤:挑出符合条件的
var aFruits = fruits.where((f) => f.startsWith('a')).toList();
// → ['apple', 'avocado']
// 转换:对每个元素做变换
var upper = fruits.map((f) => f.toUpperCase()).toList();
// → ['APPLE', 'BANANA', 'AVOCADO']
// 判断:是否包含满足条件的元素
var hasApple = fruits.any((f) => f == 'apple'); // true
// 排序
var sorted = [3, 1, 2]..sort(); // [1, 2, 3]4. 函数:可复用的代码块
4.1 基本写法
// 标准写法
int add(int a, int b) {
return a + b;
}
// 箭头写法(函数体只有一个表达式时,可用 => 简写)
int add(int a, int b) => a + b;4.2 参数
Dart 的参数规则是一大特色,记住这两种就够了:
// ① 命名参数 { } —— 调用时必须写参数名(Flutter 组件大量使用这种)
void greet({required String name, int age = 0}) {
print('你好,$name,年龄 $age');
}
greet(name: 'Tom', age: 25); // ✅ 调用时写参数名
greet(name: 'Tom'); // ✅ age 有默认值,可以不传
// ② 位置参数 [ ] —— 按顺序传,可以不传
void hello(String name, [String? city]) {
print('你好,$name${city != null ? ",来自$city" : ""}');
}
hello('Tom', '北京'); // 你好,Tom,来自北京
hello('Tom'); // 你好,Tom💡 Flutter 开发中 90% 是命名参数,因为可读性好:
Text('内容', style: TextStyle(fontSize: 16))比位置参数清晰得多。
4.3 函数作为参数(回调)
在 Flutter 中,点击事件、列表项构建等都用到了"函数当参数":
// 定义:接受一个函数作为参数
void doSomething(int Function(int, int) operation) {
print(operation(3, 4));
}
// 调用:传入匿名函数
doSomething((a, b) => a + b); // 输出 7
doSomething((a, b) => a * b); // 输出 125. 控制流:程序的执行逻辑
5.1 条件判断
// if-else
var score = 85;
if (score >= 90) {
print('优秀');
} else if (score >= 60) {
print('及格');
} else {
print('不及格');
}
// 三目运算符(简写的 if-else)
var result = score >= 60 ? 'Pass' : 'Fail'; // 'Pass'5.2 循环
// for 循环
for (var i = 0; i < 5; i++) {
print(i); // 0 1 2 3 4
}
// for-in 循环(遍历集合,更常用)
var fruits = ['苹果', '香蕉'];
for (var fruit in fruits) {
print(fruit);
}
// while 循环
var i = 0;
while (i < 5) {
print(i);
i++;
}5.3 switch
var color = 'red';
switch (color) {
case 'red':
print('红色');
break;
case 'blue':
print('蓝色');
break;
default:
print('其他颜色');
}6. 类:面向对象的基础
类是 Dart 的核心概念,Flutter 中一切 Widget 都是类。
6.1 定义和使用类
class Person {
// 属性
String name;
int age;
// 构造函数:Person(this.name, this.age) 是简写
Person(this.name, this.age);
// 方法
void sayHello() {
print('你好,我是$name,今年$age岁');
}
// Getter:像属性一样访问,但实际是计算出来的
bool get isAdult => age >= 18;
}
// 使用
var p = Person('Tom', 25);
p.sayHello(); // 你好,我是Tom,今年25岁
print(p.isAdult); // true(像属性一样用,不加括号)6.2 命名构造函数
一个类可以有多个构造函数,用名字区分:
class Person {
String name;
int age;
Person(this.name, this.age);
// 命名构造函数:从 Map 创建对象
Person.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'];
}
// 使用
var p1 = Person('Tom', 25);
var p2 = Person.fromJson({'name': 'Jerry', 'age': 30});6.3 继承:子类获得父类的能力
class Animal {
String name;
Animal(this.name);
void speak() => print('$name 发出声音');
}
// Dog 继承 Animal,获得 name 和 speak()
class Dog extends Animal {
String breed;
Dog(String name, this.breed) : super(name); // super 调用父类构造函数
@override // 重写父类方法
void speak() => print('$name 汪汪汪!(品种:$breed)');
}
var dog = Dog('旺财', '柯基');
dog.speak(); // 旺财 汪汪汪!(品种:柯基)6.4 抽象类:只定义规范,不实现
// 抽象类:定义"长什么样",但不实现细节
abstract class Shape {
double get area; // 只有声明,没有实现
void draw(); // 只有声明,没有实现
}
// 具体类必须实现抽象类的所有方法
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double get area => 3.14 * radius * radius;
@override
void draw() => print('画一个半径为$radius的圆');
}6.5 Mixin:给类"混入"额外能力
Mixin 不是继承,而是额外能力叠加,可以理解为"插件":
mixin Flyable {
void fly() => print('飞起来了!');
}
mixin Swimmable {
void swim() => print('游起来了!');
}
// 用 with 混入能力,一个类可以混入多个
class Duck extends Animal with Flyable, Swimmable {
Duck(String name) : super(name);
}
var duck = Duck('唐老鸭');
duck.speak(); // 唐老鸭 发出声音
duck.fly(); // 飞起来了!
duck.swim(); // 游起来了!💡 继承(extends)是"是什么",Mixin(with)是"能做什么"。一个类只能继承一个父类,但可以混入多个 Mixin。
7. 异步编程:等一等再继续
Flutter 开发中,网络请求、文件读写等都是异步操作。Dart 用 async / await 处理异步,非常直观。
7.1 Future —— 等一个结果
Future 就是"未来的值",类似于其他语言的 Promise:
// 定义异步函数:加 async 关键字
Future<String> fetchName() async {
// await 等待耗时操作完成
await Future.delayed(Duration(seconds: 1)); // 模拟耗时 1 秒
return 'Flutter';
}
// 使用异步函数
void main() async { // 使用 await 的函数也必须加 async
print('开始');
String name = await fetchName(); // 等待结果
print(name); // 1 秒后输出:Flutter
}💡 规则很简单:函数加
async,调用加await,返回值用Future包一下。
7.2 错误处理
Future<void> loadData() async {
try {
final result = await fetchData(); // 可能出错的异步操作
print(result);
} catch (e) {
print('出错了:$e'); // 捕获错误
} finally {
print('无论如何都执行'); // 可选
}
}7.3 Stream —— 持续接收数据
Future 是等一个结果,Stream 是持续接收多个结果(比如聊天消息、传感器数据):
// 定义:用 async* 和 yield 产生数据流
Stream<int> countDown(int from) async* {
for (int i = from; i > 0; i--) {
await Future.delayed(Duration(seconds: 1));
yield i; // yield 产生一个值,但不结束函数
}
}
// 使用:listen 监听数据流
countDown(3).listen((value) {
print(value); // 每秒输出:3 → 2 → 1
});💡 Future vs Stream:Future = 一次性的结果;Stream = 持续不断的数据流。
8. 枚举与扩展
8.1 枚举(enum):限定选项
enum Status { loading, success, error }
var current = Status.loading;
// 在 switch 中使用(Dart 会提示你处理所有情况)
switch (current) {
case Status.loading:
print('加载中');
break;
case Status.success:
print('成功');
break;
case Status.error:
print('出错了');
break;
}8.2 扩展方法:给已有类型加新功能
// 给 String 加一个 reversed 属性
extension StringExtension on String {
String get reversed => split('').reversed.join();
}
// 使用
print('hello'.reversed); // 'olleh'9. 其他实用语法
9.1 级联操作符 ..
连续操作同一个对象,不用反复写变量名:
// 不用级联
var paint = Paint();
paint.color = Colors.blue;
paint.strokeWidth = 5.0;
// 用级联(更简洁)
var paint = Paint()
..color = Colors.blue
..strokeWidth = 5.0;9.2 类型判断
var value = 'hello';
// is 判断类型(成立后自动类型转换)
if (value is String) {
print(value.length); // 这里 value 自动被识别为 String
}
// as 强制类型转换(确定类型时用)
var len = (value as String).length;9.3 空值合并
String? name;
String displayName = name ?? '匿名'; // name 为 null 就用 '匿名'提示
以上是 Dart 在 Flutter 开发中最常用的语法。更完整的语法参考请查看 Dart 语言指南。
