Flutter: Dart 语言速览
Dart 是 Flutter 的灵魂语言——掌握 Dart,就像学会了乐谱才能演奏交响乐。
📋 前置知识:需要先掌握以下内容
- 第1课:Flutter 简介与环境搭建
1. 你将学到
- 变量与类型系统:
int、double、String、bool;var/final/const差异 - 集合操作:
List<E>、Map<K,V>、Set<E>;collection-if / collection-for - 函数特性:可选参数、命名参数、匿名函数与闭包
- 异步编程:
Future<T>、async/await、Stream<T> - 类与混入:构造函数、工厂模式、
abstract class、mixin
2. 一个开发者的真实故事
(1) 痛点:回调地狱与类型混乱
Bob 是 ShopApp 后端开发转 Flutter 的新人。他习惯了 JavaScript 的灵活,却在 Dart 里频频碰壁:var 和 final 分不清、异步回调嵌套 5 层、泛型集合类型报错不断。一次购物车价格计算,因为 int 和 double 隐式转换问题,导致 99.99 USD 变成了 99 USD——上线后用户投诉如潮。
(2) Dart 的解法
Dart 是强类型语言,编译期就能捕获类型错误;async/await 让异步代码像同步一样清晰;final/const 防止意外修改。
DART
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
// 简化类定义(用于演示)
class Product {
final int id;
final String name;
final double price;
final String? imageUrl;
const Product({required this.id, required this.name, required this.price, this.imageUrl});
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json['id'] as int,
name: json['name'] as String,
price: (json['price'] as num).toDouble(),
imageUrl: json['image_url'] as String?,
);
Product copyWith({String? name, double? price}) => Product(
id: id, name: name ?? this.name, price: price ?? this.price, imageUrl: imageUrl,
);
static List<Product> fromJsonList(List data) => data.map((j) => Product.fromJson(j as Map<String, dynamic>)).toList();
}
class CartItem {
final Product product;
final double price;
final int quantity;
CartItem({required this.product, required this.price, this.quantity = 1});
}
// Strong typing prevents price bugs
double calculateTotal(List<CartItem> items) {
return items.fold(0.0, (sum, item) => sum + item.price * item.quantity);
}
// async/await eliminates callback hell
Future<List<Product>> fetchProducts() async {
final response = await http.get(Uri.parse('/api/v1/products'));
return Product.fromJsonList(jsonDecode(response.body));
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
(3) 收益:类型安全 + 异步清晰
Bob 改用 Dart 后,类型错误在编译期就被捕获,异步代码扁平化,购物车价格精度问题再也没出现过。
3. 变量与类型系统
Dart 是强类型语言,支持类型推断和空安全(Sound Null Safety)。
sequenceDiagram
participant Bob
participant ShopAPI
Bob->>ShopAPI: fetchProducts() [async]
ShopAPI-->>Bob: Future<List<Product>>
Bob->>Bob: await 解析 JSON
Bob->>Bob: 渲染商品列表
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
(1) 基本类型
| 类型 | 说明 | 示例 |
|---|---|---|
int |
64 位整数 | int quantity = 3; |
double |
64 位浮点 | double price = 99.99; |
String |
UTF-16 字符串 | String name = "ShopApp"; |
bool |
布尔值 | bool inStock = true; |
num |
int + double 父类 | num value = 42; |
(2) 变量声明方式
| 关键字 | 可变性 | 赋值时机 | 适用场景 |
|---|---|---|---|
var |
可变 | 运行时 | 局部变量,类型可推断 |
final |
不可变 | 运行时 | 运行期常量(如 API 响应) |
const |
不可变 | 编译时 | 编译期常量(如颜色值) |
late |
延迟初始化 | 运行时 | 必须在使用前赋值 |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:变量声明对比
DART
// var: type inferred, mutable
var productName = 'Flutter T-Shirt';
productName = 'Dart Hoodie'; // OK
// final: runtime constant, immutable
final double totalPrice = 29.99 * 3;
// totalPrice = 100.0; // Error: final variable
// const: compile-time constant
const double discountRate = 0.15;
const String storeName = 'ShopApp';
// late: deferred initialization
late String userToken;
void login() {
userToken = 'eyJhbGciOiJIUzI1NiIs...';
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
4. 集合操作
(1) List、Map、Set
| 集合 | 特点 | 字面量语法 |
|---|---|---|
List<E> |
有序、可重复 | [1, 2, 3] |
Map<K,V> |
键值对 | {'key': 'value'} |
Set<E> |
无序、不重复 | {1, 2, 3} |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:集合基本操作
DART
import 'package:flutter/material.dart';
// Product类定义见上方
// List: ordered collection
List<Product> products = [
Product(id: 1, name: 'Laptop', price: 1299.99),
Product(id: 2, name: 'Phone', price: 899.99),
];
// Map: key-value pairs
Map<String, double> prices = {
'Laptop': 1299.99,
'Phone': 899.99,
'Tablet': 599.99,
};
// Set: unique items
Set<String> categories = {'Electronics', 'Clothing', 'Books'};
// Access and modify
products.add(Product(id: 3, name: 'Tablet', price: 599.99));
prices['Headphones'] = 199.99;
categories.add('Electronics'); // Ignored, already exists
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
(2) Collection-if / Collection-for
Dart 独有的集合内条件/循环语法,极大简化 UI 构建。
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:collection-if 和 collection-for
DART
import 'package:flutter/material.dart';
// Product类定义见上方
// collection-if: conditional items
bool isAdmin = true;
var menuItems = [
'Home',
'Products',
'Cart',
if (isAdmin) 'Admin Panel',
];
// collection-for: expand items
var productNames = ['Laptop', 'Phone', 'Tablet'];
var descriptions = [
for (var name in productNames) '$name - Best Price'
];
// ['Laptop - Best Price', 'Phone - Best Price', 'Tablet - Best Price']
// Combined in Widget list
Widget buildProductList(List<Product> products) {
return Column(
children: [
for (var p in products)
ListTile(title: Text(p.name), trailing: Text('\$${p.price}')),
],
);
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
5. 函数特性
(1) 参数类型
| 类型 | 语法 | 必填 | 默认值 |
|---|---|---|---|
| 位置参数 | (a, b) |
是 | 无 |
| 可选位置参数 | ([a = 0]) |
否 | 支持 |
| 命名参数 | ({required a}) |
required 时必填 |
支持 |
| 可选命名参数 | ({a = 0}) |
否 | 支持 |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:函数参数模式
DART
import 'package:flutter/material.dart';
// Named parameters (recommended for Flutter)
Widget buildPriceTag({
required double price,
String currency = 'USD',
double fontSize = 16.0,
}) {
return Text(
'$currency \$${price.toStringAsFixed(2)}',
style: TextStyle(fontSize: fontSize),
);
}
// Call with named params
buildPriceTag(price: 99.99);
buildPriceTag(price: 49.99, currency: 'EUR', fontSize: 20.0);
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
(2) 匿名函数与闭包
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:闭包在 Flutter 中的应用
DART
import 'package:flutter/material.dart';
// Product类定义见上方
// Anonymous function (lambda)
var expensiveItems = products.where((p) => p.price > 500.0).toList();
// Closure: captures surrounding variables
double discountThreshold = 100.0;
var discountedItems = products.map((p) {
// Closure captures discountThreshold
if (p.price > discountThreshold) {
return p.copyWith(price: p.price * 0.9);
}
return p;
}).toList();
// Callback in Flutter widget
ElevatedButton(
onPressed: () {
// Closure captures context variables
addToCart(product);
},
child: const Text('Add to Cart'),
)
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
6. 异步编程
(1) Future 与 async/await
| 概念 | 说明 | 类比 |
|---|---|---|
Future<T> |
异步操作的占位符 | 订餐取餐号 |
async |
标记异步函数 | 告诉系统"我要等" |
await |
等待 Future 完成 | 拿到餐再吃 |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:async/await 网络请求
DART
import 'dart:convert';
import 'package:http/http.dart' as http;
// Product类定义见上方
Future<List<Product>> fetchProducts({int page = 1}) async {
try {
final response = await http.get(
Uri.parse('https://api.shopapp.com/v1/products?page=$page'),
);
if (response.statusCode == 200) {
final List data = jsonDecode(response.body);
return data.map((json) => Product.fromJson(json)).toList();
} else {
throw Exception('Failed to load products: ${response.statusCode}');
}
} catch (e) {
// Handle network errors
rethrow;
}
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
(2) Stream:异步数据流
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:Stream 实时购物车更新
DART
import 'dart:async';
// 简化类定义(用于演示)
class Product {
final String name;
final double price;
const Product({required this.name, required this.price});
}
class CartItem {
final Product product;
int quantity;
CartItem({required this.product, this.quantity = 1});
}
// StreamController for cart updates
class CartBloc {
final _cartStream = StreamController<List<CartItem>>.broadcast();
Stream<List<CartItem>> get cartStream => _cartStream.stream;
List<CartItem> _items = [];
void addToCart(Product product) {
_items.add(CartItem(product: product, quantity: 1));
_cartStream.sink.add(_items);
}
void dispose() {
_cartStream.close();
}
}
// Listen to stream
cartBloc.cartStream.listen((items) {
print('Cart updated: ${items.length} items, total: \$${calculateTotal(items)}');
});
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
7. 类与混入
(1) 类与构造函数
| 构造函数类型 | 语法 | 用途 |
|---|---|---|
| 默认构造 | ClassName() |
标准实例化 |
| 命名构造 | ClassName.named() |
多种初始化方式 |
| 工厂构造 | factory ClassName() |
控制实例创建(如缓存/单例) |
| 常量构造 | const ClassName() |
编译期常量对象 |
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:Product 数据模型
DART
class Product {
final int id;
final String name;
final double price;
final String? imageUrl;
final double rating;
// Main constructor with named params
const Product({
required this.id,
required this.name,
required this.price,
this.imageUrl,
this.rating = 0.0,
});
// Named constructor from JSON
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'] as int,
name: json['name'] as String,
price: (json['price'] as num).toDouble(),
imageUrl: json['image_url'] as String?,
rating: (json['rating'] as num?)?.toDouble() ?? 0.0,
);
}
// Copy with pattern for immutable updates
Product copyWith({String? name, double? price}) {
return Product(
id: id,
name: name ?? this.name,
price: price ?? this.price,
imageUrl: imageUrl,
rating: rating,
);
}
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
(2) Mixin:代码复用
▶ 示例
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
:Mixin 实现通用功能
DART
// Product类定义见上方
// Mixin: reusable behavior without inheritance
mixin PriceFormatter {
String formatPrice(double price, {String currency = 'USD'}) {
return '$currency \$${price.toStringAsFixed(2)}';
}
String formatDiscount(double original, double discounted) {
double percent = ((original - discounted) / original * 100);
return '${percent.toStringAsFixed(0)}% OFF';
}
}
// Apply mixin to class
class ProductCard with PriceFormatter {
final Product product;
ProductCard(this.product);
String get priceLabel => formatPrice(product.price);
String get discountLabel => formatDiscount(100.0, product.price);
}
// Abstract class: cannot be instantiated
abstract class Repository<T> {
Future<List<T>> getAll();
Future<T> getById(int id);
Future<T> create(T item);
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
8. 完整示例:ShopApp 数据层
⚙️ 安装依赖:
flutter pub add http
DART
import 'dart:convert';
import 'package:http/http.dart' as http;
// Model
class Product {
final int id;
final String name;
final double price;
final String category;
const Product({
required this.id,
required this.name,
required this.price,
required this.category,
});
factory Product.fromJson(Map<String, dynamic> json) => Product(
id: json['id'] as int,
name: json['name'] as String,
price: (json['price'] as num).toDouble(),
category: json['category'] as String,
);
}
// Repository with async/await
class ProductRepository {
final String baseUrl = 'https://api.shopapp.com/v1';
Future<List<Product>> fetchProducts({int page = 1, int limit = 20}) async {
final response = await http.get(
Uri.parse('$baseUrl/products?page=$page&limit=$limit'),
);
if (response.statusCode != 200) {
throw Exception('API error: ${response.statusCode}');
}
final List data = jsonDecode(response.body)['items'] as List;
return data.map((j) => Product.fromJson(j as Map<String, dynamic>)).toList();
}
Future<Product> fetchById(int id) async {
final response = await http.get(Uri.parse('$baseUrl/products/$id'));
return Product.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
}
}
// Usage
void main() async {
final repo = ProductRepository();
final products = await repo.fetchProducts(page: 1);
for (var p in products) {
print('${p.name}: \$${p.price.toStringAsFixed(2)}');
}
}
TEXT
📖 仅展示
> **输出:** 在本地 Flutter SDK(Flutter 3.x / Dart 3.x)运行。Piston 服务器未安装 Flutter,请在本机 `flutter run` 实操对照。实际 UI/状态会因平台略有差异。
❓ 常见问题
Q final 和 const 到底有什么区别?
A final 运行时赋值一次不可再改;const 编译时确定值,必须是字面量或 const 构造函数。
Q Dart 有三元运算符吗?
A 有,
condition ? value1 : value2,还有 ??(空值合并)和 ?.(空安全访问)。Q async 函数必须返回 Future 吗?
A 不必须,但推荐。async 函数即使返回 void,内部也异步执行。
Q Stream 和 Future 什么区别?
A Future 是一次性的异步结果;Stream 是持续产生的异步数据序列,如实时股票价格。
Q mixin 和 abstract class 怎么选?
A mixin 用于添加行为(如日志、格式化),不需要 is-a 关系;abstract class 定义类型契约,需要 is-a 关系。
Q Dart 支持 null 安全吗?
A Dart 2.12+ 强制 Sound Null Safety。
String 不可为 null,String? 可为 null。编译器保证运行时不会出现 null 引用错误。📖 小节
- Dart 强类型 + 空安全,编译期捕获类型错误
var/final/const区分可变性与赋值时机- collection-if/collection-for 简化 UI 列表构建
async/await让异步代码扁平清晰- Mixin 实现横向代码复用,abstract class 定义类型契约
copyWith模式实现不可变对象的更新
📝 作业
- 基础题(难度⭐):创建一个
User类,包含 id(int)、name(String)、email(String?),实现fromJson工厂构造。 - 进阶题(难度⭐⭐):用
async/await写一个模拟网络请求函数,返回类型为Future<List<Product>>,用Future.delayed模拟 2 秒延迟。 - 挑战题(难度⭐⭐⭐):实现一个
CartManager类,用StreamController广播购物车变更,支持添加/删除商品和清空操作。