1. 注释
- 单行注释:以两个斜杠(
//
)开头,持续到行尾。 - 多行注释:以
/*
开头,以*/
结尾,可以跨越多行。 - 文档注释:以
///
或/**
开头,用于为代码生成文档,在文档注释中使用[]
(如[Food]
、[feed]
),生成文档时,会转换为指向类、方法、变量等的超链接。
2. 变量
以下是变量声明和初始化的示例:
var name = 'Bob';
Dart
是强类型语言,示例中,name
变量的类型会在编译时被推断为 String
,也可在声明时显式指定类型,对于局部变量,建议使用 var
。如果对象不限于单一类型,可以在声明时指定为 Object
或 dynamic
类型。
3. 常量
如果不打算修改某个变量,可使用 final
或 const
来声明。
final name = 'Bob'; // 替代 `var`,不指定类型
final String nickname = 'Bobby'; // 配合类型一起使用
虽然,final
和 const
都用于声明不可变变量,但在用法上也有区别:
- 初始化时机:
final
是运行时初始化,const
是编译时初始化(const
变量可以视为隐式的final
); - 值的要求:
final
可以是计算值或运行时才确定的值,可以延迟初始化,而const
必须是编译时指定的常量; - 实例变量:实例变量(
Instance variables
)可以是final
,但不能是const
。
4. 命名规则
- 标识符(变量名、常量名、方法名、类名等)必须由数字、字母、下划线和美元符号(
$
)组成,且不能以数字开头; - 标识符不能是保留字和关键字(如
int
、class
、if
、void
等); - 标识符区分大小写,
age
和Age
是两个不同的标识符; - 标识符需要见名思意,变量名称建议用名词,方法名称建议用动词;
- 变量、函数、方法、参数建议使用小驼峰命名法,类、枚举、
typedef
(类型定义)建议使用大驼峰命名法; final
变量和局部的const
变量建议使用 小驼峰命名法,全局或类级别的const
常量建议使用 全大写 + 下划线分隔(如UPPER_SNAKE_CASE
);- 库和文件名建议使用 全小写+下划线(如
user_utils
、user_profile.dart
); - 私有成员(类内变量 / 方法)建议以 下划线(_)开头(如
_password
、_encryptPassword()
)。
5. 数据类型
Dart
语言提供了一系列的 内置类型(Built-in Types),这些类型拥有使用字面量来创建对象的能力。例如,this is a string
是一个字符串字面量,而 true
是一个布尔值字面量。
如下是 Dart
支持的所有 内置类型:
类别 | 数据类型 | 描述 | 示例 |
---|---|---|---|
数值类型(Numbers) | int | 不大于 64 位的整数,具体取决于平台。在本地平台,取值范围为-263 至 263 - 1,在网页上,整数值以 JavaScript 数字表示,取值范围为-253 至 253 - 1。 | int age = 25; |
double | 双精度浮点数 | double pi = 3.14; | |
num | int 和 double 的父类,可表示整数或小数,包含了 + 、- 、* 、/ 等运算符以及一些基本的数值操作方法,如 abs() 、ceil() 、floor() 等。 | num x = 10; x = 9.5; | |
字符串类型(Strings) | String | UTF-16 编码的字符序列,用单引号(' )和双引号(" )都可以创建 | String s = 'Hello'; |
布尔类型(Booleans) | bool | 只有 true 和 false 两个编译时常量 | bool isValid = true; |
集合类型(Collections) | List | 有序列表(数组) | var list = [1, 2, 3]; |
Set | 无序的唯一元素集合 | var set = {1, 2, 3}; | |
Map | 键值对映射 | var map = {'a': 1, 'b': 2}; | |
记录类型(Records) | (value1, value2) | 一种匿名的、不可变的聚合类型,Dart 3.0 以后支持 | var record = ('first', a: 2, b: true); |
函数类型(Functions) | Function | 函数也是对象,并且具有一个类型,即 Function 。这意味着函数可以被赋值给变量,或者作为参数传递给其他函数。另外,也可以像调用函数一样,调用一个 Dart 类的实例。 | int Function(int, int) adder = (a, b) => a + b; |
特殊类型 | Runes | 字符串的 Unicode 码位,但通常会被 characters API 替代 | Runes clapping = Runes('\u{1f44f}'); |
Symbol | 表示在 Dart 程序中声明的运算符或标识符,你可能永远不需要使用 Symbol 类型,但对于需要通过名称引用标识符的 API 而言,它们却极其宝贵 —— 因为代码压缩会改变标识符的名称,却不会改变标识符对应的 Symbol 。 | #mySymbol | |
Null | 唯一的对象是 null |
其它数据类型:
Object
: 除Null
外,所有Dart
类型的超类;Enum
: 所有枚举类型的超类;Future
和Stream
: 异步编程中使用;Iterable
: 用于for-in
循环和同步生成器函数(synchronous generator functions
)中;Never
:表示某个表达式永远无法成功完成求值,最常用于那些总是抛出异常的函数;dynamic
: 动态类型,允许变量接收任意类型的值,不进行静态类型检查,且在编译时不验证类型操作(可能导致运行时错误)。官方建议优先使用Object
或Object?
,以在保持一定灵活性的同时,利用Dart
的类型安全特性;void
: 表示某个值从不被使用,常被用作返回类型。
5.1 数值类型
数值与字符串之间的类型转换。
// String -> int
var one = int.parse('1');
assert(one == 1);
// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);
// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
数值字面量是编译时常量,许多算术表达式也是编译时常量(运算结果可以直接为 const
常量初始化)。
const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;
为了让较长的数字更具可读性,可用下划线(_
)分隔,不过,需要 Dart
版本为 3.6
以上。
var n1 = 1_000_000;
var n2 = 0.000_000_000_01;
var n3 = 0x00_14_22_01_23_45; // MAC address
var n4 = 555_123_4567; // US Phone number
var n5 = 100__000_000__000_000; // one hundred million million!
5.2 字符串类型
5.2.1 字符串插值
可以通过 ${expression}
的方式将表达式的值嵌入字符串中,如果该表达式是一个标识符,则可以省略 {}
。
var s = 'string interpolation';
assert(
'Dart has $s, which is very handy.' ==
'Dart has string interpolation, which is very handy.',
);
assert(
'That deserves all caps. ${s.toUpperCase()} is very handy!' ==
'That deserves all caps. STRING INTERPOLATION is very handy!',
);
5.2.2 字符串拼接
可以使用 相邻字符串字面量 或 +
运算符来拼接字符串。
var s1 =
'String '
'concatenation'
" works even over line breaks.";
var s2 = 'The + operator ' + 'works, as well.';
5.2.3 多行字符串
要创建多行字符串,可使用三重单引号('''
)或三重双引号("""
)的形式实现。
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
5.2.4 原始字符串
在字符串前面添加前缀 r
可以用来创建一个原始字符串,原始字符串的关键作用是忽略转义字符(如 \n
、\t
、\\
等)的特殊含义,直接按字符串的字面形式解析,所有字符(包括反斜杠 \
)都会被原样保留。
var s = r'In a raw string, not even \n gets special treatment.';
5.3 集合类型
5.3.1 列表(List,有序列表)
在几乎所有编程语言中,最常见的集合类型或许就是数组(Array
)—— 即一种有序的对象组。而在 Dart
中,数组是 List
对象,可以存储任意类型的元素。Dart
的列表字面量由放在方括号([]
)中的、用逗号分隔的元素列表表示。
var list = [1, 2, "Hello", true];
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
如果希望一个列表只包含一种类型(如 String
),可通过泛型声明来约束,以下是示例:
var names = <String>[];
// 或
// List<String> names = [];
names.addAll(['Seth', 'Kathy', 'Lars']);
// names.add(42); // 报错
如果希望创建固定长度的列表,可以通过如下方式实现:
var list = List<String>.filled(2, '');
list[0] = 'Hello'; // 可以修改
// list.add("World"); // 报错,不可以添加
也可以创建一个编译时常量列表,只需在列表字面量前添加 const
即可,创建后不可修改(元素和长度均固定, add()
、remove()
方法也不行):
var constantList = const [1, 2, 3];
// constantList[1] = 1; 不可修改,会报错
5.3.2 集合(Set,无序集合)
Set
的核心特点是 “无序” 和 “元素唯一” —— 元素没有固定顺序,且重复添加的元素会被自动过滤(仅保留一个)。Dart
的集合字面量由放在花括号({}
)中的、用逗号分隔的元素集合表示。
var sets = {1, true, 'hello'};
如果要创建一个空的集合,可以在 {}
前加上类型参数,或者将 {}
赋值给类型为 Set<E>
的变量。
var names = <String>{};
// 或
// Set<String> names = {};
// var names = {}; // 创建一个空的Map,而不是Set
映射字面量的语法与集合字面量相似。由于映射字面量出现得更早,{}
默认表示Map
类型。如果在{}
上或其赋值的变量上省略了类型,Dart
会创建一个Map<dynamic, dynamic>
类型的对象。
和列表类似,创建编译时常量集合,只需在集合字面量前添加 const
即可。
final constSets = const {1, true, 'hello'};
5.3.3 映射(Map)
在映射中,每个元素都是一个键值对,且键和值都可以是任意类型的对象。每个键在映射中只能出现一次,但同一个值可以与多个不同的键相关联。
下面是简单的示例:
var gifts = {
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings',
};
var nobleGases = {2: 'helium', 10: 'neon', 18: 'argon'};
也可通过构造函数实现同样的功能:
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
如果你之前使用过C#
或Java
这类语言,可能会习惯看到用new Map()
创建映射,而不是直接写Map()
,但在Dart
中,new
关键字是可选的。
和列表和集合一样,如果想要创建编译时常量映射,只需在映射字面量前添加 const
即可。
final constantMap = const {2: 'helium', 10: 'neon', 18: 'argon'};
// constantMap[2] = 'Helium'; // 报错
5.4 记录类型
记录是一种匿名、不可变的聚合类型,没有独立的类型声明,其类型由字段的类型决定。与其他集合类型类似,记录允许你将多个对象打包成一个单一对象;但与其他集合类型不同的是,记录具有固定大小、异构性和强类型的特性。记录类型需要 Dart 3.0
以上的版本才能使用。
由于其匿名性,记录无需像类那样定义名称,可直接通过 (value1, value2, ...)
或 (key1: value1, key2: value2, ...)
的语法创建实例,例如:
var point = (10, 20); // 位置记录(按顺序访问元素)
var user = (name: "Alice", age: 30); // 命名记录(按键访问元素)
记录中的字段包括 命名字段 和 位置字段 两种形式,命名字段会提供与其名称相同的 getter
方法。位置字段会提供名称为 $<position>
的 getter
方法,且计算位置序号时会跳过命名字段。
var record = ('first', a: 2, b: true, 'last');
print(record.$1); // Prints 'first'
print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'
记录类型中 命名字段 的名称,是该记录类型定义的一部分,若两个记录的命名字段名称不同,则它们属于不同的类型:
({int a, int b}) recordAB = (a: 1, b: 2);
({int x, int y}) recordXY = (x: 3, y: 4);
// 编译错误! 这两个记录不是同一个类型
// recordAB = recordXY;
在记录类型的标注中,也可以为 位置字段 命名,但这些名称仅用于文档说明(即增强代码可读性),不会影响记录的类型:
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);
recordAB = recordXY; // OK.
借助记录类型,函数可以一次性返回多个组合值。要接收这些返回值,可通过模式匹配的方式直接将记录解构到局部变量中。
// 借助记录类型返回多个值
(String name, int age) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}
final json = <String, dynamic>{'name': 'Dash', 'age': 10, 'color': 'blue'};
// 通过模式匹配解构位置字段
var (name, age) = userInfo(json);
解构命名字段需要使用 :
语法:
({String name, int age}) userInfo(Map<String, dynamic> json) {
return (name: json['name'] as String, age: json['age'] as int);
}
final json = <String, dynamic>{'name': 'Dash', 'age': 10, 'color': 'blue'};
// 通过模式匹配解构命名字段
final (:name, :age) = userInfo(json);
相关推荐
- Flutter笔记(1) —— Dart 开发环境搭建 2025-09-12
评论0
暂时没有评论