Skip to Content
Flet🧩 Components & Observables

Components & Observables trong Flet

Học cách viết UI theo phong cách Declarative với Components và Observables. ⏱️ 25 phút

🎯 Mục tiêu

Nắm vững 2 khái niệm quan trọng nhất của Flet 0.80:

  • @ft.component - Tạo UI components tái sử dụng
  • @ft.observable - Quản lý state tự động cập nhật UI

🔄 Imperative vs Declarative

Cách cũ (Imperative)

# Thay đổi control → gọi update def increment(e): counter.value = str(int(counter.value) + 1) page.update() # 👈 Phải gọi thủ công counter = ft.Text("0")

Cách mới (Declarative) ✅

# Thay đổi state → UI tự động cập nhật @ft.component def Counter(): count, set_count = ft.use_state(0) return ft.Text(str(count)) # 👈 Tự động cập nhật

🧩 @ft.component - Tạo Component

Component là hàm trả về UI, có thể tái sử dụng:

import flet as ft @ft.component def Greeting(name: str): return ft.Container( content=ft.Column([ ft.Text(f"Xin chào, {name}! 👋", size=24), ft.Text("Chào mừng đến với Flet"), ]), padding=20, bgcolor=ft.Colors.BLUE_100, border_radius=10, ) def main(page: ft.Page): page.add( Greeting("Phúc"), Greeting("Bình"), ) ft.run(main)

Quy tắc viết Component

  1. Đánh dấu với @ft.component
  2. Tên hàm viết PascalCase (Greeting, UserCard…)
  3. Return một control duy nhất (dùng Column/Row để gom nhiều controls)

📊 @ft.observable - Quản lý State toàn cục

Observable là class lưu trữ state dùng chung, tự động cập nhật UI khi thay đổi:

import flet as ft # Định nghĩa observable class @ft.observable class AppState: count: int = 0 def increment(self): self.count += 1 def decrement(self): self.count -= 1 # Tạo instance state = AppState() @ft.component def Counter(): return ft.Row([ ft.IconButton(ft.Icons.REMOVE, on_click=lambda _: state.decrement()), ft.Text(str(state.count), size=24), # 👈 Tự động cập nhật khi count thay đổi ft.IconButton(ft.Icons.ADD, on_click=lambda _: state.increment()), ], alignment=ft.MainAxisAlignment.CENTER) def main(page: ft.Page): page.add(Counter()) ft.run(main)

Observable với Collections

@ft.observable class TodoState: items: list = [] # Observable list def add(self, text: str): self.items.append({"id": len(self.items), "text": text, "done": False}) def toggle(self, id: int): for item in self.items: if item["id"] == id: item["done"] = not item["done"] break def remove(self, id: int): self.items = [item for item in self.items if item["id"] != id] state = TodoState()

🎯 Ví dụ: Todo App với Observable

import flet as ft @ft.observable class TodoState: items: list = [] def add(self, text: str): self.items.append({"id": len(self.items), "text": text, "done": False}) def toggle(self, id: int): for item in self.items: if item["id"] == id: item["done"] = not item["done"] break state = TodoState() @ft.component def TodoItem(item): return ft.ListTile( leading=ft.Checkbox( value=item["done"], on_change=lambda _: state.toggle(item["id"]), ), title=ft.Text( item["text"], style=ft.TextStyle( decoration=ft.TextDecoration.LINE_THROUGH if item["done"] else None ), ), ) @ft.component def TodoList(): input_ref, set_input = ft.use_state("") def add_todo(_): if input_ref: state.add(input_ref) set_input("") return ft.Column([ ft.Row([ ft.TextField( value=input_ref, hint_text="Thêm việc cần làm...", expand=True, on_change=lambda e: set_input(e.control.value), on_submit=add_todo, ), ft.IconButton(ft.Icons.ADD, on_click=add_todo), ]), ft.Column([TodoItem(item) for item in state.items]), ]) def main(page: ft.Page): page.title = "Todo App" page.add(TodoList()) ft.run(main)

📐 Khi nào dùng gì?

Trường hợpGiải pháp
State local (1 component)ft.use_state()
State chia sẻ (nhiều components)@ft.observable class

💡 Best Practices

Tip
  1. Tách nhỏ components - Mỗi component làm một việc
  2. State nâng lên - Đưa state lên component cha nếu cần chia sẻ
  3. Observable cho app state - User data, settings, cart…
  4. use_state cho UI state - Form input, toggle visibility…

⏭️ Bước tiếp theo

Tuyệt! Tiếp tục với Hooks để học chi tiết về ft.use_state() và các hooks khác.

Last updated on