Bài 3. Dart: lớp, bất đồng bộ và record

Bài này hoàn thiện kiến thức Dart cần cho Flutter: lớp và constructor, mixin, sealed class, record, pattern và lập trình bất đồng bộ với Future, async/await, Stream.

60 phútTrung cấp
Bạn sẽ học được
  • Viết lớp với các kiểu constructor của Dart
  • Dùng record và pattern matching
  • Hiểu Future, async/await, Stream
  • Chuyển JSON thành đối tượng Dart
Thuật ngữ cần biết
Named constructor
Constructor có tên riêng, ví dụ SinhVien.fromJson.
Factory
Constructor có thể trả về đối tượng có sẵn hoặc lớp con.
Future
Giá trị sẽ có trong tương lai (như Promise).
Stream
Chuỗi giá trị đến dần theo thời gian.

Khái niệm và lý thuyết

Mọi giá trị trong Dart đều là đối tượng. Lớp Dart hỗ trợ constructor ngắn gọn this.x, constructor có tên, factory, getter/setter, kế thừa đơn, mixin để tái sử dụng hành vi, và sealed class để mô hình hóa trạng thái kết hợp với switch vét cạn.

Dart chạy trên một luồng với vòng lặp sự kiện. Thao tác chậm (mạng, tệp) trả về Future; dùng await để chờ mà không chặn giao diện. Dữ liệu liên tục (vị trí GPS, tin nhắn) dùng Stream.

Lớp và constructor

class SinhVien {
  final String ma;
  final String ten;
  double diem;
  SinhVien(this.ma, this.ten, {this.diem = 0});
  SinhVien.moi(String ten) : this('SV000', ten);
  factory SinhVien.fromJson(Map<String, dynamic> j) =>
      SinhVien(j['ma'] as String, j['ten'] as String,
               diem: (j['diem'] as num).toDouble());
  bool get dat => diem >= 5;
  @override
  String toString() => '$ma - $ten ($diem)';
}

Mixin, sealed class và record

mixin CoLog { void log(String m) => print('[${runtimeType}] $m'); }
class DichVu with CoLog {}

sealed class KetQua {}
class ThanhCong extends KetQua { final List<String> duLieu; ThanhCong(this.duLieu); }
class Loi extends KetQua { final String thongBao; Loi(this.thongBao); }

String hienThi(KetQua k) => switch (k) {
  ThanhCong(:final duLieu) => duLieu.join(', '),
  Loi(:final thongBao)     => 'Lỗi: $thongBao',
}; // thiếu nhánh -> trình biên dịch báo lỗi

(String, int) layThongTin() => ('An', 20);   // record
final (ten, tuoi) = layThongTin();           // destructuring

Future và async/await

Future<String> taiDuLieu() async {
  await Future.delayed(const Duration(seconds: 2));
  return 'Xong';
}
Future<void> main() async {
  try {
    print(await taiDuLieu());
  } catch (e) {
    print('Lỗi: $e');
  }
}

Stream

Stream<int> demNguoc(int n) async* {
  for (var i = n; i >= 0; i--) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}
await for (final s in demNguoc(3)) print(s);
Mẹo hay

Với dự án lớn, dùng gói json_serializable hoặc freezed để sinh tự động fromJson/toJson và copyWith thay vì viết tay.

Lưu ý

Quên await khiến lệnh sau chạy trước khi dữ liệu về; trình phân tích Dart có cảnh báo unawaited_futures, nên bật trong analysis_options.yaml.

Thực hành
  1. Viết lớp SanPham có fromJson, toJson và getter giá sau thuế.
  2. Mô hình trạng thái tải dữ liệu bằng sealed class (đang tải, có dữ liệu, lỗi).
  3. Viết hàm async giả lập đăng nhập mất 1 giây, trả về record (bool, String).
  4. Tạo Stream đếm ngược và in ra từng giây.
Tóm tắt bài học
  • Dart hỗ trợ đầy đủ OOP, mixin, sealed class.
  • Record và pattern giúp trả nhiều giá trị, phân rã gọn.
  • Future/async/await cho thao tác chậm; Stream cho dữ liệu liên tục.
  • fromJson là cầu nối giữa API và đối tượng Dart.