Ultra Guide: Common Flet Mistakes & Solutions

Welcome to the ultimate guide for Flet developers! Whether you're just starting out or have been working with Flet for a while, you've probably encountered some frustrating issues. In this comprehensive guide, we'll explore 20+ common mistakes developers make when working with Flet, explain why they happen, and provide practical solutions with working code examples. Let's dive in and level up your Flet skills!

1. Forgetting to Call page.update() After State Changes

One of the most frequent beginner mistakes is modifying UI elements without updating the page. Flet won't automatically reflect changes to your controls unless you explicitly call page.update(). This leads to confusing situations where your code runs without errors, but the UI remains unchanged.

Always remember that Flet uses an imperative approach - you need to tell the framework when to refresh the display after making changes to your controls.

import flet as ft

def main(page: ft.Page):
    counter = ft.Text("0")
    
    def increment(e):
        # This won't update the UI!
        counter.value = str(int(counter.value) + 1)
        # You MUST call this to see changes
        page.update()
    
    page.add(
        counter,
        ft.ElevatedButton("Increment", on_click=increment)
    )

ft.app(target=main)
Pro Tip: Consider using page.add() for initial controls and page.update() for subsequent changes to existing controls.

2. Misunderstanding Control References vs. Control Instances

Many developers get confused between storing a reference to a control versus creating a new instance. When you store a control in a variable, you're keeping a reference to that specific instance. Creating a new instance each time will result in unexpected behavior since you're not modifying the control that's actually displayed on the page.

This mistake often leads to "ghost controls" that exist in your code but aren't connected to the actual UI elements users interact with.

import flet as ft

def main(page: ft.Page):
    # CORRECT: Store reference to the actual control
    my_text = ft.Text("Original")
    page.add(my_text)
    
    def change_text(e):
        my_text.value = "Changed!"  # Modifies the displayed control
        page.update()
    
    # WRONG: Creating new instance
    # def change_text_wrong(e):
    #     new_text = ft.Text("Changed!")  # New instance, not connected to UI
    #     page.update()  # Won't show anything
    
    page.add(ft.ElevatedButton("Change Text", on_click=change_text))

ft.app(target=main)

3. Blocking the Main Thread with Long-Running Operations

Flet applications run in a single thread by default. If you perform long-running operations (like file processing, network requests, or complex calculations) directly in event handlers, your entire UI will freeze until the operation completes. This creates a terrible user experience where the application appears unresponsive.

The solution is to use Python's asyncio for asynchronous operations or run blocking code in separate threads using concurrent.futures.

import flet as ft
import asyncio
import time

def main(page: ft.Page):
    status = ft.Text("Ready")
    page.add(status)
    
    # BAD: This will freeze the UI for 5 seconds
    # def bad_handler(e):
    #     time.sleep(5)  # UI freezes!
    #     status.value = "Done!"
    #     page.update()
    
    # GOOD: Using async/await
    async def good_handler(e):
        status.value = "Working..."
        page.update()
        await asyncio.sleep(5)  # Non-blocking wait
        status.value = "Done!"
        page.update()
    
    page.add(ft.ElevatedButton("Process", on_click=good_handler))

ft.app(target=main)

4. Incorrect Event Handler Signatures

Flet event handlers must accept exactly one parameter (conventionally named e or event) that represents the event object. Providing too few or too many parameters will cause runtime errors when the event is triggered. Additionally, forgetting to make async event handlers async when using await inside them will cause syntax errors.

Remember that even if you don't use the event object, you still need to include it in your function signature.

import flet as ft

def main(page: ft.Page):
    # CORRECT signatures
    def sync_handler(e):  # Must have 'e' parameter
        print("Button clicked!")
    
    async def async_handler(e):  # Async handler with 'e' parameter
        await asyncio.sleep(1)
        print("Async operation complete!")
    
    # INCORRECT examples:
    # def wrong_handler():  # Missing 'e' parameter - will crash!
    #     pass
    
    # async def wrong_async_handler():  # Missing 'e' parameter
    #     pass
    
    page.add(
        ft.ElevatedButton("Sync", on_click=sync_handler),
        ft.ElevatedButton("Async", on_click=async_handler)
    )

ft.app(target=main)

5. Not Handling Page Resize Events Properly

Responsive design is crucial for modern applications, but many Flet developers forget to handle window resize events. Without proper resize handling, your layout might break or look awkward on different screen sizes. Flet provides the on_resize event for the page, but you need to implement logic to adjust your controls accordingly.

Consider using responsive containers like ResponsiveRow or calculating dimensions dynamically based on page.window_width and page.window_height.

import flet as ft

def main(page: ft.Page):
    status = ft.Text(f"Size: {page.window_width}x{page.window_height}")
    
    def handle_resize(e):
        status.value = f"Size: {page.window_width}x{page.window_height}"
        # Adjust layout based on new dimensions
        if page.window_width < 600:
            col.controls[0].width = page.window_width - 40
        else:
            col.controls[0].width = 500
        page.update()
    
    page.on_resize = handle_resize
    
    col = ft.Column([
        ft.Container(
            content=ft.Text("Responsive Content"),
            width=500,
            bgcolor=ft.colors.BLUE_100,
            padding=20
        )
    ])
    
    page.add(status, col)

ft.app(target=main)

6. Memory Leaks from Unclosed Event Listeners

When you add event listeners to controls that are later removed from the page, those listeners might continue to exist in memory, causing memory leaks. This is especially problematic in long-running applications or when frequently adding/removing controls dynamically.

Always clean up event listeners when controls are removed, or use weak references when possible. Flet doesn't automatically clean up event handlers when controls are removed from the page tree.

import flet as ft

def main(page: ft.Page):
    dynamic_controls = []
    
    def create_control():
        text = ft.Text("Dynamic Item")
        button = ft.ElevatedButton("Remove", on_click=lambda e: remove_control(text, button))
        container = ft.Row([text, button])
        dynamic_controls.append((text, button, container))
        return container
    
    def remove_control(text, button):
        # Remove from page
        for item in dynamic_controls:
            if item[0] == text and item[1] == button:
                page.controls.remove(item[2])
                # Important: Clear references to prevent memory leaks
                dynamic_controls.remove(item)
                break
        page.update()
    
    page.add(
        ft.ElevatedButton("Add Item", on_click=lambda e: page.add(create_control())),
        ft.Divider()
    )

ft.app(target=main)

7. Improper Use of Container Padding and Margin

Confusing padding and margin is a common layout mistake. Padding adds space inside a container (between the container's border and its content), while margin adds space outside the container (between the container and other elements). Using them incorrectly leads to unexpected spacing and alignment issues.

Remember: padding affects the container's internal space, margin affects external spacing. Also, Flet containers don't have a direct margin property - you need to use spacing in parent containers or wrap in another container with padding.

import flet as ft

def main(page: ft.Page):
    # CORRECT usage
    page.add(
        ft.Container(
            content=ft.Text("Inside Padding"),
            padding=20,  # Space inside container
            bgcolor=ft.colors.AMBER_100
        ),
        ft.Container(
            content=ft.Text("More Content"),
            padding=10,
            bgcolor=ft.colors.GREEN_100
        )
    )
    
    # To simulate margin, use Column spacing or wrap in another container
    page.spacing = 15  # Space between controls in page
    
    # Alternative: Wrap in container with padding for "margin" effect
    # ft.Container(
    #     content=ft.Text("With margin-like spacing"),
    #     padding=ft.padding.only(top=15),  # Top padding acts as margin
    #     bgcolor=ft.colors.RED_100
    # )

ft.app(target=main)

8. Not Using Keys for Dynamic Lists

When creating dynamic lists of controls (like chat messages, todo items, or data rows), failing to assign unique keys can cause serious UI issues. Flet uses keys to track which controls have changed, been added, or removed. Without keys, Flet might reuse controls incorrectly, leading to data being displayed in the wrong places or event handlers being attached to wrong items.

Always assign a unique, stable key to each item in a dynamic list. The key should remain the same for the same logical item across updates.

import flet as ft

def main(page: ft.Page):
    items = []
    next_id = 0
    
    def add_item(e):
        nonlocal next_id
        item_id = next_id
        next_id += 1
        
        # CORRECT: Assign unique key
        new_item = ft.ListTile(
            key=str(item_id),  # Unique key for this item
            title=ft.Text(f"Item {item_id}"),
            trailing=ft.IconButton(
                ft.icons.DELETE,
                on_click=lambda e, id=item_id: remove_item(id)
            )
        )
        items.append(new_item)
        update_list()
    
    def remove_item(item_id):
        global items
        items = [item for item in items if item.key != str(item_id)]
        update_list()
    
    def update_list():
        list_view.controls = items
        page.update()
    
    list_view = ft.ListView()
    page.add(
        ft.ElevatedButton("Add Item", on_click=add_item),
        list_view
    )

ft.app(target=main)

9. Hardcoding Styles Instead of Using Themes

Hardcoding colors, fonts, and spacing throughout your application makes it difficult to maintain consistency and update the design later. Flet provides a powerful theming system that allows you to define consistent styles across your entire application.

Use page.theme and page.dark_theme to define global styles, and leverage Flet's built-in color system instead of using raw hex values everywhere.

import flet as ft

def main(page: ft.Page):
    # Define theme once
    page.theme = ft.Theme(
        color_scheme=ft.ColorScheme(
            primary=ft.colors.BLUE,
            secondary=ft.colors.GREEN,
            background=ft.colors.GREY_100
        ),
        font_family="Verdana"
    )
    
    # Use theme colors consistently
    page.add(
        ft.ElevatedButton("Primary Action", style=ft.ButtonStyle(color=ft.colors.ON_PRIMARY)),
        ft.OutlinedButton("Secondary Action", style=ft.ButtonStyle(color=ft.colors.SECONDARY)),
        ft.Container(
            content=ft.Text("Themed Content"),
            bgcolor=ft.colors.BACKGROUND,
            padding=20
        )
    )
    
    # Instead of hardcoded colors like:
    # ft.ElevatedButton("Hardcoded", bgcolor="#2196F3")

ft.app(target=main)

10. Ignoring Error Handling in Event Handlers

Event handlers without proper error handling can cause your entire application to crash when unexpected situations occur. Network failures, invalid user input, file access issues, and other exceptions should be caught and handled gracefully to maintain application stability.

Always wrap potentially problematic code in try-except blocks and provide user feedback when errors occur. Never let unhandled exceptions bubble up from event handlers.

import flet as ft

def main(page: ft.Page):
    status = ft.Text("")
    page.add(status)
    
    def risky_operation(e):
        try:
            # Simulate operation that might fail
            result = 10 / 0  # This will raise ZeroDivisionError
            status.value = f"Result: {result}"
        except ZeroDivisionError:
            status.value = "Error: Cannot divide by zero!"
            status.color = ft.colors.RED
        except Exception as ex:
            status.value = f"Unexpected error: {str(ex)}"
            status.color = ft.colors.RED
        finally:
            page.update()
    
    page.add(ft.ElevatedButton("Perform Risky Operation", on_click=risky_operation))

ft.app(target=main)

11. Not Using ResponsiveRow for Complex Layouts

Trying to create responsive layouts with basic Columns and Rows often leads to complex, hard-to-maintain code with manual width calculations. Flet's ResponsiveRow control automatically handles responsive behavior by wrapping controls to the next line when there's insufficient horizontal space.

Use col property on controls within ResponsiveRow to specify how many columns they should occupy on different screen sizes.

import flet as ft

def main(page: ft.Page):
    # CORRECT: Using ResponsiveRow
    page.add(
        ft.ResponsiveRow(
            controls=[
                ft.Container(
                    ft.Text("Card 1"),
                    col={"sm": 6, "md": 4, "xl": 3},  # Responsive columns
                    bgcolor=ft.colors.BLUE_100,
                    padding=10
                ),
                ft.Container(
                    ft.Text("Card 2"),
                    col={"sm": 6, "md": 4, "xl": 3},
                    bgcolor=ft.colors.GREEN_100,
                    padding=10
                ),
                ft.Container(
                    ft.Text("Card 3"),
                    col={"sm": 6, "md": 4, "xl": 3},
                    bgcolor=ft.colors.AMBER_100,
                    padding=10
                ),
                ft.Container(
                    ft.Text("Card 4"),
                    col={"sm": 6, "md": 4, "xl": 3},
                    bgcolor=ft.colors.RED_100,
                    padding=10
                )
            ],
            spacing=10
        )
    )
    
    # Instead of complex manual calculations with Columns/Rows

ft.app(target=main)

12. Misusing page.add() vs. page.controls.append()

There's an important difference between page.add() and directly manipulating page.controls. The page.add() method automatically calls page.update() after adding controls, while modifying page.controls directly requires you to manually call page.update() to see changes.

For adding multiple controls at once, it's more efficient to modify page.controls and then call page.update() once, rather than calling page.add() multiple times.

import flet as ft

def main(page: ft.Page):
    # Method 1: Using page.add() (automatically updates)
    page.add(ft.Text("Added with page.add()"))
    
    # Method 2: Direct manipulation (requires manual update)
    page.controls.append(ft.Text("Added to controls list"))
    page.update()  # Must call this!
    
    # Efficient for multiple controls:
    new_controls = [
        ft.Text("Batch item 1"),
        ft.Text("Batch item 2"),
        ft.Text("Batch item 3")
    ]
    page.controls.extend(new_controls)
    page.update()  # Single update for all

ft.app(target=main)

13. Forgetting to Handle Page Lifecycle Events

Flet provides several page lifecycle events (on_connect, on_disconnect, on_error) that are crucial for proper resource management. Not handling these events can lead to memory leaks, unclosed connections, or inconsistent application state when users connect/disconnect.

Use these events to initialize resources when a user connects and clean up when they disconnect (especially important for web deployments with multiple users).

import flet as ft

def main(page: ft.Page):
    user_id = None
    
    def on_connect(e):
        nonlocal user_id
        user_id = id(e.page)
        print(f"User connected: {user_id}")
        # Initialize user-specific resources here
    
    def on_disconnect(e):
        print(f"User disconnected: {user_id}")
        # Clean up user-specific resources here
        # Close database connections, cancel timers, etc.
    
    page.on_connect = on_connect
    page.on_disconnect = on_disconnect
    
    page.add(ft.Text("Lifecycle-aware application"))

ft.app(target=main)

14. Not Using Proper State Management Patterns

As Flet applications grow in complexity, managing state across different parts of the UI becomes challenging. Storing state in local variables within the main function leads to spaghetti code that's hard to maintain and debug.

Consider implementing proper state management patterns like a centralized state class, observer pattern, or using Flet's built-in capabilities for more complex scenarios. This makes your code more predictable and easier to test.

import flet as ft

class AppState:
    def __init__(self):
        self.counter = 0
        self.observers = []
    
    def increment(self):
        self.counter += 1
        self.notify_observers()
    
    def add_observer(self, observer):
        self.observers.append(observer)
    
    def notify_observers(self):
        for observer in self.observers:
            observer()

def main(page: ft.Page):
    state = AppState()
    counter_text = ft.Text(str(state.counter))
    
    def update_counter():
        counter_text.value = str(state.counter)
        page.update()
    
    state.add_observer(update_counter)
    
    def handle_increment(e):
        state.increment()
    
    page.add(
        counter_text,
        ft.ElevatedButton("Increment", on_click=handle_increment)
    )

ft.app(target=main)

15. Overusing Global Variables

While global variables might seem convenient for sharing state between functions, they create tight coupling, make testing difficult, and can lead to unpredictable behavior in multi-user scenarios (especially with Flet's web deployment).

Instead of globals, pass necessary data as parameters, use class-based approaches, or implement proper dependency injection. This makes your code more modular, testable, and maintainable.

import flet as ft

# BAD: Global variable
# current_user = None

def main(page: ft.Page):
    # GOOD: Encapsulate state
    class Session:
        def __init__(self):
            self.current_user = None
    
    session = Session()
    
    def login(e):
        session.current_user = "john_doe"
        update_ui()
    
    def logout(e):
        session.current_user = None
        update_ui()
    
    def update_ui():
        if session.current_user:
            welcome.value = f"Welcome, {session.current_user}!"
            page.add(logout_btn)
            if login_btn in page.controls:
                page.controls.remove(login_btn)
        else:
            welcome.value = "Please log in"
            page.add(login_btn)
            if logout_btn in page.controls:
                page.controls.remove(logout_btn)
        page.update()
    
    welcome = ft.Text("Please log in")
    login_btn = ft.ElevatedButton("Login", on_click=login)
    logout_btn = ft.ElevatedButton("Logout", on_click=logout)
    
    page.add(welcome, login_btn)

ft.app(target=main)

16. Not Optimizing for Performance with Large Lists

Displaying large datasets (hundreds or thousands of items) by creating all controls at once can severely impact performance and memory usage. Flet provides LazyColumn and virtualized lists that only render visible items, dramatically improving performance.

For large datasets, always use virtualized scrolling controls instead of creating all items upfront. This keeps your application responsive even with massive amounts of data.

import flet as ft

def main(page: ft.Page):
    # BAD: Creating all items at once
    # items = [ft.ListTile(title=ft.Text(f"Item {i}")) for i in range(10000)]
    # page.add(ft.ListView(controls=items))
    
    # GOOD: Using virtualized list (pseudo-code - Flet doesn't have built-in yet)
    # In practice, you'd implement pagination or use third-party solutions
    # For demonstration, we'll show a paginated approach
    
    current_page = 0
    items_per_page = 50
    total_items = 10000
    
    def load_page(page_num):
        start = page_num * items_per_page
        end = min(start + items_per_page, total_items)
        return [ft.ListTile(title=ft.Text(f"Item {i}")) for i in range(start, end)]
    
    list_view = ft.ListView(height=400)
    pagination = ft.Row()
    
    def update_display():
        list_view.controls = load_page(current_page)
        pagination.controls = [
            ft.IconButton(
                ft.icons.CHEVRON_LEFT,
                on_click=lambda e: change_page(-1),
                disabled=current_page == 0
            ),
            ft.Text(f"Page {current_page + 1}"),
            ft.IconButton(
                ft.icons.CHEVRON_RIGHT,
                on_click=lambda e: change_page(1),
                disabled=(current_page + 1) * items_per_page >= total_items
            )
        ]
        page.update()
    
    def change_page(direction):
        nonlocal current_page
        current_page += direction
        update_display()
    
    update_display()
    page.add(list_view, pagination)

ft.app(target=main)

17. Incorrect Handling of Async/Await in Flet

Flet supports both synchronous and asynchronous programming models, but mixing them incorrectly can lead to deadlocks, race conditions, or unexpected behavior. Remember that if your main function is async, you must use await for async operations, and Flet will handle the async context properly.

Don't call async functions without await, and don't use await in synchronous functions. Choose one model consistently for your application.

import flet as ft
import asyncio

# CORRECT: Async main function
async def main(page: ft.Page):
    status = ft.Text("Ready")
    page.add(status)
    
    async def long_operation():
        await asyncio.sleep(2)
        return "Operation completed"
    
    async def handle_click(e):
        status.value = "Working..."
        page.update()
        result = await long_operation()  # Proper await
        status.value = result
        page.update()
    
    page.add(ft.ElevatedButton("Start Async Operation", on_click=handle_click))

# Run with async support
ft.app(target=main)

# INCORRECT alternatives:
# def main_sync(page):  # Sync main
#     async def async_handler(e):  # But async handler
#         await asyncio.sleep(1)  # This won't work properly

18. Not Using Proper Error Boundaries

Just like in React, Flet applications can benefit from error boundaries - components that catch errors in their child components and display fallback UI instead of crashing the entire application. While Flet doesn't have built-in error boundaries, you can implement similar patterns.

Wrap critical sections of your UI in try-except blocks and provide fallback content when errors occur. This prevents isolated component failures from bringing down your entire application.

import flet as ft

def safe_container(content_func, fallback_text="Error loading content"):
    try:
        return content_func()
    except Exception as e:
        print(f"Component error: {e}")
        return ft.Container(
            content=ft.Column([
                ft.Icon(ft.icons.ERROR, color=ft.colors.RED),
                ft.Text(fallback_text, color=ft.colors.RED)
            ]),
            padding=20,
            bgcolor=ft.colors.RED_50
        )

def main(page: ft.Page):
    def risky_content():
        # Simulate component that might fail
        if False:  # Change to True to see error handling
            raise ValueError("Simulated component error")
        return ft.Text("Content loaded successfully")
    
    page.add(
        ft.Text("Main Application"),
        safe_container(risky_content)
    )

ft.app(target=main)

19. Ignoring Accessibility Best Practices

Creating accessible applications is not just good practice - it's often a legal requirement. Many Flet developers forget to add proper accessibility attributes like semantic roles, labels, and keyboard navigation support.

Use Flet's accessibility properties (tooltip, expand, semantic roles) and ensure all interactive elements are keyboard accessible. Test your application with screen readers and keyboard-only navigation.

import flet as ft

def main(page: ft.Page):
    # GOOD: Accessible controls
    page.add(
        ft.TextField(
            label="Search",
            hint_text="Enter search term",
            tooltip="Search for products",  # Helpful tooltip
            autofocus=True
        ),
        ft.ElevatedButton(
            "Search",
            tooltip="Click to search",  # Descriptive tooltip
            on_click=lambda e: print("Searching...")
        ),
        ft.Switch(
            label="Dark mode",
            tooltip="Toggle dark mode theme"  # Labels for switches
        ),
        # Ensure all interactive elements can be reached with Tab key
        ft.IconButton(
            ft.icons.SETTINGS,
            tooltip="Open settings",  # Icons need tooltips
            on_click=lambda e: print("Settings")
        )
    )
    
    # BAD: Missing accessibility info
    # ft.IconButton(ft.icons.SEARCH)  # No tooltip, unclear purpose

ft.app(target=main)

20. Not Testing Across Different Platforms

Flet applications can run on web, desktop (Windows, macOS, Linux), and mobile. However, behavior can differ between platforms due to underlying differences in rendering engines, available fonts, and system capabilities.

Always test your application on all target platforms. Pay special attention to layout differences, font rendering, file system access, and platform-specific features. What works perfectly on Windows might have issues on macOS or in the browser.

import flet as ft
import platform

def main(page: ft.Page):
    current_platform = platform.system()
    
    # Platform-specific adjustments
    if current_platform == "Darwin":  # macOS
        page.fonts = {"Default": "SF Pro"}  # Use system font
    elif current_platform == "Windows":
        page.fonts = {"Default": "Segoe UI"}
    # Web will use default browser fonts
    
    page.add(
        ft.Text(f"Running on: {current_platform}"),
        ft.Text("Platform-specific content here"),
        ft.ElevatedButton(
            "Platform Action",
            on_click=lambda e: handle_platform_action(current_platform)
        )
    )

def handle_platform_action(platform_name):
    if platform_name == "Windows":
        # Windows-specific logic
        pass
    elif platform_name == "Darwin":
        # macOS-specific logic
        pass
    else:
        # Web or other platforms
        pass

ft.app(target=main)

21. Misunderstanding Control Visibility vs. Removal

Many developers confuse hiding a control (visible=False) with removing it from the page. Hidden controls still exist in the control tree and consume memory, while removed controls are completely detached. Choose the right approach based on your needs.

Use visibility toggling for controls you'll show/hide frequently. Remove controls entirely when they're no longer needed to free up memory and improve performance.

import flet as ft

def main(page: ft.Page):
    secret_message = ft.Text("This is a secret!", visible=False)
    
    def toggle_secret(e):
        secret_message.visible = not secret_message.visible
        page.update()
    
    def remove_permanently(e):
        if secret_message in page.controls:
            page.controls.remove(secret_message)
            toggle_btn.disabled = True
            remove_btn.disabled = True
            page.update()
    
    toggle_btn = ft.ElevatedButton("Toggle Secret", on_click=toggle_secret)
    remove_btn = ft.ElevatedButton("Remove Forever", on_click=remove_permanently)
    
    page.add(
        ft.Text("Secret Message Demo"),
        secret_message,
        toggle_btn,
        remove_btn
    )

ft.app(target=main)

22. Not Using Proper Logging for Debugging

Using print() statements for debugging works for simple cases, but becomes unmanageable in complex applications. Flet applications, especially when deployed to web servers, benefit from proper logging with different severity levels and structured output.

Implement proper logging with Python's logging module to track application flow, errors, and user actions. This makes debugging production issues much easier.

import flet as ft
import logging

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("FletApp")

def main(page: ft.Page):
    logger.info("Application started")
    
    def handle_action(e):
        try:
            logger.debug("Action handler called")
            # Simulate some logic
            result = perform_action()
            logger.info(f"Action completed successfully: {result}")
            status.value = "Success!"
        except Exception as ex:
            logger.error(f"Action failed: {ex}", exc_info=True)
            status.value = "Error occurred!"
        finally:
            page.update()
    
    def perform_action():
        # Simulate work
        return "Done"
    
    status = ft.Text("Ready")
    page.add(
        ft.ElevatedButton("Perform Action", on_click=handle_action),
        status
    )

ft.app(target=main)

Final Thoughts: Mastering Flet requires understanding both its unique architecture and general UI development best practices. By avoiding these common mistakes, you'll create more robust, maintainable, and user-friendly applications. Remember that every developer makes mistakes - the key is learning from them and continuously improving your code!