Dart | 安装基础环境和快速入门(保姆级教程)
Dart 是一种由 Google 开发的客户端优化的编程语言,适用于移动、Web、服务器和物联网(IoT)等领域的开发。它是 Flutter 框架的官方语言,用于构建高性能、高保真的跨平台应用。
一、安装 Dart SDK
- 访问官网: 前往 Dart 官方网站:https://dart.dev
- 百度网盘:
链接: https://pan.baidu.com/s/1Zeq9SdRa-zkVja3Z80V1fA 提取码: 5qpx

一直next即可
验证sdk:
dart --version
二、快速入门
安装Dart插件:

1.基础语法
- 入口函数: 每个 Dart 程序都必须有一个
main()函数,它是程序执行的起点。 - 变量与类型:
var: 声明变量,类型由编译器推断。String: 字符串类型。int: 整数类型。double: 浮点数类型。bool: 布尔类型 (true,false)。dynamic: 可以存储任何类型的值(不推荐过度使用,会失去类型安全)。final/const: 声明不可变的变量。final在运行时赋值一次,const在编译时常量。
1. 声明变量
有几种方式可以声明变量:
使用
var关键字 (推荐用于局部变量):var允许编译器根据你第一次赋的值来推断变量的类型。- 一旦推断出类型,该变量就不能再被赋予其他类型的值。
void main() {var name = 'Alice'; // 推断为 String 类型var age = 25; // 推断为 int 类型var height = 1.75; // 推断为 double 类型// name = 100; // 错误!不能将 int 赋值给一个 String 类型的变量name = 'Bob'; // 正确!可以赋值给同为 String 类型的值 }显式指定类型:
- 你可以明确地写出变量的数据类型,这使得代码意图更清晰,也避免了类型推断可能带来的歧义。
void main() {String name = 'Charlie';int age = 30;double price = 9.99;bool isActive = true;// 或者先声明后赋值String city;city = 'Beijing'; }使用
dynamic关键字 (谨慎使用):dynamic类型的变量可以存储任何类型的值,并且可以在运行时改变其类型。- 注意: 使用
dynamic会失去 Dart 的类型安全检查,容易在运行时引发错误,应尽量避免,除非有特殊需求。
void main() {dynamic something = 'Hello';print(something.runtimeType); // Stringsomething = 42;print(something.runtimeType); // intsomething = true;print(something.runtimeType); // bool }使用
Object?:Object是所有 Dart 类的根类。Object?表示可以是任何对象,也可以是null。- 它比
dynamic更安全,但使用时需要进行类型检查或转换。
2. 变量的命名规则
- 名称必须以字母或下划线 (
_) 开头。 - 后续字符可以是字母、数字、下划线或美元符号 (
$)。 - 区分大小写(
myVariable和myvariable是不同的)。 - 不能使用 Dart 的保留关键字(如
int,class,void等)作为变量名。 - 推荐使用 驼峰命名法 (camelCase),例如:
userName,accountBalance。
Dart 常量 (Constants)
常量是值在初始化后不能被改变的变量。Dart 提供了两种创建常量的方式,它们的使用场景和约束不同。
1. final 关键字
- 含义: 一个
final变量只能被赋值一次。 - 赋值时机: 可以在运行时确定其值。也就是说,它的值可以是程序执行过程中计算出来的。
- 作用域: 通常用于声明在对象的生命周期内不应改变的变量,或者在函数内声明一个不应被修改的值。
void main() {// 在声明时赋值final String language = 'Dart';// language = 'Java'; // 错误!不能重新赋值// 在运行时赋值(例如,获取当前时间)final DateTime now = DateTime.now();print(now); // 每次运行程序,值都不同,但赋值后就不能再改// final 变量也可以在构造函数中初始化(在类中)
}2. const 关键字
- 含义:
const代表编译时常量。 - 赋值时机: 其值必须在编译时就能完全确定。它不能依赖于运行时才能知道的值。
- 深层含义:
const不仅是一个值,它还代表一个规范化的对象。这意味着,两个具有相同const构造的表达式会引用内存中的同一个对象。
void main() {// 基本数据类型的编译时常量const int maxUsers = 100;const String appName = 'My App';// 使用 const 构造函数创建对象const List<int> numbers = [1, 2, 3]; // 使用 const 列表字面量const Map<String, int> scores = {'Alice': 95, 'Bob': 87};// 这是允许的,因为 DateTime.utc 是一个 const 构造函数,且参数是编译时常量const DateTime compileTime = DateTime(2025, 1, 1);// 这是错误的!DateTime.now() 的值在运行时才能知道// const DateTime now = DateTime.now(); // 编译错误// 演示 const 的“规范性”const List<int> list1 = [1, 2, 3];const List<int> list2 = [1, 2, 3];print(identical(list1, list2)); // true,它们是同一个对象final List<int> list3 = [1, 2, 3];final List<int> list4 = [1, 2, 3];print(identical(list3, list4)); // false,它们是两个不同的对象
}final vs const 关键区别总结
| 特性 | final | const |
|---|---|---|
| 赋值时机 | 运行时 (Runtime) | 编译时 (Compile-time) |
| 值的来源 | 可以是运行时计算的结果 | 必须是编译时已知的常量 |
| 对象规范性 | 每次 final 声明都会创建新对象 | 相同的 const 表达式引用同一个对象 |
| 性能 | 略低(运行时赋值) | 更高(编译时确定,可复用) |
| 何时使用 | 值在初始化后不应改变,但初始化依赖运行时 | 值是固定的,且希望在编译时确定和优化 |
- 字符串:
- 可以用单引号 (
') 或双引号 (")。 - 字符串插值:
'Hello, $name!'或'Hello, ${name.toUpperCase()}!'。
- 可以用单引号 (
1. 创建字符串
你可以使用单引号 (') 或双引号 (") 来创建字符串字面量。
void main() {String singleQuote = 'Hello, Dart!';String doubleQuote = "Hello, World!";// 两种方式效果完全相同print(singleQuote); // Hello, Dart!print(doubleQuote); // Hello, World!// 引号嵌套:如果字符串内需要包含引号,可以使用不同类型的引号包围String withSingle = "He said, 'Hello!'"; String withDouble = 'She said, "Hi!"'; // 或者使用反斜杠 \ 进行转义String escaped1 = 'It\'s a sunny day.';String escaped2 = "She said, \"Hello!\"";
}2. 多行字符串
当你的字符串内容跨越多行时,可以使用以下两种方式:
三重引号 (
'''或"""):- 使用三个单引号或三个双引号。
- 字符串内的换行和空格会被原样保留。
String multiLine1 = ''' This is line one. This is line two.This line has leading spaces. ''';String multiLine2 = """ JSON example: {"name": "Alice","age": 30 } """;相邻字符串拼接:
- 将多个字符串字面量写在一起,Dart 会自动将它们连接成一个字符串。
- 这种方式不会保留字面量之间的换行符。
String concatenated = 'This is the first part ''and this is the second part ''on the same line.'; // 结果: "This is the first part and this is the second part on the same line."
3. 字符串插值 (String Interpolation)
这是 Dart 字符串最强大的特性之一。你可以在字符串中嵌入变量或表达式的值。
- 语法: 使用
$variableName或${expression}。 - 如果只是插入一个简单的变量名,使用
$加变量名即可。 - 如果要插入一个表达式、属性或更复杂的逻辑,必须用
${}包裹。
void main() {String name = 'Bob';int age = 25;double height = 1.80;// 插入变量String greeting = 'Hello, $name!'; print(greeting); // Hello, Bob!// 插入表达式String info1 = '$name is ${age + 1} years old next year.'; print(info1); // Bob is 26 years old next year.// 调用方法或访问属性String info2 = 'The length of the name is ${name.length}.'; String info3 = '${name.toUpperCase()} is shouting!'; print(info2); // The length of the name is 3.print(info3); // BOB is shouting!// 当变量名后紧跟字母数字字符时,必须用 {} 包裹String tricky = 'His name is ${name}y'; // 正确: Buby// String wrong = 'His name is $namey'; // 错误!编译器会找名为 'namey' 的变量
}4. 字符串拼接
有几种方式可以将两个或多个字符串连接起来。
使用
+操作符:String firstName = 'John'; String lastName = 'Doe'; String fullName = firstName + ' ' + lastName; // John Doe使用相邻字符串 (如上所述):
String sentence = 'Welcome to ' 'Dart programming!'; // Welcome to Dart programming!使用字符串插值:
String fullName = '$firstName $lastName'; // 更简洁使用
StringBuffer类 (推荐用于大量拼接):- 当你需要进行大量的字符串拼接操作时(例如在循环中),使用
+会产生很多中间的字符串对象,效率较低。 StringBuffer是一个可变的缓冲区,专门为此优化。
void main() {StringBuffer buffer = StringBuffer();buffer.write('Hello');buffer.write(' ');buffer.write('World');buffer.writeln('!'); // write + 换行buffer.writeAll(['a', 'b', 'c'], '-'); // 用 '-' 连接并写入String result = buffer.toString(); // 转换为最终的 Stringprint(result);/*Hello World!a-b-c*/ }- 当你需要进行大量的字符串拼接操作时(例如在循环中),使用
Dart 字符串的常用属性和方法
String 类提供了丰富的 API 来操作和查询字符串。
常用属性
length: 返回字符串的长度(字符数)。isEmpty: 如果字符串为空(长度为0),返回true。isNotEmpty: 如果字符串不为空,返回true。
String emptyStr = '';
String text = 'Dart';print(text.length); // 4
print(emptyStr.isEmpty); // true
print(text.isNotEmpty); // true常用方法
大小写转换:
toUpperCase(): 转换为大写。toLowerCase(): 转换为小写。
print('hello'.toUpperCase()); // HELLO print('WORLD'.toLowerCase()); // world查找和判断:
contains(Pattern pattern, [int startIndex = 0]): 判断是否包含某个子串或模式。startsWith(Pattern pattern, [int index = 0]): 判断是否以某个模式开头。endsWith(Pattern other): 判断是否以某个模式结尾。indexOf(String needle, [int start = 0]): 返回子串第一次出现的索引,未找到返回 -1。
String email = 'user@example.com'; print(email.contains('@')); // true print(email.startsWith('user')); // true print(email.endsWith('.com')); // true print(email.indexOf('example')); // 5截取和分割:
substring(int start, [int? end]): 提取从start到end(不包括)的子串。split(Pattern pattern): 根据分隔符拆分成一个List<String>。
String path = '/home/user/documents/file.txt'; print(path.substring(1, 5)); // home print(path.split('/')); // ['', 'home', 'user', 'documents', 'file.txt'] print('a,b,c'.split(',')); // ['a', 'b', 'c']去除空白:
trim(): 去除字符串首尾的空白字符(空格、制表符、换行符等)。trimLeft()/trimRight(): 分别去除左侧/右侧空白。
String messy = ' Hello Dart \n'; print(messy.trim()); // 'Hello Dart'替换:
replaceAll(Pattern from, String replace): 将所有匹配项替换为新字符串。
String text = 'I love cats. Cats are great.'; print(text.replaceAll('Cats', 'Dogs')); // I love dogs. Dogs are great.其他:
compareTo(String other): 按字典顺序比较两个字符串,返回负数、0或正数。codeUnitAt(int index): 获取指定索引处的 UTF-16 码元。
重要注意事项
不可变性 (Immutability): Dart 的
String是不可变的。任何看起来“修改”了字符串的方法(如toUpperCase,trim,replaceAll)实际上都返回了一个新的字符串对象,而原始字符串保持不变。String original = 'hello'; String upper = original.toUpperCase(); print(original); // hello (未改变) print(upper); // HELLO (新对象)UTF-16 编码: Dart 字符串基于 UTF-16。对于大多数基本拉丁字母,一个字符对应一个码元。但对于一些特殊字符(如某些 emoji 或 Unicode 码点大于 U+FFFF 的字符),可能需要两个码元(代理对)。因此,
length属性返回的是码元的数量,而不是直观的“字符”数量。如果需要精确的字符计数,可以使用characters包。# pubspec.yaml dependencies:characters: ^1.2.1import 'package:characters/characters.dart';void main() {String emojiStr = '👨👩👧👦'; // 家庭 emojiprint(emojiStr.length); // 可能输出 7 或 8 (码元数)print(emojiStr.characters.length); // 输出 1 (正确的字符数) }
- 集合 (Collections):
List: 有序集合,类似数组。var list = [1, 2, 3];Set: 无序且唯一元素的集合。var set = {1, 2, 3};Map: 键值对集合。var map = {'name': 'Alice', 'age': 30};
- 控制流:
if/elsefor循环 (for (var i = 0; i < 5; i++)) 和for-in循环 (for (var item in list))while/do-while
