Dart 速通:类型、空安全与异步
Dart 是 Flutter 唯一的前端语言,它比 JavaScript 严格、比 Java 简洁。会 JS 或 Java 的人半天就能写起来,真正需要刻意练的只有两块:空安全与异步。本章只讲写 Flutter 天天要用的语法。
变量与类型
void main() {
var name = '瑞林'; // 类型推断为 String
final now = DateTime.now(); // 运行期常量,只赋值一次
const pi = 3.14159; // 编译期常量,性能更好
late String token; // 延迟初始化,使用前必须赋值
token = 'abc123';
print('$name $now $pi $token'); // 插值用 $,复杂表达式用 ${}
}
| 关键字 | 赋值时机 | 是否可改 | 典型用途 |
|---|---|---|---|
var | 声明时推断 | 可改 | 局部变量 |
final | 运行时一次 | 不可改 | 接口返回、Controller 引用 |
const | 编译期 | 不可改 | Widget 构造、常量表 |
late | 稍后赋值 | 可改 | initState 里初始化的字段 |
空安全
Dart 3 默认开启空安全:String 一定不是 null,可能为 null 的必须写成 String?,编译器强制你处理。
class User {
final String name;
final int? age; // 可能为 null
const User({required this.name, this.age});
String get label => age == null ? '$name 年龄未知' : '$name ${age!} 岁';
}
void demo(List<User> users) {
final first = users.isEmpty ? null : users.first;
print(first?.name ?? '没有用户'); // 安全访问 + 默认值
}
| 写法 | 含义 | 什么时候用 |
|---|---|---|
String? | 可空类型 | 字段或返回值可能缺失 |
value! | 断言非空 | 逻辑上一定非空,误判会抛异常 |
a?.b | a 为 null 时整体为 null | 链式访问可空对象 |
a ?? b | a 为 null 时取 b | 提供默认值 |
a ??= b | a 为 null 时赋值 | 懒初始化缓存 |
required | 具名参数必填 | 构造函数不可省略的参数 |
类、mixin 与继承
extends 只能继承一个类,with 可混入多个 mixin,implements 只拿到接口约定、实现全部自己写。
abstract class Repository { // 接口约定
Future<List<String>> load();
}
mixin Loggable {
void log(String msg) => print('[log] $msg');
}
class ApiRepository with Loggable implements Repository {
@override
Future<List<String>> load() async {
log('开始请求');
return const ['a', 'b'];
}
}
class CachedRepository extends ApiRepository { // 继承实现,复用父类逻辑
final List<String> _cache = [];
@override
Future<List<String>> load() async {
if (_cache.isEmpty) _cache.addAll(await super.load());
return _cache;
}
}
集合与扩展
void collections() {
final list = [1, 2, 3, 4, 5, 6];
final doubled = list.map((e) => e * 2).toList(); // 映射
final evens = list.where((e) => e.isEven).toList(); // 过滤
final sum = list.fold<int>(0, (acc, e) => acc + e); // 聚合
final tags = <String>{'dart', 'flutter'}; // Set 自动去重
print('$doubled $evens $sum $tags');
}
extension NumFormat on double {
String toMoney() => '¥${toStringAsFixed(2)}'; // 给已有类型加方法
}
typedef Json = Map<String, dynamic>;
typedef OnTapCallback = void Function(int index);
异步:Future 与 Stream
| 类型 | 语义 | 典型场景 |
|---|---|---|
Future<T> | 一次性的异步结果 | 网络请求、读写文件 |
Stream<T> | 连续多次的事件流 | 数据库监听、WebSocket |
Future<String> fetchUserName(int id) async {
await Future<void>.delayed(const Duration(milliseconds: 300));
if (id <= 0) throw ArgumentError('id 必须为正数');
return 'user_$id';
}
Future<void> run() async {
try {
print(await fetchUserName(1));
} on ArgumentError catch (e) {
print('参数错误:$e'); // 具名捕获,先具体后笼统
} catch (e) {
print('未知错误:$e');
}
print(await Future.wait([fetchUserName(1), fetchUserName(2)])); // 并行等待
}
Stream<int> ticker() async* {
for (var i = 0; i < 3; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
yield i; // yield 逐个产出事件
}
}
与 JavaScript / Java 的差异对照
| 关注点 | Dart | JavaScript | Java |
|---|---|---|---|
| 类型系统 | 静态 + 推断 | 动态(TS 才有静态类型) | 静态 |
| 空值 | String? + ?. + ?? 强制处理 | null 随处可见 | 引用类型可为 null |
| 常量 | const(编译期)/ final(运行期) | 仅块级 const | final |
| 异步 | async/await + Future/Stream | async/await + Promise | CompletableFuture |
| 构造函数 | 命名参数 + this.x 简写 | 无类构造概念 | 支持重载 |
小结:Dart 的重点只有三件事——用 final/const 表达不可变、用空安全把 null 消灭在编译期、用 async/await 处理一次性异步而用 Stream 处理连续事件;把 ! 当最后手段,能不用就不用。