TL;DR: FletX provides reactive variables (RxInt, RxStr, RxBool, RxList, RxDict) that automatically notify observers when they change. Watch them with .listen(), compute derived values with create_computed(), and use @obx decorator to rebuild UI components automatically. No manual update() calls needed.
classTodoController(FletXController):def__init__(self):super().__init__()self.todos=self.create_rx_list([])# Computed: auto-updates when todos changeself.count=self.create_computed(lambda:len(self.todos))defadd_todo(self,title):self.todos.append({"title":title,"done":False})# Everything updates automatically!classTodoPage(FletXPage):def__init__(self):super().__init__()self.controller=TodoController()@obxdefbuild(self):# UI rebuilds automatically when todos changesreturnft.Column([ft.Text(f"Todos: {self.controller.count}"),# Auto-updatesft.Column([ft.Text(t["title"])fortinself.controller.todos])])
fromfletx.coreimportFletXControllerclassCounterController(FletXController):def__init__(self):super().__init__()# Create a reactive integerself.count=self.create_rx_int(0)
# Get the valuecurrent_count=self.count.value# Set a new valueself.count.value=5# Or self.count.set(5)# Use type-specific methodsself.count.increment()# count += 1self.count.decrement()# count -= 1
defon_ready(self):# Call function when count changesself.count.listen(self._on_count_change)def_on_count_change(self):print(f"Count is now: {self.count.value}")
fromfletx.coreimportReactiveclassUserModel:def__init__(self,name,email):self.name=nameself.email=emailclassUserController(FletXController):def__init__(self):super().__init__()# Generic reactive for any objectself.user=self.create_reactive(UserModel("John","john@example.com"))defupdate_user(self,user):self.user.value=user
classDataController(FletXController):def__init__(self):super().__init__()self.data=self.create_rx_str("")defon_ready(self):# Call function whenever data changesself.data.listen(self._on_data_change)def_on_data_change(self):print(f"Data changed: {self.data.value}")
defon_ready(self):self.count.listen(self._log_count)self.count.listen(self._update_ui)self.count.listen(self._save_to_db)# All three are called when count changes
fromfletx.decoratorsimportobxclassTodoPage(FletXPage):def__init__(self):super().__init__()self.controller=TodoController()@obxdeftodo_item_widget(self,todo):"""This rebuilds when reactive values in the controller change"""returnft.Card(content=ft.Container(content=ft.Text(todo["title"]),padding=10))@obxdefbuild(self):"""Main UI rebuilds automatically when todos or count changes"""returnft.Column([ft.Text(f"Count: {self.controller.count}",# Auto-updatessize=24,weight="bold"),ft.ListView([self.todo_item_widget(todo)fortodoinself.controller.todos# Auto-updates])])
How it works:
@obx wraps the method
When the method runs, it tracks reactive variables used
If any reactive variable changes, the method is called again
# ❌ Without @obx - need manual updatesdefbuild(self):defon_count_change():count_text.value=str(self.controller.count.value)self.page_instance.update()self.controller.count.listen(on_count_change)count_text=ft.Text(str(self.controller.count.value))returncount_text# ✅ With @obx - automatic!@obxdefbuild(self):returnft.Text(str(self.controller.count.value))
# ✅ Good - use create_rx_* methodsself.count=self.create_rx_int(0)self.name=self.create_rx_str("")self.items=self.create_rx_list([])# ❌ Avoid - importing types directlyfromfletx.core.stateimportRxIntself.count=RxInt(0)# Not tracked by controller lifecycle
# ✅ Good - setup listeners in on_readydefon_ready(self):self.count.listen(self._on_count_change)# ❌ Avoid - setup in __init__def__init__(self):self.count.listen(self._on_count_change)# Might not work
# ✅ Good - no side effects in @obx methods@obxdefbuild(self):returnft.Text(self.controller.value)# ❌ Avoid - side effects in @obx@obxdefbuild(self):save_to_db(self.controller.value)# Don't do this!returnft.Text(self.controller.value)