Skip to Content
Flet🎨 Theme Switcher

Mini Project: Theme Switcher 🎨

Xây dựng ứng dụng demo chuyển đổi Light/Dark theme. ⏱️ 20 phút

🎯 Mục tiêu

Tạo ứng dụng với:

  • Toggle Light/Dark mode
  • Custom color scheme
  • Preview các UI components với theme

🚀 Bắt đầu

Bước 1: Theme Mode cơ bản

import flet as ft def main(page: ft.Page): page.title = "Theme Switcher" page.theme_mode = ft.ThemeMode.LIGHT def toggle_theme(_): if page.theme_mode == ft.ThemeMode.LIGHT: page.theme_mode = ft.ThemeMode.DARK else: page.theme_mode = ft.ThemeMode.LIGHT page.update() page.add( ft.Switch(label="Dark Mode", on_change=toggle_theme), ft.Text("Hello World!"), ) ft.run(main)

Bước 2: Thêm các UI Components

import flet as ft def main(page: ft.Page): page.title = "Theme Switcher" page.theme_mode = ft.ThemeMode.LIGHT page.padding = 20 def toggle_theme(e): page.theme_mode = ft.ThemeMode.DARK if e.control.value else ft.ThemeMode.LIGHT page.update() page.add( ft.Row([ ft.Text("Theme Switcher", size=24, weight=ft.FontWeight.BOLD), ft.Container(expand=True), ft.Switch(label="Dark Mode", on_change=toggle_theme), ]), ft.Divider(), ft.Text("Buttons", weight=ft.FontWeight.BOLD), ft.Row([ ft.ElevatedButton("Elevated"), ft.FilledButton("Filled"), ft.OutlinedButton("Outlined"), ft.TextButton("Text"), ], wrap=True), ft.Text("Inputs", weight=ft.FontWeight.BOLD), ft.TextField(label="Text Field"), ft.Checkbox(label="Checkbox"), ft.Switch(label="Switch"), ft.Slider(value=50), ) ft.run(main)

Bước 3: Custom Color Scheme

import flet as ft # Custom themes LIGHT_THEME = ft.Theme( color_scheme=ft.ColorScheme( primary=ft.Colors.BLUE, secondary=ft.Colors.ORANGE, surface=ft.Colors.WHITE, background=ft.Colors.GREY_100, ), ) DARK_THEME = ft.Theme( color_scheme=ft.ColorScheme( primary=ft.Colors.CYAN, secondary=ft.Colors.AMBER, surface=ft.Colors.GREY_900, background=ft.Colors.BLACK, ), ) def main(page: ft.Page): page.title = "Theme Switcher" page.theme = LIGHT_THEME page.dark_theme = DARK_THEME page.theme_mode = ft.ThemeMode.LIGHT page.padding = 20 def toggle_theme(e): page.theme_mode = ft.ThemeMode.DARK if e.control.value else ft.ThemeMode.LIGHT page.update() page.add( ft.Switch(label="Dark Mode", on_change=toggle_theme), ) ft.run(main)

Bước 4: Code hoàn chỉnh với Preview

import flet as ft @ft.observable class ThemeState: is_dark: bool = False primary_color: str = "blue" colors = { "blue": ft.Colors.BLUE, "green": ft.Colors.GREEN, "purple": ft.Colors.PURPLE, "orange": ft.Colors.ORANGE, "red": ft.Colors.RED, } def toggle_mode(self): self.is_dark = not self.is_dark def set_color(self, color: str): self.primary_color = color def get_primary(self): return self.colors.get(self.primary_color, ft.Colors.BLUE) state = ThemeState() @ft.component def ColorPicker(): colors = ["blue", "green", "purple", "orange", "red"] color_map = { "blue": ft.Colors.BLUE, "green": ft.Colors.GREEN, "purple": ft.Colors.PURPLE, "orange": ft.Colors.ORANGE, "red": ft.Colors.RED, } return ft.Row([ ft.Container( width=40, height=40, bgcolor=color_map[c], border_radius=20, border=ft.border.all(3, ft.Colors.WHITE) if state.primary_color == c else None, shadow=ft.BoxShadow(blur_radius=5, color=ft.Colors.BLACK26) if state.primary_color == c else None, on_click=lambda _, color=c: state.set_color(color), ) for c in colors ], spacing=10) @ft.component def ComponentsPreview(): return ft.Column([ ft.Text("Preview Components", size=20, weight=ft.FontWeight.BOLD), ft.Divider(height=20), # Buttons ft.Text("Buttons", weight=ft.FontWeight.W_500), ft.Row([ ft.ElevatedButton("Elevated"), ft.FilledButton("Filled"), ft.OutlinedButton("Outlined"), ], wrap=True), # Form Elements ft.Text("Form Elements", weight=ft.FontWeight.W_500), ft.TextField(label="Username", prefix_icon=ft.Icons.PERSON), ft.TextField(label="Password", password=True, prefix_icon=ft.Icons.LOCK), ft.Row([ ft.Checkbox(label="Remember me", value=True), ft.Switch(label="Notifications"), ]), # Slider ft.Text("Slider", weight=ft.FontWeight.W_500), ft.Slider(value=60, min=0, max=100), # Progress ft.Text("Progress", weight=ft.FontWeight.W_500), ft.ProgressBar(value=0.7), # Card ft.Text("Card", weight=ft.FontWeight.W_500), ft.Card( content=ft.Container( content=ft.Column([ ft.ListTile( leading=ft.Icon(ft.Icons.PERSON), title=ft.Text("John Doe"), subtitle=ft.Text("[email protected]"), ), ft.Row([ ft.TextButton("EDIT"), ft.TextButton("DELETE"), ], alignment=ft.MainAxisAlignment.END), ]), padding=10, ), ), ], spacing=15, scroll=ft.ScrollMode.AUTO) @ft.component def ThemeSettings(): return ft.Card( content=ft.Container( content=ft.Column([ ft.Text("Theme Settings", size=20, weight=ft.FontWeight.BOLD), ft.Divider(), # Mode toggle ft.Row([ ft.Icon(ft.Icons.LIGHT_MODE if not state.is_dark else ft.Icons.DARK_MODE), ft.Text("Dark Mode"), ft.Container(expand=True), ft.Switch( value=state.is_dark, on_change=lambda _: state.toggle_mode(), ), ]), # Color picker ft.Text("Primary Color", weight=ft.FontWeight.W_500), ColorPicker(), ]), padding=20, ), ) @ft.component def ThemeSwitcherApp(): return ft.Row([ ft.Container( content=ThemeSettings(), width=300, ), ft.VerticalDivider(), ft.Container( content=ComponentsPreview(), expand=True, padding=20, ), ], expand=True) def main(page: ft.Page): page.title = "Theme Switcher" page.padding = 20 # Apply dynamic theme def update_theme(): page.theme = ft.Theme( color_scheme=ft.ColorScheme(primary=state.get_primary()) ) page.dark_theme = ft.Theme( color_scheme=ft.ColorScheme(primary=state.get_primary()) ) page.theme_mode = ft.ThemeMode.DARK if state.is_dark else ft.ThemeMode.LIGHT page.update() # Watch for state changes page.add(ThemeSwitcherApp()) # Simple approach - rebuild on theme change def on_state_change(): update_theme() update_theme() ft.run(main)

✅ Code đơn giản hơn (Imperative)

import flet as ft def main(page: ft.Page): page.title = "Theme Switcher" page.padding = 30 # Theme colors colors = [ft.Colors.BLUE, ft.Colors.GREEN, ft.Colors.PURPLE, ft.Colors.ORANGE] def toggle_theme(e): page.theme_mode = ft.ThemeMode.DARK if e.control.value else ft.ThemeMode.LIGHT page.update() def change_color(color): def handler(_): page.theme = ft.Theme(color_scheme=ft.ColorScheme(primary=color)) page.dark_theme = ft.Theme(color_scheme=ft.ColorScheme(primary=color)) page.update() return handler page.add( ft.Text("🎨 Theme Switcher", size=28, weight=ft.FontWeight.BOLD), ft.Divider(), # Dark mode toggle ft.Switch(label="Dark Mode", on_change=toggle_theme), # Color picker ft.Text("Primary Color:", weight=ft.FontWeight.W_500), ft.Row([ ft.Container( width=50, height=50, bgcolor=c, border_radius=25, on_click=change_color(c), ) for c in colors ], spacing=10), ft.Divider(), # Preview ft.Text("Preview:", weight=ft.FontWeight.W_500), ft.ElevatedButton("Elevated Button"), ft.FilledButton("Filled Button"), ft.TextField(label="Sample Input"), ft.Slider(value=50), ft.ProgressBar(value=0.6), ) ft.run(main)

🎯 Thử thách mở rộng

  1. Save preference - Lưu theme vào client storage
  2. System theme - Theo theme của hệ điều hành
  3. Custom fonts - Đổi font family
  4. More colors - Color picker đầy đủ hơn

📝 Kiến thức ôn tập

  • page.theme_mode - Light/Dark mode
  • page.themepage.dark_theme - Custom themes
  • ft.ColorScheme - Bảng màu
  • Dynamic UI updates

⏭️ Tiếp theo

Tuyệt! Tiếp tục với Navigation & Routing để học điều hướng.

Last updated on