Skip to main content
Flet provides a comprehensive theming system based on Material Design 3 and Cupertino (iOS-style) design. This page explains how to customize your app’s appearance.

Theme Basics

Flet supports three theme modes:
import flet as ft

def main(page: ft.Page):
    # Set theme mode
    page.theme_mode = ft.ThemeMode.LIGHT   # Light mode
    # page.theme_mode = ft.ThemeMode.DARK  # Dark mode
    # page.theme_mode = ft.ThemeMode.SYSTEM # Follow system (default)
    
    page.add(ft.Text("Hello, themed world!"))

ft.run(main)

Theme Modes

import flet as ft

def main(page: ft.Page):
page.theme_mode = ft.ThemeMode.LIGHT

page.add(
    ft.Container(
        content=ft.Text("Light theme"),
        bgcolor=ft.Colors.SURFACE,
        padding=20,
    )
)

Color Schemes

Seed Colors

Generate a complete theme from a single seed color:
import flet as ft

def main(page: ft.Page):
    # Generate theme from seed color
    page.theme = ft.Theme(
        color_scheme_seed=ft.Colors.GREEN,
    )
    
    page.add(
        ft.FilledButton("Primary Color Button"),
        ft.OutlinedButton("Secondary Button"),
        ft.Text("Theme generated from green seed color"),
    )

ft.run(main)

Custom Color Scheme

Define specific colors for your theme:
import flet as ft

def main(page: ft.Page):
    # Custom light theme
    page.theme = ft.Theme(
        color_scheme=ft.ColorScheme(
            primary=ft.Colors.BLUE,
            on_primary=ft.Colors.WHITE,
            secondary=ft.Colors.AMBER,
            on_secondary=ft.Colors.BLACK,
            surface=ft.Colors.GREY_100,
            on_surface=ft.Colors.BLACK,
            error=ft.Colors.RED,
            on_error=ft.Colors.WHITE,
        )
    )
    
    # Custom dark theme
    page.dark_theme = ft.Theme(
        color_scheme=ft.ColorScheme(
            primary=ft.Colors.BLUE_300,
            on_primary=ft.Colors.BLACK,
            secondary=ft.Colors.AMBER_300,
            on_secondary=ft.Colors.BLACK,
            surface=ft.Colors.GREY_900,
            on_surface=ft.Colors.WHITE,
            error=ft.Colors.RED_300,
            on_error=ft.Colors.BLACK,
        )
    )
    
    page.add(
        ft.FilledButton("Primary Button"),
        ft.Container(
            content=ft.Text("Surface Container"),
            bgcolor=ft.Colors.SURFACE,
            padding=20,
        ),
    )

ft.run(main)

Color Scheme Properties

Material Design 3 provides extensive color roles:
import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(
        color_scheme=ft.ColorScheme(
            # Primary colors
            primary=ft.Colors.BLUE,
            on_primary=ft.Colors.WHITE,
            primary_container=ft.Colors.BLUE_100,
            on_primary_container=ft.Colors.BLUE_900,
            
            # Secondary colors
            secondary=ft.Colors.GREEN,
            on_secondary=ft.Colors.WHITE,
            secondary_container=ft.Colors.GREEN_100,
            on_secondary_container=ft.Colors.GREEN_900,
            
            # Tertiary colors
            tertiary=ft.Colors.ORANGE,
            on_tertiary=ft.Colors.WHITE,
            tertiary_container=ft.Colors.ORANGE_100,
            on_tertiary_container=ft.Colors.ORANGE_900,
            
            # Error colors
            error=ft.Colors.RED,
            on_error=ft.Colors.WHITE,
            error_container=ft.Colors.RED_100,
            on_error_container=ft.Colors.RED_900,
            
            # Surface colors
            surface=ft.Colors.GREY_50,
            on_surface=ft.Colors.BLACK,
            surface_variant=ft.Colors.GREY_100,
            on_surface_variant=ft.Colors.GREY_700,
            
            # Utility colors
            outline=ft.Colors.GREY_400,
            outline_variant=ft.Colors.GREY_200,
            shadow=ft.Colors.BLACK,
            scrim=ft.Colors.BLACK,
            inverse_surface=ft.Colors.GREY_800,
            on_inverse_surface=ft.Colors.WHITE,
            inverse_primary=ft.Colors.BLUE_200,
        )
    )

ft.run(main)

Using Theme Colors

Reference theme colors in your controls:
import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(color_scheme_seed=ft.Colors.PURPLE)
    
    page.add(
        # Use theme colors
        ft.Container(
            content=ft.Text("Primary Surface"),
            bgcolor=ft.Colors.PRIMARY_CONTAINER,
            padding=20,
        ),
        ft.Container(
            content=ft.Text("Secondary Surface"),
            bgcolor=ft.Colors.SECONDARY_CONTAINER,
            padding=20,
        ),
        ft.Container(
            content=ft.Text("Surface"),
            bgcolor=ft.Colors.SURFACE,
            padding=20,
        ),
    )

ft.run(main)

Nested Themes

Apply different themes to specific parts of your app:
import flet as ft

def main(page: ft.Page):
    # Page theme (yellow)
    page.theme = ft.Theme(
        color_scheme_seed=ft.Colors.YELLOW,
    )
    
    page.add(
        # Inherits page theme
        ft.Container(
            content=ft.Button("Page theme button"),
            bgcolor=ft.Colors.SURFACE_TINT,
            padding=20,
            width=300,
        ),
        
        # Override with pink theme
        ft.Container(
            theme=ft.Theme(
                color_scheme=ft.ColorScheme(primary=ft.Colors.PINK)
            ),
            content=ft.Button("Pink theme button"),
            bgcolor=ft.Colors.SURFACE_TINT,
            padding=20,
            width=300,
        ),
        
        # Unique dark theme (always dark)
        ft.Container(
            theme=ft.Theme(color_scheme_seed=ft.Colors.INDIGO),
            theme_mode=ft.ThemeMode.DARK,
            content=ft.Button("Dark theme button"),
            bgcolor=ft.Colors.SURFACE_TINT,
            padding=20,
            width=300,
        ),
    )

ft.run(main)

Text Themes

Customize typography across your app:
import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(
        text_theme=ft.TextTheme(
            # Display styles (largest)
            display_large=ft.TextStyle(size=57, weight=ft.FontWeight.NORMAL),
            display_medium=ft.TextStyle(size=45, weight=ft.FontWeight.NORMAL),
            display_small=ft.TextStyle(size=36, weight=ft.FontWeight.NORMAL),
            
            # Headline styles
            headline_large=ft.TextStyle(size=32, weight=ft.FontWeight.NORMAL),
            headline_medium=ft.TextStyle(size=28, weight=ft.FontWeight.NORMAL),
            headline_small=ft.TextStyle(size=24, weight=ft.FontWeight.NORMAL),
            
            # Title styles
            title_large=ft.TextStyle(size=22, weight=ft.FontWeight.W500),
            title_medium=ft.TextStyle(size=16, weight=ft.FontWeight.W500),
            title_small=ft.TextStyle(size=14, weight=ft.FontWeight.W500),
            
            # Body styles
            body_large=ft.TextStyle(size=16, weight=ft.FontWeight.NORMAL),
            body_medium=ft.TextStyle(size=14, weight=ft.FontWeight.NORMAL),
            body_small=ft.TextStyle(size=12, weight=ft.FontWeight.NORMAL),
            
            # Label styles
            label_large=ft.TextStyle(size=14, weight=ft.FontWeight.W500),
            label_medium=ft.TextStyle(size=12, weight=ft.FontWeight.W500),
            label_small=ft.TextStyle(size=11, weight=ft.FontWeight.W500),
        )
    )
    
    page.add(
        ft.Text("Display Large", theme_style=ft.TextThemeStyle.DISPLAY_LARGE),
        ft.Text("Headline Medium", theme_style=ft.TextThemeStyle.HEADLINE_MEDIUM),
        ft.Text("Body Large", theme_style=ft.TextThemeStyle.BODY_LARGE),
        ft.Text("Label Small", theme_style=ft.TextThemeStyle.LABEL_SMALL),
    )

ft.run(main)

Custom Fonts

Load and use custom fonts:
import flet as ft

def main(page: ft.Page):
    # Register custom fonts
    page.fonts = {
        "Roboto": "https://github.com/google/fonts/raw/main/apache/roboto/static/Roboto-Regular.ttf",
        "Pacifico": "https://github.com/google/fonts/raw/main/ofl/pacifico/Pacifico-Regular.ttf",
        "CustomFont": "/fonts/custom-font.ttf",  # Local asset
    }
    
    page.add(
        ft.Text("Default font"),
        ft.Text("Roboto font", font_family="Roboto"),
        ft.Text("Pacifico font", font_family="Pacifico", size=30),
    )

ft.run(main)

Component Themes

Customize specific component styles:
import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(
        # Button theme
        elevated_button_theme=ft.ButtonThemeData(
            style=ft.ButtonStyle(
                shape=ft.RoundedRectangleBorder(radius=10),
                padding=20,
                bgcolor={ft.ControlState.DEFAULT: ft.Colors.BLUE},
            )
        ),
        
        # TextField theme
        input_decoration_theme=ft.InputDecorationTheme(
            border=ft.OutlineInputBorder(
                border_radius=8,
                border_side=ft.BorderSide(width=2, color=ft.Colors.BLUE),
            ),
            focused_border=ft.OutlineInputBorder(
                border_radius=8,
                border_side=ft.BorderSide(width=2, color=ft.Colors.BLUE_700),
            ),
        ),
        
        # AppBar theme
        app_bar_theme=ft.AppBarTheme(
            bgcolor=ft.Colors.BLUE,
            color=ft.Colors.WHITE,
        ),
    )
    
    page.add(
        ft.ElevatedButton("Themed Button"),
        ft.TextField(label="Themed Input"),
    )

ft.run(main)

Page Transitions

Customize page transition animations:
import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(
        page_transitions=ft.PageTransitionsTheme(
            android=ft.PageTransitionTheme.FADE_UPWARDS,
            ios=ft.PageTransitionTheme.CUPERTINO,
            linux=ft.PageTransitionTheme.ZOOM,
            macos=ft.PageTransitionTheme.ZOOM,
            windows=ft.PageTransitionTheme.ZOOM,
        )
    )

ft.run(main)

Material vs Cupertino

Use adaptive controls that automatically switch between Material and Cupertino:
import flet as ft

def main(page: ft.Page):
    # Adaptive controls automatically use:
    # - Material design on Android/Windows/Linux/Web
    # - Cupertino design on iOS/macOS
    
    page.add(
        ft.AdaptiveButton(
            text="Adaptive Button",
            # Material on Android, Cupertino on iOS
        ),
        ft.AdaptiveTextField(
            label="Adaptive Input",
        ),
    )
    
    # Force Cupertino style
    page.add(
        ft.CupertinoButton(
            content=ft.Text("Cupertino Button"),
        ),
        ft.CupertinoTextField(
            placeholder="Cupertino Input",
        ),
    )

ft.run(main)

Dynamic Theme Switching

Allow users to toggle theme at runtime:
import flet as ft

def main(page: ft.Page):
    def toggle_theme(e):
        # Toggle between light and dark
        page.theme_mode = (
            ft.ThemeMode.DARK
            if page.theme_mode == ft.ThemeMode.LIGHT
            else ft.ThemeMode.LIGHT
        )
        page.update()
    
    def change_seed_color(e):
        # Change theme seed color
        colors = [ft.Colors.BLUE, ft.Colors.GREEN, ft.Colors.RED, ft.Colors.PURPLE]
        import random
        page.theme = ft.Theme(color_scheme_seed=random.choice(colors))
        page.update()
    
    page.add(
        ft.Switch(
            label="Dark mode",
            value=page.theme_mode == ft.ThemeMode.DARK,
            on_change=toggle_theme,
        ),
        ft.ElevatedButton("Random Color", on_click=change_seed_color),
    )

ft.run(main)

Visual Density

Adjust spacing and sizing:
import flet as ft

def main(page: ft.Page):
    page.theme = ft.Theme(
        visual_density=ft.VisualDensity.COMPACT,  # More compact
        # visual_density=ft.VisualDensity.COMFORTABLE,  # Default
        # visual_density=ft.VisualDensity.STANDARD,  # More spacious
    )
    
    page.add(
        ft.FilledButton("Button 1"),
        ft.FilledButton("Button 2"),
        ft.FilledButton("Button 3"),
    )

ft.run(main)

Best Practices

Common pitfalls to avoid:
  1. Hardcoding colors instead of using theme colors
  2. Not providing both light and dark theme variants
  3. Using inconsistent color schemes across the app
  4. Ignoring platform conventions (Material vs Cupertino)

Use Theme Colors

import flet as ft

def main(page: ft.Page):
    # Good: Use theme colors
    page.add(
        ft.Container(
            content=ft.Text("Themed"),
            bgcolor=ft.Colors.PRIMARY,  # Adapts to theme
        )
    )
    
    # Avoid: Hardcoded colors
    page.add(
        ft.Container(
            content=ft.Text("Hardcoded"),
            bgcolor="#0000FF",  # Won't adapt to theme changes
        )
    )

Provide Dark Mode

import flet as ft

def main(page: ft.Page):
    # Always provide both light and dark themes
    page.theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
    page.dark_theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
    page.theme_mode = ft.ThemeMode.SYSTEM  # Respect user preference

Test Both Modes

import flet as ft

def main(page: ft.Page):
    # Add a theme switcher for testing
    def toggle_theme(e):
        page.theme_mode = (
            ft.ThemeMode.DARK
            if page.theme_mode == ft.ThemeMode.LIGHT
            else ft.ThemeMode.LIGHT
        )
        page.update()
    
    page.add(
        ft.IconButton(
            icon=ft.Icons.BRIGHTNESS_6,
            on_click=toggle_theme,
        )
    )

Next Steps

Architecture

Understand Flet’s client-server architecture

Page and Controls

Learn about the control tree and Page object

Build docs developers (and LLMs) love