SMARTWATCH 学习手册有 C 基础 · 嵌入式从零开始
支线三 / Dart 与 Flutter App / 01

Dart 语法入门:从 C 迁移到新语言

把后面会遇到的写法在这一篇集中解释。先读普通数值和函数,再认识集合、空值、对象与回调,每段都能独立观察结果。

接续上一篇:先把开发环境和第一个程序跑起来

1 · 变量、类型与不可重新赋值

放入 main(),运行观察

var count = 1;
count = 2;
final readings = <int>[3900, 4000];
readings.add(4100);
const title = 'Watch';
double voltage = 4.0;
bool connected = false;
print('$title: $voltage V, $connected');

var 让编译器从初值推断类型,count 仍然是整数,不能随后赋成字符串。final 表示这个变量只能赋值一次;它指向的列表仍可增加元素。const 声明编译时常量,常量列表的内容也不能修改。int、double、bool、String 分别表示整数、浮点数、布尔值和字符串。

字符串中的 $name 或 ${表达式} 插入计算结果。与 C 的字符数组不同,Dart 不要求手工安排字符串缓冲或结尾的零字符。对象的内存回收由运行环境管理,但监听、定时器等活动资源仍要按生命周期停止。

2 · 列表、映射和尖括号

列表按下标查找,Map 按键查找

final values = <int>[10, 20, 30];
final names = <int, String>{0: 'Lying', 3: 'Walking'};
for (final value in values) {
  print(value);
}
print(values[1]);
print(names[3]);

List 表示装整数的列表,Map 表示用整数作键、字符串作值的映射。尖括号中的类型参数说明容器装什么,叫泛型。下标仍从零开始;越界不是得到一个随机内存值,而会报告运行错误。

Dart 的 / 产生除法结果,~/ 用于整数除法。if、for、while、return 的基本逻辑沿用熟悉的写法;条件必须是 bool,不能直接把整数当作真假。

3 · 函数、命名参数与回调

完整片段 · 输出 6 和 clicked

int add(int a, int b, {int offset = 0}) {
  return a + b + offset;
}

void main() {
  print(add(2, 3, offset: 1));
  void Function() action = () => print("clicked");
  action();
}

花括号内是命名参数,调用时写 offset: 1;有默认值时可以省略。required 表示这个命名参数必须提供。位置参数按顺序传递,可选位置参数使用方括号。函数只有一个返回表达式时,可以写成 => 表达式。

void Function() 表示“不接收参数、不返回结果”的函数类型。() => print(...) 是匿名函数,先交给 action,action() 才执行。Flutter 按钮也用这种方式接收回调;onPressed: action 交付函数,onPressed: action() 会在构建页面时就调用它。

4 · 空值安全:几个符号究竟在检查什么

放入 main(),先看 null,再看赋值之后

String? deviceName;
print(deviceName?.length);
print(deviceName ?? 'No device');
deviceName = 'SmartWatch';
print(deviceName.length);

String? 允许值为 null,即没有对象;普通 String 不能直接保存 null。?. 只在对象存在时访问成员,?? 在左边为 null 时采用右边默认值。! 是程序员声明“这里一定不为 null”,若判断错误会在运行时失败。处理扫描失败时通常应明确判断,而不是到处加 !。

late 表示变量稍后会在使用前初始化,不会自动创建对象。未初始化就使用仍然会失败。页面销毁后是否仍然有效,是另一个生命周期问题,不能用空值检查替代。

5 · 类、构造函数、继承与重写

类定义放在 main() 外;main() 中创建 BatteryMeasurement(4000)

class Measurement {
  final int millivolts;
  Measurement(this.millivolts);
  String describe() => "$millivolts mV";
}

class BatteryMeasurement extends Measurement {
  BatteryMeasurement(super.millivolts);
  @override
  String describe() => "Battery: ${super.describe()}";
}

类把一类对象的数据和操作写在一起,millivolts 是字段,describe 是方法。构造函数与类同名,this.millivolts 把传入值放进当前对象。extends 继承已有类型的能力,super 访问父类;@override 明确声明这里重写已有方法,便于工具检查名称和签名。

Flutter 页面会继承框架的 Widget 类型并重写 build;它是后面解释界面的必要语法,不要求先建立一套设备对象框架。import 导入其他文件或库;名称前的下划线表示库内私有,而不是 C 的文件级 static。

动手观察

运行下载示例,解释为什么 final 列表还能增加元素。将 onPressed 的两种写法分别口头说明执行时刻,删除一个必要的空值处理,观察分析器给出的错误。

资料与代码

配套文件

先复制到独立练习目录;用法见 支线示例说明。完整 Flutter 小应用见 tutorial/examples/flutter_watch/README.md。