Title
App state persists across toggled components when using ft.use_state (Python 3.12, Flet 0.80.5, Windows 11)
Environment
- OS: Windows 11
- Python: 3.12
- Flet: 0.80.5
- Repo: flet-dev/flet
Description
When toggling between two component trees that each hold their own @ft.observable app instance, the UI shows state from the first app even after switching to the second. The displayed class name and the rendered users don’t match the expected app instance after toggling.
Steps to Reproduce
- Run the sample below with Flet 0.80.5 / Python 3.12 on Windows 11.
- Click “Toggle App” to switch between
App and DifferentApp.
- Observe the text showing the class instance and the rendered user list.
Expected Behavior
- When
DifferentApp is active, the text should show Class DifferentApp instance DifferentApp and the user list should be Alice2 / Bob2.
- When
App is active, the text should show Class App instance App with John/Jane/Foo.
Actual Behavior
- After toggling, the UI shows
Class DifferentApp instance App while rendering users from the first app (John/Jane/Foo), indicating state from the first app is reused across toggles.
- Switching back keeps showing the first app state.
Code Sample
from dataclasses import dataclass, field
import flet as ft
@ft.observable
@dataclass
class User:
first_name: str
last_name: str
def update(self, first_name: str, last_name: str):
self.first_name = first_name
self.last_name = last_name
@ft.observable
@dataclass
class App:
users: list[User] = field(default_factory=list)
def add_user(self, first_name: str, last_name: str):
if first_name.strip() or last_name.strip():
self.users.append(User(first_name, last_name))
def delete_user(self, user: User):
self.users.remove(user)
@ft.observable
@dataclass
class DifferentApp:
users: list[User] = field(default_factory=list)
def add_user(self, first_name: str, last_name: str):
if first_name.strip() or last_name.strip():
self.users.append(User(first_name, last_name))
def delete_user(self, user: User):
self.users.remove(user)
@ft.component
def UserView(user: User, app: App) -> ft.Control:
delete_user = app.delete_user
is_editing, set_is_editing = ft.use_state(False)
new_first_name, set_new_first_name = ft.use_state(user.first_name)
new_last_name, set_new_last_name = ft.use_state(user.last_name)
def start_edit():
set_new_first_name(user.first_name)
set_new_last_name(user.last_name)
set_is_editing(True)
def save():
user.update(new_first_name, new_last_name)
set_is_editing(False)
def cancel():
set_is_editing(False)
if not is_editing:
return ft.Row([
ft.Text(f"{user.first_name} {user.last_name}"),
ft.Button("Edit", on_click=start_edit),
ft.Button("Delete", on_click=lambda: delete_user(user)),
])
return ft.Row([
ft.TextField(label="First Name", value=new_first_name,
on_change=lambda e: set_new_first_name(e.control.value), width=180),
ft.TextField(label="Last Name", value=new_last_name,
on_change=lambda e: set_new_last_name(e.control.value), width=180),
ft.Button("Save", on_click=save),
ft.Button("Cancel", on_click=cancel),
])
@ft.component
def AddUserForm(app) -> ft.Control:
add_user = app.add_user
new_first_name, set_new_first_name = ft.use_state("")
new_last_name, set_new_last_name = ft.use_state("")
def add_user_and_clear():
add_user(new_first_name, new_last_name)
set_new_first_name("")
set_new_last_name("")
return ft.Row(controls=[
ft.TextField(label="First Name", width=200, value=new_first_name,
on_change=lambda e: set_new_first_name(e.control.value)),
ft.TextField(label="Last Name", width=200, value=new_last_name,
on_change=lambda e: set_new_last_name(e.control.value)),
ft.Button("Add", on_click=add_user_and_clear),
])
@ft.component
def AppView() -> list[ft.Control]:
first_app, first_app_set = ft.use_state(True)
def toggle_app(e: ft.Event[ft.Button]):
new_first_app = not first_app
first_app_set(new_first_app)
bottom_bar = ft.Button("Toggle App", on_click=toggle_app)
if first_app:
app, _ = ft.use_state(App(users=[User("John", "Doe"),
User("Jane", "Doe"),
User("Foo", "Bar")]))
text_control = ft.Text(f"Class {App.__name__} instance {app.__class__.__name__}")
return [bottom_bar, text_control, AddUserForm(app),
*[UserView(user, app) for user in app.users]]
else:
app2, _ = ft.use_state(DifferentApp(users=[User("Alice2", "Smith"),
User("Bob2", "Johnson")]))
text_control = ft.Text(f"Class {DifferentApp.__name__} instance {app2.__class__.__name__}")
return [bottom_bar, text_control, AddUserForm(app2),
*[UserView(user, app2) for user in app2.users]]
ft.run(lambda page: page.render(AppView))
Screenshots
)
)
Additional Notes
It appears ft.use_state is preserving and reusing the first component’s observable instance across toggles. Guidance on resetting state or isolating component instances when toggling would be helpful.
Title
App state persists across toggled components when using
ft.use_state(Python 3.12, Flet 0.80.5, Windows 11)Environment
Description
When toggling between two component trees that each hold their own
@ft.observableapp instance, the UI shows state from the first app even after switching to the second. The displayed class name and the rendered users don’t match the expected app instance after toggling.Steps to Reproduce
AppandDifferentApp.Expected Behavior
DifferentAppis active, the text should showClass DifferentApp instance DifferentAppand the user list should beAlice2 / Bob2.Appis active, the text should showClass App instance AppwithJohn/Jane/Foo.Actual Behavior
Class DifferentApp instance Appwhile rendering users from the first app (John/Jane/Foo), indicating state from the first app is reused across toggles.Code Sample
Screenshots
)
)
Additional Notes
It appears
ft.use_stateis preserving and reusing the first component’s observable instance across toggles. Guidance on resetting state or isolating component instances when toggling would be helpful.