- Khai báo class với field, method, constructor.
- Sử dụng đủ 5 loại constructor: default, named, initializer list, redirecting, factory.
- Hiểu getter/setter (computed property).
- Phân biệt instance member vs static member.
- Inheritance:
extends,@override,super, abstract class/method. - Interface (implicit interface) và
implements. - Mixin:
mixin,with,onconstraint. - Class modifier Dart 3:
base,final,sealed,interface,mixin class. - Override
toString,==,hashCode+ patterncopyWith.
1. Class anatomy
class Point {
// Fields
final int x;
final int y;
// Constructor
Point(this.x, this.y);
// Method
double distanceTo(Point other) {
final dx = x - other.x;
final dy = y - other.y;
return (dx * dx + dy * dy).toDouble().sqrt();
}
}
void main() {
final p = Point(3, 4);
print(p.x); // 3
print(p.distanceTo(Point(0, 0))); // 5
}
Naming conventions:
- Class: PascalCase (
Point,HttpClient). - Field, method, variable: camelCase.
- Private: prefix
_(library-private, không class-private).
new keyword? Không cần
Dart 2+ new là optional. Idiomatic không dùng:
final p1 = new Point(3, 4); // OK nhưng lỗi thời
final p2 = Point(3, 4); // idiomatic Dart
2. Constructor — 5 loại
2.1 Default constructor
class Point {
final int x;
final int y;
// Cách dài
Point(int x, int y)
: x = x,
y = y;
// Cách ngắn — shorthand "this.x"
Point(this.x, this.y);
}
this.x shorthand tự gán param vào field tương ứng — tránh boilerplate.
2.2 Named constructor — nhiều cách tạo instance
class Point {
final int x;
final int y;
// Default
Point(this.x, this.y);
// Named
Point.origin() : x = 0, y = 0;
Point.fromMap(Map<String, int> m) : x = m['x']!, y = m['y']!;
}
final a = Point(3, 4);
final b = Point.origin();
final c = Point.fromMap({'x': 1, 'y': 2});
2.3 Initializer list — chạy trước body
class Rectangle {
final int width;
final int height;
final int area; // final phải gán ở initializer list
Rectangle(this.width, this.height)
: area = width * height,
assert(width > 0),
assert(height > 0) {
print('Constructed: $width × $height');
}
}
Initializer list chạy trước body. Dùng cho:
- Gán final field từ expression dùng param.
assertsanity check.- Gọi
super(...)với arg.
2.4 Const constructor
class Point {
final int x;
final int y;
const Point(this.x, this.y); // const constructor — yêu cầu mọi field final
}
const a = Point(1, 2);
const b = Point(1, 2);
print(identical(a, b)); // true — canonicalized!
Class có const constructor → 2 instance const cùng arg dùng cùng memory. Cực kỳ quan trọng cho Flutter performance (widget không rebuild khi const).
2.5 Redirecting constructor
class Point {
final int x, y;
Point(this.x, this.y);
Point.zero() : this(0, 0); // redirect tới default
Point.diag(int v) : this(v, v);
}
2.6 Factory constructor — không bắt buộc trả instance mới
Đây là loại constructor đặc biệt của Dart. factory không bắt buộc trả instance mới — có thể cache, return subclass, hoặc null (nullable type).
class Logger {
static final _instances = <String, Logger>{};
final String name;
Logger._internal(this.name); // private constructor
factory Logger(String name) {
return _instances.putIfAbsent(name, () => Logger._internal(name));
}
void log(String msg) => print('[$name] $msg');
}
void main() {
final a = Logger('http');
final b = Logger('http');
print(identical(a, b)); // true — same instance, cache hit
final c = Logger('db');
print(identical(a, c)); // false — khác name, instance khác
}
Use cases factory:
- Singleton per key (như Logger trên).
- Parse JSON:
factory User.fromJson(Map json) => User(...). - Return subclass:
factory Shape.from(String type) => type == 'circle' ? Circle() : Square();. - Cache expensive object.
3. Getter / Setter — computed property
class Rectangle {
int width, height;
Rectangle(this.width, this.height);
// Getter — không có ()
int get area => width * height;
int get perimeter => 2 * (width + height);
// Setter
set dimensions(List<int> xy) {
width = xy[0];
height = xy[1];
}
}
final r = Rectangle(3, 4);
print(r.area); // 12 — gọi như field, không có ()
r.dimensions = [5, 6]; // gọi setter
Từ caller, r.area trông giống r.width (field). Đây là feature: bạn có thể refactor field thành getter
mà không breaking caller code.
4. Static member
class MathHelper {
static const pi = 3.14159;
static int square(int x) => x * x;
static int cube(int x) => x * x * x;
}
print(MathHelper.pi); // 3.14159
print(MathHelper.square(5)); // 25
// Không gọi từ instance
final h = MathHelper();
// h.square(5); // ❌ Lỗi
5. Private — prefix _
Dart không có private keyword. Convention: prefix _ = private.
// File: user.dart
class User {
String name; // public
String _passwordHash; // private — chỉ accessible trong cùng file/library
User(this.name, this._passwordHash);
}
// File: main.dart
import 'user.dart';
void main() {
final u = User('Việt', 'abc123');
print(u.name); // OK
// print(u._passwordHash); // ❌ Lỗi — private khác file
}
Khác Java/C# (private = class scope). Dart private = library scope — mọi file trong cùng library access được. Đa số case: 1 file = 1 library, nên ≈ file scope.
6. Inheritance — extends
class Animal {
final String name;
Animal(this.name);
void speak() => print('$name làm âm thanh chung');
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() => print('$name gâu gâu');
}
void main() {
Animal a = Dog('Mực');
a.speak(); // Mực gâu gâu (polymorphism)
}
Quy tắc:
- Dart single inheritance — chỉ 1 parent. Multi-inheritance qua mixin.
@overrideannotation — không bắt buộc nhưng analyzer recommend (catch typo).super(args)trong initializer list gọi constructor parent.- Mọi class implicit extends
Object.
7. Abstract class & method
abstract class Shape {
double area(); // abstract method — không body
double perimeter();
// Concrete method — có body, mọi subclass thừa kế
void describe() {
print('Area: ${area()}, Perimeter: ${perimeter()}');
}
}
class Circle extends Shape {
final double radius;
Circle(this.radius);
@override
double area() => 3.14 * radius * radius;
@override
double perimeter() => 2 * 3.14 * radius;
}
// Shape s = Shape(); // ❌ Lỗi — không instantiate abstract
Shape s = Circle(5);
s.describe();
8. Implicit interface — implements
Mọi class Dart là interface ngầm của chính nó. Không cần định nghĩa interface riêng.
class Animal {
void eat() => print('ăn');
void sleep() => print('ngủ');
}
// Implements — chỉ commit signature, KHÔNG kế thừa code
class Robot implements Animal {
@override
void eat() => print('nạp pin');
@override
void sleep() => print('standby');
}
Animal r = Robot();
r.eat(); // nạp pin
extends vs implements vs with| Quan hệ | Kế thừa code? | Phải override method? | Use case |
|---|---|---|---|
extends A | Có | Chỉ method abstract | "Is-a" relationship, share code |
implements A | Không | Tất cả | "Behaves like" — commit signature only |
with M | Có | Không | Compose behavior từ mixin |
9. Mixin — share code không qua inheritance
mixin Walker {
void walk() => print('đi bộ');
}
mixin Swimmer {
void swim() => print('bơi');
}
class Duck with Walker, Swimmer {}
void main() {
final d = Duck();
d.walk(); // đi bộ
d.swim(); // bơi
}
Mixin với constraint on — chỉ áp dụng được cho class kế thừa class cụ thể:
class Animal {}
mixin Flyer on Animal { // chỉ class extends Animal mới dùng được
void fly() => print('bay');
}
class Bird extends Animal with Flyer {} // OK
// class Plane with Flyer {} // ❌ — Plane không extends Animal
Mixin compose code vào class — không phải parent. Một class có thể có nhiều mixin (vd with M1, M2, M3).
Method resolution order: A → M3 → M2 → M1 → ParentOfA → Object. Đây gọi là linearization.
10. Class modifier Dart 3
Dart 3 thêm 5 keyword class modifier để kiểm soát ai được extends/implements class của bạn.
| Modifier | Cho phép extends? | Cho phép implements? | Cho phép as mixin? | Use case |
|---|---|---|---|---|
| (none) | Cùng/khác library | Cùng/khác library | Không | Default open |
base | Cùng/khác library | Chỉ cùng library | Không | Force extends — chống abuse implements |
final | Chỉ cùng library | Chỉ cùng library | Không | Khoá hierarchy — không cho subtype ở library khác |
sealed | Chỉ cùng library | Chỉ cùng library | Không | Exhaustive switch + chỉ subclass trong file |
interface | Chỉ cùng library | Cùng/khác library | Không | "Pure interface" — không cho extends ngoài lib |
mixin class | Có | Có | Có | Class dùng được như mixin (legacy support) |
// File a.dart
sealed class Result {}
class Success extends Result {}
class Failure extends Result {}
// File b.dart
import 'a.dart';
// class Other extends Result {} // ❌ — sealed không cho extends ngoài library
// Nhưng switch trên Result là EXHAUSTIVE — compiler enforce
String describe(Result r) {
return switch (r) {
Success() => 'ok',
Failure() => 'fail',
// Không cần default — compiler biết chỉ 2 case
};
}
11. toString, ==, hashCode
class Point {
final int x, y;
const Point(this.x, this.y);
@override
String toString() => 'Point($x, $y)';
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is Point && other.x == x && other.y == y);
@override
int get hashCode => Object.hash(x, y);
}
final a = Point(1, 2);
final b = Point(1, 2);
print(a == b); // true
print(a.hashCode == b.hashCode); // true
print(a); // Point(1, 2)
== phải override hashCode
Quy tắc: nếu a == b thì a.hashCode == b.hashCode. Vi phạm → hash-based collection (Set, Map) vỡ.
Analyzer cảnh báo. Trong thực tế dùng package equatable hoặc freezed để auto-gen — sẽ thấy ở Flutter.
12. Pattern copyWith
Pattern chuẩn cho immutable model — Flutter dùng khắp nơi (Bloc state, theme config, etc).
class User {
final String name;
final int age;
final bool premium;
const User({required this.name, required this.age, this.premium = false});
User copyWith({String? name, int? age, bool? premium}) {
return User(
name: name ?? this.name,
age: age ?? this.age,
premium: premium ?? this.premium,
);
}
}
final u1 = User(name: 'Việt', age: 25);
final u2 = u1.copyWith(age: 26); // User(Việt, 26, false)
final u3 = u1.copyWith(premium: true); // User(Việt, 25, true)
13. Enum nâng cao (Dart 2.17+)
enum Priority {
low(0),
medium(1),
high(2),
critical(3);
final int weight;
const Priority(this.weight);
bool isHigherThan(Priority other) => weight > other.weight;
String get label => switch (this) {
Priority.low => 'Thấp',
Priority.medium => 'Trung bình',
Priority.high => 'Cao',
Priority.critical => 'Cực cao',
};
}
print(Priority.high.label); // Cao
print(Priority.high.isHigherThan(Priority.low)); // true
14. Bài tập
Money class với ==, hashCode, copyWith
Implement class Money với field amount: int (cents) và currency: String.
Override ==, hashCode, toString. Thêm copyWith.
Test: 2 instance cùng amount + currency → == trả true.
💡 Gợi ý đáp án
class Money {
final int amount;
final String currency;
const Money(this.amount, this.currency);
Money copyWith({int? amount, String? currency}) =>
Money(amount ?? this.amount, currency ?? this.currency);
@override
String toString() => '${amount / 100} $currency';
@override
bool operator ==(Object o) =>
identical(this, o) ||
(o is Money && o.amount == amount && o.currency == currency);
@override
int get hashCode => Object.hash(amount, currency);
}
void main() {
final a = Money(100, 'USD');
final b = Money(100, 'USD');
print(a == b); // true
print(a.copyWith(amount: 200)); // 2.0 USD
}
Shape hierarchy
Tạo abstract class Shape với double area(). Implement 3 subclass: Circle,
Rectangle, Triangle. Viết function Shape biggest(List<Shape> shapes)
trả về Shape lớn nhất.
💡 Gợi ý đáp án
import 'dart:math';
abstract class Shape {
double area();
}
class Circle extends Shape {
final double r;
Circle(this.r);
@override double area() => pi * r * r;
}
class Rectangle extends Shape {
final double w, h;
Rectangle(this.w, this.h);
@override double area() => w * h;
}
class Triangle extends Shape {
final double base, height;
Triangle(this.base, this.height);
@override double area() => 0.5 * base * height;
}
Shape biggest(List<Shape> shapes) =>
shapes.reduce((a, b) => a.area() > b.area() ? a : b);
void main() {
final s = biggest([Circle(5), Rectangle(4, 8), Triangle(6, 10)]);
print('${s.runtimeType}: ${s.area()}');
}
Logger mixin
Viết mixin Logger thêm method void log(String msg) in ra với timestamp prefix.
Apply vào 2 class khác nhau (vd HttpClient, DbClient) và demo.
💡 Gợi ý đáp án
mixin Logger {
void log(String msg) {
print('[${DateTime.now().toIso8601String()}] $msg');
}
}
class HttpClient with Logger {
void get(String url) {
log('GET $url');
}
}
class DbClient with Logger {
void query(String sql) {
log('SQL: $sql');
}
}
void main() {
HttpClient().get('/users'); // [2026-...] GET /users
DbClient().query('SELECT *'); // [2026-...] SQL: SELECT *
}
Singleton qua factory
Refactor pattern Singleton manual (private static + private constructor) sang dùng factory constructor.
So sánh code length + clarity.
💡 Gợi ý đáp án
// Manual singleton (verbose)
class AppManualSingleton {
static AppManualSingleton? _instance;
static AppManualSingleton get instance => _instance ??= AppManualSingleton._();
AppManualSingleton._();
}
// Factory singleton — gọi như constructor bình thường
class App {
static final App _instance = App._internal();
factory App() => _instance;
App._internal();
}
void main() {
print(identical(App(), App())); // true — cùng instance
}
Factory singleton: caller gọi App() tự nhiên — không cần biết internal. Manual: caller phải gọi .instance.
Sealed Result<T> chuẩn bị cho chương 8
Tạo sealed class Result<T> với 2 subclass: Success<T>(T data) và Failure(String error).
Demo create + dùng cơ bản (chưa cần switch — sẽ học ở chương 8).
💡 Gợi ý đáp án
sealed class Result<T> {
const Result();
}
class Success<T> extends Result<T> {
final T data;
const Success(this.data);
}
class Failure extends Result<Never> {
final String error;
const Failure(this.error);
}
Result<int> divide(int a, int b) {
if (b == 0) return Failure('chia 0');
return Success(a ~/ b);
}
void main() {
final r = divide(10, 2);
if (r is Success<int>) print(r.data); // 5
if (r is Failure) print(r.error);
}
Chương 8 sẽ học switch (r) { case Success(:var data): ...; case Failure(:var error): ... } đẹp hơn.
15. Quiz
Constructor Point.origin() gọi từ ngoài bằng cách nào?
Xem đáp án
Đáp án: Point.origin() — gọi với cú pháp tên class.tên constructor(). Không phải Point().origin().
factory constructor có bắt buộc trả về instance mới không?
Xem đáp án
Đáp án: Không. Đây là điểm đặc biệt. Có thể trả cached instance, instance subclass, hoặc null (nếu return type nullable). Tự do hơn constructor thường.
Dart có keyword interface không?
Xem đáp án
Đáp án: Có ở Dart 3 (class modifier). Nhưng từ Dart 2, mọi class đã là implicit interface — implements AnyClass luôn work mà không cần interface keyword.
Sau class A extends B with M1, M2, method resolution order là gì?
Xem đáp án
Đáp án: A → M2 → M1 → B → Object. Đọc từ trái sang phải mixin, sau cùng đến parent. Gọi là linearization.
Override == mà không override hashCode — chuyện gì?
Xem đáp án
Đáp án: Analyzer cảnh báo (hash_and_equals lint). Hash-based collection (Set, Map) sẽ behave sai — vì hashCode mặc định là identity, không match logic ==.
sealed class Dart 3 dùng để làm gì?
Xem đáp án
Đáp án: Giới hạn subclass trong cùng library (file), enable exhaustive switch — compiler đảm bảo mọi subclass được handle. Pattern phổ biến cho state machine, Result type.
_name field — private ở scope nào?
Xem đáp án
Đáp án: Library scope (file scope nếu không export). Khác Java/C# class-scope. Đa số case 1 file = 1 library, nên ≈ file scope.
static method gọi từ instance được không?
Xem đáp án
Đáp án: Không. Chỉ qua tên class: User.all(), không user.all(). Khác JavaScript class.
16. Tổng kết
- ✅ Class anatomy: field, method, constructor.
- ✅ 5 loại constructor: default, named, initializer list, redirecting, factory.
- ✅ Getter/setter — computed property, gọi như field.
- ✅
staticmember — gọi qua tên class. - ✅
_prefix — library-private, không class-private. - ✅
extendssingle inheritance +@override+super. - ✅
abstractclass — không instantiate được. - ✅
implements— implicit interface, mọi class auto là interface. - ✅
mixin+with+onconstraint. - ✅ Class modifier Dart 3:
base,final,sealed,interface,mixin class. - ✅ Pattern:
==+hashCode+copyWith. - ✅ Enum nâng cao với field + method (Dart 2.17+).
17. Kết nối tới các chương khác
- Chương 5 — Null Safety: field
latecho lazy init. - Chương 8 — Patterns: sealed class + exhaustive switch deep dive.
- Flutter chương 2: mọi widget là
class extends StatelessWidget/StatefulWidget. - Flutter chương 7: Bloc/Cubit dựa class hierarchy.