Flutter: Dart 语言速览

Dart 是 Flutter 的灵魂语言——掌握 Dart,就像学会了乐谱才能演奏交响乐。

📋 前置知识:需要先掌握以下内容

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:回调地狱与类型混乱

Bob 是 ShopApp 后端开发转 Flutter 的新人。他习惯了 JavaScript 的灵活,却在 Dart 里频频碰壁:varfinal 分不清、异步回调嵌套 5 层、泛型集合类型报错不断。一次购物车价格计算,因为 intdouble 隐式转换问题,导致 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)。

100%
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 引用错误。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建一个 User 类,包含 id(int)、name(String)、email(String?),实现 fromJson 工厂构造。
  2. 进阶题(难度⭐⭐):用 async/await 写一个模拟网络请求函数,返回类型为 Future<List<Product>>,用 Future.delayed 模拟 2 秒延迟。
  3. 挑战题(难度⭐⭐⭐):实现一个 CartManager 类,用 StreamController 广播购物车变更,支持添加/删除商品和清空操作。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏