Dart 入门回顾
1. Dart 是什么
Dart 是一门类型安全、支持面向对象、支持异步编程的语言。它的语法和 JavaScript、TypeScript、Java、C# 都有一些相似之处。
Dart 常见使用场景:
- Flutter 跨平台应用开发
- 命令行工具
- Web 应用
- 服务端接口或脚本
- 作为学习现代类型语言的入口
Dart 的几个核心特点:
- 静态类型:变量、函数返回值、参数都有类型。
- 类型推断:很多时候可以用
var,让 Dart 自动推断类型。 - 空安全:默认情况下,变量不能是
null,除非你显式写?。 - 面向对象:几乎所有东西都是对象。
- 异步友好:内置
Future、Stream、async、await。 - 包管理清晰:使用
pubspec.yaml管理依赖。
2. 安装和第一个程序
2.1 安装 Dart SDK
如果你只学 Dart,可以安装 Dart SDK。
如果你准备学 Flutter,可以直接安装 Flutter,因为 Flutter 自带完整 Dart SDK。
安装完成后,在终端检查:
dart --version
2.2 创建命令行项目
dart create hello_dart
cd hello_dart
dart run
常见项目结构:
hello_dart/
bin/
hello_dart.dart
lib/
test/
pubspec.yaml
含义:
bin/:命令行入口代码。lib/:可复用业务代码。test/:测试代码。pubspec.yaml:项目配置和依赖管理。
2.3 第一个 Dart 程序
void main() {
print('Hello, Dart!');
}
main 是 Dart 程序入口。运行:
dart run
如果是单文件:
dart run main.dart
3. 变量和基础类型
3.1 var、final、const
void main() {
var name = 'Mika';
final age = 18;
const pi = 3.14159;
print(name);
print(age);
print(pi);
}
区别:
| 写法 | 含义 | 常用场景 |
|---|---|---|
var | 变量,类型由 Dart 推断 | 普通局部变量 |
final | 只能赋值一次,运行时确定 | 接口返回值、对象属性 |
const | 编译期常量 | 固定配置、常量值 |
推荐习惯:
- 默认优先用
final。 - 需要修改时再用
var。 - 真正固定不变的值用
const。
3.2 显式类型
String title = 'Dart Guide';
int count = 10;
double price = 19.9;
bool isActive = true;
Dart 常见基础类型:
| 类型 | 说明 | 示例 |
|---|---|---|
int | 整数 | 1 |
double | 小数 | 3.14 |
num | 数字,可以是 int 或 double | 10、2.5 |
String | 字符串 | 'hello' |
bool | 布尔值 | true、false |
List<T> | 数组/列表 | [1, 2, 3] |
Map<K, V> | 键值对象 | {'name': 'Mika'} |
Set<T> | 不重复集合 | {1, 2, 3} |
dynamic | 动态类型 | 不推荐滥用 |
Object | 所有对象的基类 | 比 dynamic 更安全 |
3.3 字符串
void main() {
final name = 'Mika';
final age = 18;
print('Name: $name');
print('Next year: ${age + 1}');
}
多行字符串:
final text = '''
第一行
第二行
第三行
''';
4. 空安全:Dart 新手必须先懂
Dart 默认变量不能为 null。
String name = 'Mika';
// name = null; // 错误
如果允许为 null,要加 ?:
String? nickname;
nickname = null;
nickname = '小米';
4.1 访问可空对象
void main() {
String? name;
print(name?.length); // 安全访问,name 为 null 时返回 null
print(name ?? '默认名字'); // 如果 name 是 null,使用默认值
}
常见空安全操作符:
| 操作符 | 含义 | 示例 |
|---|---|---|
? | 声明可空类型 | String? name |
?. | 安全访问 | user?.name |
?? | 空值默认值 | name ?? '匿名' |
??= | 为空时赋值 | name ??= '默认' |
! | 强制断言非空 | name!.length |
注意:! 要少用。它的意思是“我保证这里不是 null”,如果你保证错了,运行时就会报错。
5. 流程控制
5.1 if / else
void main() {
final score = 85;
if (score >= 90) {
print('优秀');
} else if (score >= 60) {
print('及格');
} else {
print('不及格');
}
}
5.2 for 循环
void main() {
for (var i = 0; i < 5; i++) {
print(i);
}
}
遍历列表:
void main() {
final names = ['Mika', 'Aki', 'Rin'];
for (final name in names) {
print(name);
}
}
5.3 while
void main() {
var count = 3;
while (count > 0) {
print(count);
count--;
}
}
5.4 switch
void main() {
final role = 'admin';
switch (role) {
case 'admin':
print('管理员');
case 'user':
print('普通用户');
default:
print('未知角色');
}
}
在 Dart 3 里,switch 也可以作为表达式:
String getRoleName(String role) {
return switch (role) {
'admin' => '管理员',
'user' => '普通用户',
_ => '未知角色',
};
}
6. 集合:List、Map、Set
6.1 List
void main() {
final numbers = [1, 2, 3];
print(numbers.length);
print(numbers[0]);
numbers.add(4);
numbers.remove(2);
print(numbers);
}
指定类型:
List<String> names = ['Mika', 'Aki'];
常用方法:
void main() {
final nums = [1, 2, 3, 4, 5];
final doubled = nums.map((item) => item * 2).toList();
final even = nums.where((item) => item.isEven).toList();
final sum = nums.reduce((a, b) => a + b);
print(doubled);
print(even);
print(sum);
}
6.2 Map
void main() {
final user = {
'name': 'Mika',
'age': 18,
};
print(user['name']);
}
指定类型:
Map<String, Object> user = {
'name': 'Mika',
'age': 18,
};
6.3 Set
void main() {
final tags = {'dart', 'flutter', 'dart'};
print(tags); // {dart, flutter}
}
Set 会自动去重。
6.4 集合里的 if 和 for
Dart 可以在集合字面量里使用 if 和 for:
void main() {
final isAdmin = true;
final menus = [
'首页',
'个人中心',
if (isAdmin) '后台管理',
];
print(menus);
}
void main() {
final ids = [1, 2, 3];
final routes = [
for (final id in ids) '/user/$id',
];
print(routes);
}
7. 函数
7.1 普通函数
int add(int a, int b) {
return a + b;
}
void main() {
print(add(1, 2));
}
7.2 箭头函数
只有一行表达式时,可以写成:
int add(int a, int b) => a + b;
7.3 可选位置参数
用 []:
String greet(String name, [String? prefix]) {
return '${prefix ?? '你好'},$name';
}
void main() {
print(greet('Mika'));
print(greet('Mika', '早上好'));
}
7.4 命名参数
用 {}:
void createUser({
required String name,
int age = 18,
}) {
print('$name, $age');
}
void main() {
createUser(name: 'Mika');
createUser(name: 'Aki', age: 20);
}
命名参数是 Dart 和 Flutter 里非常常见的写法。required 表示必须传。
7.5 函数作为参数
void runTask(void Function() task) {
print('开始');
task();
print('结束');
}
void main() {
runTask(() {
print('执行任务');
});
}
8. 类和对象
8.1 定义类
class User {
final String name;
final int age;
User(this.name, this.age);
void sayHi() {
print('你好,我是 $name,今年 $age 岁');
}
}
void main() {
final user = User('Mika', 18);
user.sayHi();
}
8.2 命名构造函数
class User {
final String name;
final int age;
User(this.name, this.age);
User.guest()
: name = '游客',
age = 0;
}
void main() {
final guest = User.guest();
print(guest.name);
}
8.3 工厂构造函数
factory 适合根据条件返回不同对象,或者封装创建逻辑:
class User {
final String name;
final int age;
User(this.name, this.age);
factory User.fromJson(Map<String, Object?> json) {
return User(
json['name'] as String,
json['age'] as int,
);
}
}
void main() {
final user = User.fromJson({
'name': 'Mika',
'age': 18,
});
print(user.name);
}
8.4 getter
class User {
final String firstName;
final String lastName;
User(this.firstName, this.lastName);
String get fullName => '$firstName $lastName';
}
void main() {
final user = User('Mika', 'Tanaka');
print(user.fullName);
}
8.5 继承
class Animal {
void speak() {
print('动物发出声音');
}
}
class Dog extends Animal {
@override
void speak() {
print('汪');
}
}
void main() {
final dog = Dog();
dog.speak();
}
8.6 abstract 抽象类
abstract class Repository {
Future<String> fetchData();
}
class ApiRepository implements Repository {
@override
Future<String> fetchData() async {
return 'data from api';
}
}
abstract class 常用于定义规范,具体实现交给子类。
8.7 mixin
mixin 可以把一组能力混入类里:
mixin Logger {
void log(String message) {
print('[LOG] $message');
}
}
class Service with Logger {
void request() {
log('请求开始');
}
}
void main() {
final service = Service();
service.request();
}
9. 异常处理
void main() {
try {
final result = 10 ~/ 0;
print(result);
} catch (error, stackTrace) {
print('出错了:$error');
print(stackTrace);
} finally {
print('无论成功失败都会执行');
}
}
主动抛出异常:
void checkAge(int age) {
if (age < 0) {
throw ArgumentError('年龄不能小于 0');
}
}
10. 异步编程:Future、async、await
Dart 里异步操作通常返回 Future<T>。
Future<String> fetchUserName() async {
await Future.delayed(Duration(seconds: 1));
return 'Mika';
}
void main() async {
final name = await fetchUserName();
print(name);
}
理解方式:
Future<String>:未来会得到一个String。async:这个函数里可以使用await。await:等待异步结果回来。
10.1 异步错误处理
Future<String> fetchData() async {
throw Exception('网络错误');
}
void main() async {
try {
final data = await fetchData();
print(data);
} catch (error) {
print('请求失败:$error');
}
}
10.2 并发请求
Future<void> main() async {
final results = await Future.wait([
fetchUser(),
fetchPosts(),
]);
print(results);
}
Future<String> fetchUser() async {
await Future.delayed(Duration(milliseconds: 500));
return 'user';
}
Future<String> fetchPosts() async {
await Future.delayed(Duration(milliseconds: 500));
return 'posts';
}
11. Stream:持续产生的数据
Future 是一次性结果,Stream 是连续结果。
Stream<int> countDown() async* {
for (var i = 3; i >= 1; i--) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (final value in countDown()) {
print(value);
}
}
常见场景:
- 倒计时
- 文件读取流
- WebSocket 消息
- Flutter 里的状态变化
12. Records 和 Patterns
Dart 3 加入了 Records 和 Patterns,适合写更简洁的数据处理。
12.1 Records
Record 可以让函数返回多个值:
(String, int) getUser() {
return ('Mika', 18);
}
void main() {
final user = getUser();
print(user.$1);
print(user.$2);
}
也可以使用命名字段:
({String name, int age}) getUser() {
return (name: 'Mika', age: 18);
}
void main() {
final user = getUser();
print(user.name);
print(user.age);
}
12.2 Patterns 解构
void main() {
final (name, age) = ('Mika', 18);
print(name);
print(age);
}
配合 switch:
String describe(Object value) {
return switch (value) {
int n when n > 0 => '正整数',
String s => '字符串:$s',
_ => '其他类型',
};
}
13. 包管理:pubspec.yaml
Dart 使用 pubspec.yaml 管理项目。
示例:
name: hello_dart
description: A simple Dart project.
version: 1.0.0
environment:
sdk: ^3.0.0
dependencies:
http: ^1.2.0
dev_dependencies:
lints: ^4.0.0
test: ^1.25.0
添加依赖:
dart pub add http
安装依赖:
dart pub get
升级依赖:
dart pub upgrade
格式化代码:
dart format .
静态检查:
dart analyze
运行测试:
dart test
14. 实战:写一个简单 Todo 命令行程序
目标:
- 新增任务
- 显示任务
- 标记任务完成
新建文件:bin/todo.dart
class Todo {
final int id;
final String title;
bool done;
Todo({
required this.id,
required this.title,
this.done = false,
});
void complete() {
done = true;
}
@override
String toString() {
final status = done ? '已完成' : '未完成';
return '[$id] $title - $status';
}
}
class TodoStore {
final List<Todo> _items = [];
List<Todo> get items => List.unmodifiable(_items);
void add(String title) {
final todo = Todo(
id: _items.length + 1,
title: title,
);
_items.add(todo);
}
void complete(int id) {
Todo? todo;
for (final item in _items) {
if (item.id == id) {
todo = item;
break;
}
}
if (todo == null) {
print('没有找到 ID 为 $id 的任务');
return;
}
todo.complete();
}
void printAll() {
if (_items.isEmpty) {
print('暂无任务');
return;
}
for (final item in _items) {
print(item);
}
}
}
void main() {
final store = TodoStore();
store.add('学习 Dart 基础语法');
store.add('写一个命令行程序');
store.add('继续学习 Flutter');
store.complete(2);
store.printAll();
}
运行:
dart run bin/todo.dart
这个例子用到了:
- 类
- 构造函数
- 命名参数
finalList- getter
- 空安全
- 方法封装
15. 实战:JSON 数据转模型
开发里很常见的场景是:接口返回 JSON,然后转成 Dart 对象。
class Article {
final int id;
final String title;
final String? summary;
Article({
required this.id,
required this.title,
this.summary,
});
factory Article.fromJson(Map<String, Object?> json) {
return Article(
id: json['id'] as int,
title: json['title'] as String,
summary: json['summary'] as String?,
);
}
Map<String, Object?> toJson() {
return {
'id': id,
'title': title,
'summary': summary,
};
}
}
void main() {
final json = {
'id': 1,
'title': 'Dart 入门',
'summary': null,
};
final article = Article.fromJson(json);
print(article.title);
print(article.toJson());
}
真实项目里,如果模型很多,可以使用 json_serializable 等工具自动生成 JSON 转换代码。
16. 实战:发起 HTTP 请求
添加依赖:
dart pub add http
示例:
import 'dart:convert';
import 'package:http/http.dart' as http;
class Post {
final int id;
final String title;
Post({
required this.id,
required this.title,
});
factory Post.fromJson(Map<String, Object?> json) {
return Post(
id: json['id'] as int,
title: json['title'] as String,
);
}
}
Future<List<Post>> fetchPosts() async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final response = await http.get(uri);
if (response.statusCode != 200) {
throw Exception('请求失败:${response.statusCode}');
}
final jsonList = jsonDecode(response.body) as List<dynamic>;
return jsonList
.map((item) => Post.fromJson(item as Map<String, Object?>))
.toList();
}
Future<void> main() async {
try {
final posts = await fetchPosts();
print(posts.first.title);
} catch (error) {
print(error);
}
}
这个例子用到了:
- 第三方包
importasync/await- HTTP 请求
- JSON 解析
- 模型转换
- 异常处理
17. 常见开发命令
| 命令 | 作用 |
|---|---|
dart --version | 查看 Dart 版本 |
dart create app_name | 创建项目 |
dart run | 运行项目 |
dart run bin/main.dart | 运行指定文件 |
dart pub get | 安装依赖 |
dart pub add package_name | 添加依赖 |
dart pub upgrade | 升级依赖 |
dart format . | 格式化代码 |
dart analyze | 静态检查 |
dart test | 运行测试 |
dart compile exe bin/main.dart | 编译为可执行文件 |
18. 推荐代码风格
18.1 命名
class UserProfile {}
void fetchUserList() {}
final userName = 'Mika';
const maxRetryCount = 3;
常见规则:
- 类名:
PascalCase - 变量、函数、参数:
camelCase - 文件名:
snake_case.dart - 私有成员:以下划线
_开头
class Counter {
int _count = 0;
int get count => _count;
void increment() {
_count++;
}
}
18.2 少用 dynamic
不推荐:
dynamic value = 'hello';
更推荐:
Object value = 'hello';
或者直接写明确类型:
String value = 'hello';
18.3 优先用 final
final users = await fetchUsers();
除非后续真的要重新赋值,否则默认用 final。
18.4 让函数短一点
不推荐一个函数里塞太多逻辑。可以拆成多个小函数:
bool isAdult(int age) => age >= 18;
String getAccessMessage(int age) {
if (isAdult(age)) {
return '可以访问';
}
return '暂不能访问';
}
19. 新手常见坑
19.1 var 不是 JavaScript 的 var
Dart 的 var 只是让编译器推断类型。推断后类型就固定了。
var value = 'hello';
// value = 123; // 错误,因为 value 已经被推断为 String
19.2 可空类型不能直接当非空用
String? name;
// print(name.length); // 错误
应该:
print(name?.length);
或者:
if (name != null) {
print(name.length);
}
19.3 final 不是深层不可变
final list = [1, 2, 3];
list.add(4); // 可以
// list = []; // 不可以
final 表示变量不能重新指向另一个对象,不代表对象内部不能变。
如果需要不可修改列表,可以:
final list = List.unmodifiable([1, 2, 3]);
19.4 忘记 await
final data = fetchData(); // data 是 Future,不是真正结果
应该:
final data = await fetchData();
19.5 乱用 !
String? name;
print(name!.length); // 运行时报错
如果不确定,就不要用 !。
20. 学完这篇后怎么继续
如果你想做 Flutter:
- 继续学 Dart 类、异步、空安全、集合处理。
- 学 Flutter Widget、布局、状态管理。
- 写一个 Todo、天气、记账、博客客户端之类的小项目。
如果你想写命令行工具:
- 学
args包解析命令行参数。 - 学文件读写。
- 学 JSON/YAML 配置。
- 学
dart compile exe打包。
如果你想写 Web:
- 学 Dart Web 模板。
- 学
package:web。 - 学构建和部署。
如果你有前端基础,可以这样类比:
| 前端概念 | Dart 对应理解 |
|---|---|
| TypeScript 类型 | Dart 静态类型 |
| Promise | Future |
| async / await | async / await |
| Array | List |
| Object / Record | Map / class / record |
| interface | abstract class / implements |
| npm | pub |
| package.json | pubspec.yaml |
21. 一周学习路线
第 1 天:环境和基础语法
- 安装 Dart SDK
- 运行
Hello, Dart - 学变量、类型、字符串、流程控制
第 2 天:集合和函数
- 学
List、Map、Set - 学函数、命名参数、箭头函数
- 写几个数据处理函数
第 3 天:类和对象
- 学 class、构造函数、getter
- 学
factory fromJson - 写一个
User、Article、Todo模型
第 4 天:空安全
- 理解
?、?.、??、! - 练习可空接口字段
- 改掉乱用
dynamic的习惯
第 5 天:异步
- 学
Future - 学
async/await - 写一个模拟请求函数
第 6 天:包和项目
- 学
pubspec.yaml - 添加
http - 请求接口并转模型
第 7 天:做一个小项目
任选一个:
- Todo 命令行工具
- JSON 文件读取工具
- 简单文章列表请求工具
- Flutter Todo 页面
22. 练习题
练习 1:成绩评级
写一个函数:
String getGrade(int score)
要求:
- 90 分以上返回
A - 80 到 89 返回
B - 60 到 79 返回
C - 60 以下返回
D
练习 2:用户模型
定义一个 User 类:
- 字段:
id、name、email - 支持
User.fromJson - 支持
toJson
练习 3:过滤任务
给定任务列表:
final todos = [
{'title': '学习 Dart', 'done': true},
{'title': '学习 Flutter', 'done': false},
{'title': '写项目', 'done': false},
];
筛选出未完成任务。
练习 4:模拟接口请求
写一个异步函数:
Future<List<String>> fetchNames()
要求:
- 延迟 1 秒返回
- 返回
['Mika', 'Aki', 'Rin'] - 在
main中用await获取并打印
23. 最小开发模板
你可以以后把这个当作新项目起手式:
class AppConfig {
final String baseUrl;
final int timeoutSeconds;
const AppConfig({
required this.baseUrl,
this.timeoutSeconds = 10,
});
}
class ApiClient {
final AppConfig config;
ApiClient(this.config);
Future<String> get(String path) async {
await Future.delayed(Duration(milliseconds: 300));
return 'GET ${config.baseUrl}$path';
}
}
Future<void> main() async {
const config = AppConfig(baseUrl: 'https://example.com');
final client = ApiClient(config);
final result = await client.get('/users');
print(result);
}
这个模板包含:
- 配置对象
- 服务类
- 异步方法
constfinal- 命名参数
24. 官方资料
- Dart 文档首页:https://dart.dev/docs
- Dart 语言概览:https://dart.dev/overview
- Dart SDK 安装:https://dart.dev/get-dart
- 空安全:https://dart.dev/null-safety
- Records:https://dart.dev/language/records
- Patterns:https://dart.dev/language/patterns
- Dart Web 入门:https://dart.dev/web/get-started
25. 最后总结
新手学 Dart,最重要的是先掌握这几件事:
- 会创建项目和运行代码。
- 会写变量、函数、流程控制和集合。
- 理解空安全,不害怕
String?。 - 会写类和数据模型。
- 会用
Future、async、await。 - 会添加依赖和组织项目。
真正开始开发时,不需要一口气背完所有语法。你只要能写出清晰的数据模型、函数、异步请求,再配合 dart analyze 和 dart format,就已经能进入实际项目开发了。
如果您觉得这篇文章有帮助,请点个赞吧~
评论
请登录后发表评论
去登录