2、Dart语言基础:变量与数据类型、函数与闭包、面向对象编程、异步编程
好,咱们直接进入正题。Dart 是 Flutter 的基石,你如果 Dart 写得不顺手,Flutter 开发起来会非常痛苦。我个人带团队时,发现很多新人上来就怼 Widget,结果遇到状态管理、异步请求时一脸懵。说白了,地基没打牢。
这一章我会把 Dart 最核心的四个部分串起来讲。你跟着我过一遍,基本就能上手写业务了。
2.1 变量与数据类型
Dart 是强类型语言,但它也给了你偷懒的空间。我个人习惯:能用 var 的地方就用 var,但前提是你一眼能看出类型。如果类型不明确,我会显式声明。
var name = '张三'; // 编译器自动推断为 String
int age = 28; // 显式声明,更清晰
dynamic anything = '可以是任何类型'; // 慎用,我一般只在 JSON 解析时用
dynamic,结果运行时类型错误导致线上崩溃。从那以后,我给自己定了个规矩:dynamic 只在和原生平台交互时使用。
Dart 的数据类型分为几类:
- 基本类型:
int、double、String、bool - 集合类型:
List、Set、Map - 特殊类型:
null、Symbol、Runes
这里有个坑,我提一下。Dart 里 int 和 double 都是 num 的子类。你写 API 返回时,如果后端返回了 3.0,Dart 会把它解析成 double。你如果强转成 int,会报错。嗯,我踩过这个坑。
2.2 函数与闭包
Dart 里函数是一等公民。什么意思?就是函数可以赋值给变量,可以作为参数传递,也可以作为返回值。这在 Flutter 里太常见了,比如你写 onPressed 回调,本质上就是在传函数。
// 普通函数
int add(int a, int b) {
return a + b;
}
// 箭头函数(单表达式)
int multiply(int a, int b) => a * b;
// 函数作为参数
void execute(int a, int b, int Function(int, int) operation) {
print(operation(a, b));
}
闭包这个东西,很多新手觉得玄乎。说白了,闭包就是一个函数,它记住了自己创建时的环境变量。我举个例子:
Function makeCounter() {
int count = 0;
return () {
count++;
return count;
};
}
void main() {
var counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
}
你看,count 变量明明在 makeCounter 执行完后就应该被销毁了,但闭包把它「包」住了。我在项目中用闭包做过防抖函数,效果很好。
StatefulWidget 里创建回调函数时,可以用闭包捕获当前的状态值,避免异步回调时读到过期的数据。
2.3 面向对象编程
Dart 是纯面向对象的语言,连 int、null 都是对象。这一点和 Java 不同,Java 有基本类型和包装类型的区分,Dart 没有。
类的基本写法:
class User {
final String name;
int age;
// 构造函数
User(this.name, this.age);
// 命名构造函数
User.guest() : name = '游客', age = 0;
void sayHello() {
print('你好,我是 $name');
}
}
我个人比较喜欢用 final 来声明不可变字段。为什么?因为不可变对象在多线程环境下更安全,而且代码意图更清晰。你想想看,一个字段如果到处都能改,调试起来多头疼。
继承和混入(Mixin)是 Dart 里两个重要的概念。继承是「is-a」关系,混入是「has-a」关系。我建议你多用混入,少用多层继承。多层继承容易导致代码僵化,混入则灵活得多。
mixin Flyable {
void fly() => print('飞行中...');
}
class Bird with Flyable {}
void main() {
Bird().fly(); // 飞行中...
}
2.4 异步编程
Flutter 是单线程模型,但异步编程无处不在。网络请求、文件读写、数据库操作,全是异步的。Dart 用 Future 和 Stream 来处理异步。
先看 Future:
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return '数据加载完成';
}
void main() async {
print('开始加载...');
var data = await fetchData();
print(data);
}
这里有个关键点:await 只能在 async 函数里使用。如果你在普通函数里用了 await,编译器会直接报错。嗯,这个错误我见过无数次了。
再说 Stream。Stream 和 Future 的区别是什么?Future 只返回一个值,Stream 可以返回多个值。比如你监听用户输入、监听文件变化,用 Stream 就很合适。
Stream<int> countStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (var value in countStream()) {
print(value);
}
}
StatefulWidget 的 dispose 方法里一定要取消订阅。
异步编程的另一个常见场景是并发。Dart 提供了 Future.wait 来并行执行多个异步任务:
Future<void> main() async {
var results = await Future.wait([
fetchUser(),
fetchPosts(),
fetchComments(),
]);
// 三个请求并行执行,全部完成后才继续
}
这个模式在首页数据加载时特别有用。你想想看,如果串行请求三个接口,用户得等三倍的时间。并行的话,只等最慢的那个。
AsyncHelper 工具类,统一处理超时、重试、错误转换。这样业务代码里就不用重复写 try-catch 了,代码干净很多。
好了,Dart 基础的四个核心部分就讲到这里。变量与数据类型是基本功,函数与闭包是灵活性的体现,面向对象是组织代码的骨架,异步编程是 Flutter 的灵魂。把这四个吃透了,你写 Flutter 代码会顺手很多。
下一章我们开始搭建项目目录结构,到时候我会分享我踩过的坑和总结的最佳实践。