CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Flutter 开发入门/课时/Dart 语言
RawBlame
课时目录12 节

Flutter 开发入门

  1. 01Flutter 环境
  2. 02Dart 语言
  3. 03Widget 体系
  4. 04架构原则
  5. 05状态管理
  6. 06动画
  7. 07网络与本地存储
  8. 08Firebase 集成
  9. 09测试
  10. 10CI/CD 与发布
  11. 11分析与监控
  12. 12综合练习

Dart 语言

async、集合与 FP。

30 分钟

Dart 语言

这一课你将能做到

  1. 用 Dart 写变量、函数、类,理解 final / const / var / late 的区别
  2. 熟练使用 List、Map、Set 以及函数式操作(map / where / fold)
  3. 写出正确的异步代码:Future、async/await、Stream,并理解空安全(? / ! / ??)

为什么要学

Flutter 的 UI 全部由 Dart 写,Dart 不熟,写 Widget 就是抄。Dart 和 Java/Kotlin 很像,但有三个对 Flutter 至关重要的特性:空安全(编译期拦截 NPE)、async/await(IO 不卡 UI)、级联与函数式 API(写 UI 表达力强)。

人话:Dart 就是给 Flutter 量身定做的「安全版 JavaScript + 简化版 Java」。

先建立图像

强类型        var / final / const / late
面向对象      class / mixin / extension / interface
函数式        高阶函数 / map / where / 级联 ..
异步          Future + async/await + Stream
空安全        String vs String?  编译期强制区分

跟着做

不想装环境?打开 <https://dartpad.dev> 把每段代码粘进去点 Run。

步骤 1:变量与类型

void main() {
  var name = 'Ada';            // 类型推断为 String
  final now = DateTime.now();  // 运行期一次赋值,不可改
  const pi = 3.14159;          // 编译期常量
  late String token;           // 延迟初始化,使用前必须赋值

  int age = 18;
  double height = 1.72;
  bool isAdmin = false;

  print('$name $age $height $isAdmin $pi');
  token = 'abc';
  print(token);
}

要点:var 类型一旦确定不可变;final 是「运行期只读」;const 是「编译期常量」,能 const 就 const,Flutter 中性能更好。

步骤 2:函数

// 普通函数
int add(int a, int b) => a + b;

// 可选命名参数(Flutter 里到处是这种写法)
void greet({required String name, int age = 0}) {
  print('hello $name, $age');
}

// 函数作为参数
void repeat(int times, void Function(int) action) {
  for (var i = 0; i < times; i++) action(i);
}

void main() {
  print(add(2, 3));
  greet(name: 'Bob');          // age 用默认值
  repeat(3, (i) => print('第 $i 次'));
}

步骤 3:集合与函数式

void main() {
  var nums = [1, 2, 3, 4, 5];
  var evens = nums.where((n) => n.isEven).map((n) => n * 10).toList();
  print(evens);                          // [20, 40]

  var sum = nums.fold(0, (a, b) => a + b);
  print(sum);                            // 15

  var user = {'name': 'Ada', 'age': 18};
  print(user['name']);

  var tags = <String>{'a', 'b', 'a'};    // Set 自动去重
  print(tags);                           // {a, b}

  // 集合里写控制流(Flutter 写 UI 巨好用)
  var list = [
    'start',
    if (user['age'] == 18) '成年',
    for (var i = 0; i < 3; i++) '项$i',
    ...nums.map((e) => 'n$e'),
  ];
  print(list);
}

步骤 4:类、构造、命名构造、级联

class User {
  final String name;
  int age;
  User(this.name, this.age);

  // 命名构造
  User.guest()
      : name = 'guest',
        age = 0;

  void grow() => age++;

  @override
  String toString() => 'User($name, $age)';
}

class Point {
  final int x, y;
  const Point(this.x, this.y);           // const 构造
}

void main() {
  var u = User('Ada', 18)..grow()..grow(); // 级联
  print(u);                                 // User(Ada, 20)
  print(User.guest());

  const p1 = Point(1, 2);
  const p2 = Point(1, 2);
  print(identical(p1, p2));                 // true,const 对象复用
}

步骤 5:空安全

void main() {
  String a = 'ok';
  // a = null;          // 编译错:String 不能为 null
  String? b = null;                       // 加 ? 才能为 null

  print(b?.length);                       // 安全调用:null
  print(b ?? 'default');                  // 空则用默认
  b = 'hi';
  print(b.length);                        // 编译器已知非空,自动提升

  String? c;
  // print(c!.length);                    // 危险:null 时崩溃
  if (c != null) print(c.length);         // 推荐:判空
}

规则:默认非空,需要可空显式加 ?;尽量用 ?. ?? if (x != null),避免 !。

步骤 6:异步

Future<String> fetchUser() async {
  await Future.delayed(const Duration(seconds: 1));
  return 'Ada';
}

Stream<int> countdown(int from) async* {
  for (var i = from; i > 0; i--) {
    await Future.delayed(const Duration(milliseconds: 300));
    yield i;
  }
}

Future<void> main() async {
  print('start');
  var name = await fetchUser();   // 等待
  print('got $name');

  // 并发两个 Future
  var both = await Future.wait([fetchUser(), fetchUser()]);
  print(both);

  // Stream:事件流
  await for (var n in countdown(3)) {
    print('倒计时 $n');
  }
  print('end');
}

讲清楚要点

  1. final vs const:final 运行期一次赋值;const 编译期常量。Widget 加 const 能跳过 rebuild 优化性能。
  2. 空安全在编译期救你:把 Java 的 NullPointerException 提前到编译错误,是 Dart 最大的安全红利。
  3. late 是延迟承诺:告诉编译器「我用之前一定赋值」。乱用会把空安全打破,能用构造初始化就别用 late。
  4. Future 是「一次的结果」,Stream 是「多次的事件」:网络请求用 Future;WebSocket、键盘监听用 Stream。
  5. mixin 提供代码复用但不进入继承链:class A extends B with C, D,常见如 SingleTickerProviderStateMixin。

你可能会懵的地方

  • 为什么 Flutter 代码到处是 const? const Widget 在 rebuild 时被复用,跳过 diff,性能更好。能用就加。

  • late 和 ? 有啥区别? late String s 仍是非空,只是推迟赋值;String? s 是真的可能为 null。

  • 箭头函数 => 和 {} 区别? => expr 等价于 { return expr; },只能写一行表达式。

  • 异步函数为啥必须 await? 不 await 就拿到 Future 本身而不是结果。Dart 是单线程事件循环,await 不阻塞线程,让出执行权。

  • 为什么 const Point(1,2) == const Point(1,2) 是同一对象? Dart 对 const 对象做规范化(canonicalization),相同字段的 const 实例共享内存。

错法对照

错法问题正解
String s = null;编译错String? s = null; 或赋初值
网络请求 fetch() 不 await 直接用结果拿到的是 Futurevar r = await fetch();
滥用 ! 强制解包运行期 crash用 ?. ?? 或判空
在构造函数外用 const 修饰可变字段编译错const 类的所有字段必须 final
for 循环里 await 多次请求串行慢用 Future.wait 并发
用 print 调试 Flutter日志被吞用 debugPrint 或 log()

卡点急救

  1. The non-nullable variable 'x' must be initialized:字段没初始化。选 late、构造函数必填、或加 ?。
  2. await 用在非 async 函数:把函数改成 Future<void> foo() async { ... }。
  3. type 'Null' is not a subtype of type 'String':某处把 null 当成 String 用。开 debugger 查堆栈,多半缺 ?.。
  4. Stream 没数据:检查是否漏了 await for 或 listen;Stream 默认单订阅,二次监听要用 .asBroadcastStream()。

面试常问

  • Dart 的 isolate 是什么? Dart 单线程 + 事件循环;isolate 是独立堆内存的并发单元,靠消息传递,不共享内存。CPU 密集任务用 compute() 或 Isolate.run。

  • Future 和 Stream 区别? Future 一次性返回单个结果或错误;Stream 是异步事件序列,可多次发射。

  • 空安全怎么实现? 类型系统层把类型分为「非空 / 可空」两类,编译期做流程分析(definite assignment、promotion),禁止把 null 赋给非空。

  • mixin 和继承区别? mixin 不构造 is-a 关系,只是横向代码复用;可以避免多层继承混乱。

自测三问

  1. final、const、late 三者的差别和典型用法?
  2. 如何把 [1,2,3,4,5] 变成 [4, 16](保留偶数并平方)?写一行代码。
  3. 为什么 String? name; print(name.length); 编译不过?写两种修复方法。

小结一句话

Dart = 强类型 + 空安全 + 一等异步 + 函数式语法糖,掌握这些就掌握了写 Flutter 的全部语言基础。

延伸

  • Dart 官方语言之旅:<https://dart.dev/language>
  • 在 DartPad 上把本文每段代码跑一遍:<https://dartpad.dev>
  • 下一课:Widget 体系,看 Dart 类如何变成屏幕上的 UI。
加载本节练习…
← 上一课Flutter 环境下一课 →Widget 体系