Skip to Content
Flet🪝 Hooks

Hooks trong Flet

Học cách sử dụng Hooks để quản lý state local trong components. ⏱️ 15 phút

🎯 Mục tiêu

Nắm vững:

  • ft.use_state() - State local trong component
  • Khi nào dùng hooks vs observables

🪝 ft.use_state() - State Hook cơ bản

use_state() tạo state local cho component, tự động re-render khi state thay đổi:

import flet as ft @ft.component def Counter(): # Khai báo state với giá trị khởi tạo count, set_count = ft.use_state(0) return ft.Row([ ft.IconButton(ft.Icons.REMOVE, on_click=lambda _: set_count(count - 1)), ft.Text(str(count), size=24), ft.IconButton(ft.Icons.ADD, on_click=lambda _: set_count(count + 1)), ], alignment=ft.MainAxisAlignment.CENTER) def main(page: ft.Page): page.add(Counter()) ft.run(main)

Cách hoạt động

count, set_count = ft.use_state(0) # │ │ │ # │ │ └── Giá trị khởi tạo # │ └── Hàm để cập nhật state # └── Giá trị hiện tại của state

📝 use_state với các kiểu dữ liệu

String

@ft.component def NameInput(): name, set_name = ft.use_state("") return ft.Column([ ft.TextField( value=name, label="Tên của bạn", on_change=lambda e: set_name(e.control.value), ), ft.Text(f"Xin chào, {name}!" if name else "Nhập tên của bạn"), ])

Boolean

@ft.component def ToggleCard(): is_visible, set_visible = ft.use_state(True) return ft.Column([ ft.Switch( label="Hiển thị nội dung", value=is_visible, on_change=lambda _: set_visible(not is_visible), ), ft.Text("Nội dung secret! 🤫") if is_visible else ft.Container(), ])

List

@ft.component def TagList(): tags, set_tags = ft.use_state([]) tag_input, set_input = ft.use_state("") def add_tag(_): if tag_input and tag_input not in tags: set_tags([*tags, tag_input]) # Tạo list mới set_input("") def remove_tag(tag): set_tags([t for t in tags if t != tag]) return ft.Column([ ft.Row([ ft.TextField( value=tag_input, hint_text="Thêm tag...", on_change=lambda e: set_input(e.control.value), on_submit=add_tag, ), ft.IconButton(ft.Icons.ADD, on_click=add_tag), ]), ft.Row([ ft.Chip( label=ft.Text(tag), on_delete=lambda _, t=tag: remove_tag(t), ) for tag in tags ], wrap=True), ])

Dict/Object

@ft.component def UserForm(): user, set_user = ft.use_state({"name": "", "email": ""}) def update_field(field, value): set_user({**user, field: value}) # Spread operator để tạo object mới return ft.Column([ ft.TextField( label="Tên", value=user["name"], on_change=lambda e: update_field("name", e.control.value), ), ft.TextField( label="Email", value=user["email"], on_change=lambda e: update_field("email", e.control.value), ), ft.Text(f"User: {user}"), ])

⚠️ Quy tắc quan trọng

[!CAUTION] Không mutate state trực tiếp! Luôn tạo giá trị mới:

# ❌ SAI - Mutate trực tiếp items.append(new_item) set_items(items) # ✅ ĐÚNG - Tạo list mới set_items([*items, new_item])

🔄 Nhiều states trong một component

@ft.component def LoginForm(): # Nhiều states độc lập username, set_username = ft.use_state("") password, set_password = ft.use_state("") is_loading, set_loading = ft.use_state(False) error, set_error = ft.use_state("") def handle_login(_): set_loading(True) set_error("") # ... login logic return ft.Column([ ft.TextField(label="Username", value=username, on_change=lambda e: set_username(e.control.value)), ft.TextField(label="Password", value=password, password=True, on_change=lambda e: set_password(e.control.value)), ft.Text(error, color=ft.Colors.RED) if error else ft.Container(), ft.FilledButton( "Login", on_click=handle_login, disabled=is_loading, ), ])

📐 use_state vs Observable

Đặc điểmuse_state@ft.observable
Phạm viComponent localToàn app
Chia sẻKhông
Định nghĩaTrong componentNgoài component
Use caseForm input, toggle, UI stateUser data, cart, settings

🎯 Ví dụ thực tế: Counter với Reset

import flet as ft @ft.component def CounterWithReset(): count, set_count = ft.use_state(0) return ft.Card( content=ft.Container( content=ft.Column([ ft.Text("Counter", size=20, weight=ft.FontWeight.BOLD), ft.Text(str(count), size=48), ft.Row([ ft.IconButton(ft.Icons.REMOVE_CIRCLE, on_click=lambda _: set_count(count - 1)), ft.IconButton(ft.Icons.ADD_CIRCLE, on_click=lambda _: set_count(count + 1)), ft.IconButton(ft.Icons.REFRESH, on_click=lambda _: set_count(0)), ], alignment=ft.MainAxisAlignment.CENTER), ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), padding=20, ), ) def main(page: ft.Page): page.vertical_alignment = ft.MainAxisAlignment.CENTER page.horizontal_alignment = ft.CrossAxisAlignment.CENTER page.add(CounterWithReset()) ft.run(main)

⏭️ Bước tiếp theo

Đã nắm vững Hooks! Hãy ứng dụng vào Mini Project: Counter App để thực hành.

Last updated on