Flet State Management

Deep dive · part of Python Flet

Flet has no global store: state is just Python objects on the page. For small apps keep state in closures and call page.update() after changes; for larger apps wrap state in a class and pass it to view builders.

Flet has no Redux—state is plain Python objects you mutate, then page.update() refreshes controls. Small apps use closures; larger apps wrap a state class passed to view builders to avoid scattered globals.

Controls mirror state (label.value = str(counter)); bidirectional sync for TextField requires on_change handlers updating the state object.

Production code combines this topic with logging, tests, and clear module boundaries so refactors stay safe when requirements grow.

page.update() batches UI refresh after multiple control mutations.

Class-based state (CounterState) groups fields and methods.

ft.Ref can attach to controls for imperative access when needed.

Shared state on page.client_storage persists light keys locally.

Async handlers should use page.run_task for thread-safe UI updates.

Separation: state class vs pure view builder functions aids testing.

For forms, validate on submit reading state dict, not individual controls mid-typing unless live validation required. Multi-user server apps need backend session, not only client state.

Observable patterns (callbacks on state change) reduce forgotten update() calls when many widgets depend on one counter.

Large apps mirror MVVM: state class as model, control builders as view, handlers as viewmodel—page.update() is the render tick.

Mirror MVVM: keep mutable state in a class, rebuild controls in builders, call page.update() once per event.

Read the parent tutorial on pythondeck.com for runnable snippets, then reproduce them locally in a virtual environment with pinned dependency versions matching your deployment target.

When pairing with teammates, agree on one idiomatic pattern per concern—mixed styles in one repo slow reviews and invite subtle integration bugs during merges.

Global variables mutated without update()—UI stale until random refresh.

Storing non-serializable objects in client_storage.

Updating UI from background threads without run_task.

Duplicating state in both TextField.value and separate var inconsistently.

One State class per page or feature with typed fields.

Call page.update() once per event after all mutations.

Extract handlers (def add(_):) that read/write state clearly.

Unit-test state logic without launching ft.app when possible.

Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.

The program below demonstrates counter w/ class state. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# Example: Counter w/ class state
# Run in the REPL or save as a .py file and execute with python.
import flet as ft

class CounterState:
    def __init__(self):
        self.value = 0

def main(page):
    state = CounterState()
    label = ft.Text("0", size=32)
    def add(_):
        state.value += 1
        label.value = str(state.value)
        page.update()
    def reset(_):
        state.value = 0
        label.value = "0"
        page.update()
    page.add(label, ft.Row([
        ft.ElevatedButton("+1", on_click=add),
        ft.OutlinedButton("reset", on_click=reset),
    ]))

ft.app(target=main)

This sample walks through class state in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# Keep state in a Python object; call page.update after mutations
import flet as ft  # import

class Counter:  # state bag
    def __init__(self): self.value = 0  # start at zero

def main(page: ft.Page):  # UI
    state = Counter()  # instance
    label = ft.Text("0")  # view
    def inc(_):  # handler
        state.value += 1  # mutate
        label.value = str(state.value)  # sync label
        page.update()  # push to UI
    page.add(label, ft.ElevatedButton("+1", on_click=inc))  # layout

print("stateful UI built")  # log

Here is a hands-on illustration of shared dict. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.

# Simple apps store session data on page.client_storage or dict
session = {"user": None}  # module-level session

def login(name):  # fake auth
    session["user"] = name  # set current user

def current():  # reader
    return session["user"]  # may be None

login("Ada")  # sign in
print(current())  # Ada
login(None)  # sign out
print(current())  # None

« back to Python Flet All tutorials