Bỏ qua để đến nội dung

✅ Todo List

Xây dựng ứng dụng Todo List đầy đủ chức năng. ⏱️ 30 phút

Tạo ứng dụng Todo với các tính năng:

  • Thêm task mới
  • Đánh dấu hoàn thành/chưa hoàn thành
  • Xóa task
  • Đếm số task còn lại
  • Lọc theo trạng thái

Tạo file todo_app.py:

import flet as ft
@ft.observable
class TodoState:
items: list = []
def add(self, text: str):
if text:
self.items.append({
"id": len(self.items),
"text": text,
"done": False
})
state = TodoState()
def main(page: ft.Page):
page.title = "Todo App"
page.add(ft.Text("Todo App", size=24))
ft.run(main)
import flet as ft
@ft.observable
class TodoState:
items: list = []
def add(self, text: str):
if text.strip():
self.items.append({
"id": len(self.items),
"text": text.strip(),
"done": False
})
state = TodoState()
@ft.component
def AddTodoForm():
input_value, set_input = ft.use_state("")
def add_todo(_):
state.add(input_value)
set_input("")
return ft.Row([
ft.TextField(
value=input_value,
hint_text="Nhập việc cần làm...",
expand=True,
on_change=lambda e: set_input(e.control.value),
on_submit=add_todo,
),
ft.FilledButton("Thêm", icon=ft.Icons.ADD, on_click=add_todo),
])
def main(page: ft.Page):
page.title = "Todo App"
page.padding = 20
page.add(
ft.Text("📝 Todo List", size=28, weight=ft.FontWeight.BOLD),
AddTodoForm(),
)
ft.run(main)
import flet as ft
@ft.observable
class TodoState:
items: list = []
def add(self, text: str):
if text.strip():
self.items.append({
"id": len(self.items),
"text": text.strip(),
"done": False
})
def toggle(self, id: int):
for item in self.items:
if item["id"] == id:
item["done"] = not item["done"]
break
def delete(self, id: int):
self.items = [item for item in self.items if item["id"] != id]
state = TodoState()
@ft.component
def AddTodoForm():
input_value, set_input = ft.use_state("")
def add_todo(_):
state.add(input_value)
set_input("")
return ft.Row([
ft.TextField(
value=input_value,
hint_text="Nhập việc cần làm...",
expand=True,
on_change=lambda e: set_input(e.control.value),
on_submit=add_todo,
),
ft.FilledButton("Thêm", icon=ft.Icons.ADD, on_click=add_todo),
])
@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,
color=ft.Colors.GREY_500 if item["done"] else None,
),
),
trailing=ft.IconButton(
ft.Icons.DELETE,
icon_color=ft.Colors.RED_300,
on_click=lambda _: state.delete(item["id"]),
),
)
@ft.component
def TodoList():
if not state.items:
return ft.Container(
content=ft.Text("✨ Chưa có việc nào. Thêm task đi!", color=ft.Colors.GREY_500),
padding=40,
alignment=ft.alignment.center,
)
return ft.Column([
TodoItem(item) for item in state.items
])
def main(page: ft.Page):
page.title = "Todo App"
page.padding = 20
page.add(
ft.Text("📝 Todo List", size=28, weight=ft.FontWeight.BOLD),
AddTodoForm(),
ft.Divider(),
TodoList(),
)
ft.run(main)
import flet as ft
@ft.observable
class TodoState:
items: list = []
filter: str = "all" # all, active, completed
def add(self, text: str):
if text.strip():
self.items.append({
"id": len(self.items),
"text": text.strip(),
"done": False
})
def toggle(self, id: int):
for item in self.items:
if item["id"] == id:
item["done"] = not item["done"]
break
def delete(self, id: int):
self.items = [item for item in self.items if item["id"] != id]
def set_filter(self, f: str):
self.filter = f
def get_filtered_items(self):
if self.filter == "active":
return [i for i in self.items if not i["done"]]
elif self.filter == "completed":
return [i for i in self.items if i["done"]]
return self.items
def get_remaining_count(self):
return len([i for i in self.items if not i["done"]])
state = TodoState()
@ft.component
def AddTodoForm():
input_value, set_input = ft.use_state("")
def add_todo(_):
state.add(input_value)
set_input("")
return ft.Row([
ft.TextField(
value=input_value,
hint_text="Nhập việc cần làm...",
expand=True,
on_change=lambda e: set_input(e.control.value),
on_submit=add_todo,
prefix_icon=ft.Icons.ADD_TASK,
),
ft.FilledButton("Thêm", icon=ft.Icons.ADD, on_click=add_todo),
])
@ft.component
def FilterTabs():
return ft.Row([
ft.Text(f"{state.get_remaining_count()} việc còn lại",
color=ft.Colors.GREY_600, size=14),
ft.Container(expand=True),
ft.SegmentedButton(
selected={state.filter},
on_change=lambda e: state.set_filter(list(e.control.selected)[0]),
segments=[
ft.Segment(value="all", label=ft.Text("Tất cả")),
ft.Segment(value="active", label=ft.Text("Đang làm")),
ft.Segment(value="completed", label=ft.Text("Xong")),
],
),
])
@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,
color=ft.Colors.GREY_500 if item["done"] else None,
),
),
trailing=ft.IconButton(
ft.Icons.DELETE_OUTLINE,
icon_color=ft.Colors.RED_300,
tooltip="Xóa",
on_click=lambda _: state.delete(item["id"]),
),
)
@ft.component
def TodoList():
filtered = state.get_filtered_items()
if not filtered:
msg = "✨ Không có việc nào" if state.filter != "all" else "✨ Thêm việc cần làm!"
return ft.Container(
content=ft.Text(msg, color=ft.Colors.GREY_500, size=16),
padding=40,
alignment=ft.alignment.center,
)
return ft.Column([TodoItem(item) for item in filtered])
@ft.component
def TodoApp():
return ft.Container(
content=ft.Column([
ft.Text("📝 Todo List", size=28, weight=ft.FontWeight.BOLD),
AddTodoForm(),
ft.Container(height=10),
FilterTabs(),
ft.Divider(),
TodoList(),
]),
padding=20,
width=500,
)
def main(page: ft.Page):
page.title = "Todo App"
page.vertical_alignment = ft.MainAxisAlignment.START
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.bgcolor = ft.Colors.GREY_100
page.add(
ft.Container(
content=TodoApp(),
bgcolor=ft.Colors.WHITE,
border_radius=10,
shadow=ft.BoxShadow(blur_radius=10, color=ft.Colors.BLACK12),
margin=ft.margin.only(top=40),
)
)
ft.run(main)

Ứng dụng Todo hoàn chỉnh với:

  • ✅ Thêm task mới
  • ✅ Toggle hoàn thành
  • ✅ Xóa task
  • ✅ Đếm task còn lại
  • ✅ Lọc theo trạng thái
  • ✅ UI đẹp với Card và shadows
  1. Edit task - Cho phép sửa nội dung task
  2. Due date - Thêm ngày hết hạn
  3. Priority - Ưu tiên cao/trung bình/thấp
  4. Persistence - Lưu vào local storage
  • @ft.observable - State toàn app
  • @ft.component - Components tái sử dụng
  • State methods - add, toggle, delete, filter
  • Conditional rendering - Hiển thị theo điều kiện

Tuyệt! Tiếp tục với Mini Project: Calculator.