Skip to Content
Flutter⚡ Nâng cao🔑 Keys trong Flutter

Keys trong Flutter

1. Key là gì?

Key là một định danh duy nhất giúp Flutter phân biệt các widgets trong Widget Tree. Khi không có key, Flutter dựa vào vị tríkiểu của widget để xác định widget nào là widget nào khi rebuild.

Không có Key: Có Key: ┌──────────────┐ ┌──────────────┐ │ Widget A [0] │ │ Widget A [key1] │ ├──────────────┤ ├──────────────┤ │ Widget B [1] │ │ Widget B [key2] │ ├──────────────┤ ├──────────────┤ │ Widget C [2] │ │ Widget C [key3] │ └──────────────┘ └──────────────┘ ↓ ↓ Flutter dựa vào Flutter dựa vào INDEX để nhận diện KEY để nhận diện

2. Tại sao cần Key?

Vấn đề khi không có Key

Khi thứ tự hoặc số lượng widgets thay đổi, Flutter có thể nhầm lẫn widget nào là widget nào:

Trước khi xóa Item B: Sau khi xóa Item B: ┌──────────────┐ ┌──────────────┐ │ Item A [0] │ │ Item A [0] │ ← Vẫn đúng ├──────────────┤ ├──────────────┤ │ Item B [1] │ → │ Item C [1] │ ← Flutter nghĩ đây là Item B! ├──────────────┤ └──────────────┘ │ Item C [2] │ └──────────────┘

Hậu quả: State của Item B có thể gán nhầm cho Item C!

Giải pháp với Key

Trước khi xóa Item B: Sau khi xóa Item B: ┌──────────────────┐ ┌──────────────────┐ │ Item A [keyA] │ │ Item A [keyA] │ ← Match by key ├──────────────────┤ ├──────────────────┤ │ Item B [keyB] │ → │ Item C [keyC] │ ← Match by key, đúng state! ├──────────────────┤ └──────────────────┘ │ Item C [keyC] │ └──────────────────┘

3. Các loại Key

3.1 ValueKey

Dùng khi bạn có một giá trị duy nhất để định danh:

ValueKey('user-123') ValueKey(item.id) ValueKey(email)
┌─────────────────────────────────────┐ │ ValueKey<T> │ ├─────────────────────────────────────┤ │ So sánh bằng value.operator==() │ │ │ │ ValueKey(1) == ValueKey(1) ✓ │ │ ValueKey('a') == ValueKey('a') ✓ │ │ ValueKey(1) == ValueKey(2) ✗ │ └─────────────────────────────────────┘

3.2 ObjectKey

Dùng khi bạn muốn so sánh bằng identity (cùng object instance):

ObjectKey(userObject) ObjectKey(myInstance)
┌─────────────────────────────────────┐ │ ObjectKey │ ├─────────────────────────────────────┤ │ So sánh bằng identical() │ │ │ │ obj1 = User('A') │ │ obj2 = User('A') │ │ ObjectKey(obj1) == ObjectKey(obj1) ✓│ │ ObjectKey(obj1) == ObjectKey(obj2) ✗│ └─────────────────────────────────────┘

3.3 UniqueKey

Tạo key mới mỗi lần (dùng khi muốn force rebuild):

UniqueKey() // Mỗi lần gọi tạo key khác nhau
┌─────────────────────────────────────┐ │ UniqueKey │ ├─────────────────────────────────────┤ │ Luôn tạo key mới │ │ │ │ UniqueKey() != UniqueKey() ✓ │ │ Sử dụng: Force widget rebuild │ └─────────────────────────────────────┘

3.4 GlobalKey

Key có thể truy cập từ bất kỳ đâu trong app:

final formKey = GlobalKey<FormState>(); // Sau đó: formKey.currentState?.validate()
┌─────────────────────────────────────────────┐ │ GlobalKey<T> │ ├─────────────────────────────────────────────┤ │ • Định danh duy nhất trong toàn bộ app │ │ • Có thể truy cập State và Widget │ │ • Tốn performance - dùng hạn chế! │ │ │ │ formKey.currentState → Access State │ │ formKey.currentWidget → Access Widget │ │ formKey.currentContext → Access Context │ └─────────────────────────────────────────────┘

4. Quyết định Key Selection Tree

Cần Key không? ┌────────────┴────────────┐ │ │ Danh sách Widget cố định có thể thay đổi không đổi thứ tự │ │ CẦN KEY KHÔNG CẦN KEY ┌───────┴───────┐ │ │ Có ID Không có ID unique? unique? │ │ ValueKey(id) ObjectKey(object) hoặc UniqueKey() (rare)

5. Ví dụ minh họa: Checkbox List Bug

Không có Key (Bug!)

Initial State: Sau khi xóa "Apple": ┌─────────────────────┐ ┌─────────────────────┐ │ [✓] Apple [0] │ │ [✓] Banana [0] │ ← Checkbox state của Apple! ├─────────────────────┤ ├─────────────────────┤ │ [ ] Banana [1] │ → │ [ ] Cherry [1] │ ← Checkbox state của Banana! ├─────────────────────┤ └─────────────────────┘ │ [ ] Cherry [2] │ └─────────────────────┘ Flutter giữ state theo INDEX, không theo item!

Có Key (Đúng!)

Initial State: Sau khi xóa "Apple": ┌───────────────────────────┐ ┌───────────────────────┐ │ [✓] Apple [key: apple] │ │ [ ] Banana [key: banana]│ ← Đúng state! ├───────────────────────────┤ ├───────────────────────┤ │ [ ] Banana [key: banana]│ → │ [ ] Cherry [key: cherry]│ ← Đúng state! ├───────────────────────────┤ └───────────────────────┘ │ [ ] Cherry [key: cherry]│ └───────────────────────────┘ Flutter giữ state theo KEY!

6. Khi nào dùng Key?

✅ Nên dùng Key

Trường hợpLoại Key
ListView.builder với items có thể reorder/deleteValueKey(item.id)
AnimatedListValueKey hoặc ObjectKey
Form validationGlobalKey<FormState>
Access widget state từ bên ngoàiGlobalKey
Shuffle widgets với animationValueKey

❌ Không cần Key

Trường hợp
Danh sách tĩnh không thay đổi
Widgets không có state
Thứ tự widgets không bao giờ thay đổi

7. Performance Considerations

┌──────────────────────────────────────────────────────┐ │ Performance Impact │ ├──────────────────────────────────────────────────────┤ │ │ │ LocalKey (ValueKey, ObjectKey, UniqueKey) │ │ └── Tốt ✓ │ │ • So sánh nhanh │ │ • Chỉ trong phạm vi parent │ │ │ │ GlobalKey │ │ └── Tốn performance ⚠️ │ │ • Cần maintain registry toàn app │ │ • Chỉ dùng khi thực sự cần │ │ │ └──────────────────────────────────────────────────────┘

📝 Tóm tắt

Key TypeSo sánh bằngUse case
ValueKey== (value equality)ID, string, number
ObjectKeyidentical (same instance)Object reference
UniqueKeyLuôn khác nhauForce rebuild
GlobalKeyRegistry lookupAccess state/widget

Quy tắc vàng:

Dùng Key khi thứ tự hoặc số lượng widgets có thể thay đổi và widgets có state riêng.

Last updated on