TL;DR: A FletXController is where your business logic lives. It manages reactive state (create_rx_int(), create_rx_str(), etc.), handles events (local and global), manages side effects (use_effect()), and communicates with your UI through reactive variables and callbacks. Controllers keep your code organized and testable.
# Getting valuescount_value=self.count.value# Setting valuesself.count.value=5self.username.value="Alice"self.is_enabled.value=True# Or with set() methodself.count.set(6)self.username.set("Bob")# Working with listsself.items.append("new item")self.items.extend(["item2","item3"])self.items.remove("new item")self.items.pop()self.items.clear()# Working with dictsself.config["theme"]="light"self.config.update({"lang":"fr"})self.config.get("theme")# Special methods for specific typesself.count.increment()# count += 1self.count.decrement()# count -= 1self.is_enabled.toggle()# is_enabled = not is_enabledself.username.append(" Smith")# append to stringself.username.clear()# clear string
classUserController(FletXController):def__init__(self):super().__init__()self.email=self.create_rx_str("")defon_ready(self):# Listen to email changesself.email.listen(self._on_email_change)def_on_email_change(self):print(f"Email changed to: {self.email.value}")# Validate email, send events, etc.
classUserController(FletXController):def__init__(self):super().__init__()self.first_name=self.create_rx_str("John")self.last_name=self.create_rx_str("Doe")# Create computed full nameself.full_name=self.create_computed(lambda:f"{self.first_name.value} {self.last_name.value}")defon_ready(self):# Listen to computed value changesself.full_name.listen(lambda:print(f"Full name: {self.full_name.value}"))# In page@obxdefbuild(self):returnft.Text(self.controller.full_name.value)# Updates automatically when first_name or last_name changes
classDataController(FletXController):def__init__(self):super().__init__()self.search_query=self.create_rx_str("")self.results=self.create_rx_list([])defon_ready(self):# Effect runs whenever search_query changesself.use_effect(self._perform_search,deps=[self.search_query])def_perform_search(self):"""Effect function that runs on every search_query change"""query=self.search_query.valueifquery:# Simulate API callself.results.value=self._search_database(query)else:self.results.value=[]def_search_database(self,query):return[f"Result {i}"foriinrange(3)]
# Emit eventscontroller.emit_local("user_logged_in",{"username":"john"})# Listen to eventscontroller.on_local("user_logged_in",callback)# Listen only oncecontroller.once_local("user_logged_in",callback)# Stop listeningcontroller.off_local("user_logged_in",callback)# Get reactive list of all eventsevents=controller.listen_reactive_local("user_logged_in")events.listen(lambda:print(f"Total events: {len(events.value)}"))# Access event historylast_event=controller.event_bus.last_event.valueall_events=controller.event_bus.event_history.value
classNotificationController(FletXController):defon_ready(self):# Listen to global eventsself.on_global("user_logged_out",self._on_user_logged_out)def_on_user_logged_out(self,event):print("User logged out globally!")classAuthController(FletXController):deflogout(self):# Emit global eventself.emit_global("user_logged_out",{})# All controllers with listeners will be notified
# Same as local but global scopecontroller.emit_global("event_name",data)controller.on_global("event_name",callback)controller.once_global("event_name",callback)controller.off_global("event_name",callback)controller.listen_reactive_global("event_name")
classUserProfileController(FletXController):def__init__(self):super().__init__()defon_ready(self):# Store in local contextself.set_context("user_id",123)self.set_context("user_role","admin")# Get from contextuser_id=self.get_context("user_id")# Get reactive versionrx_role=self.get_context_reactive("user_role")rx_role.listen(lambda:print(f"Role changed: {rx_role.value}"))# Check if existshas_user=self.has_context("user_id")# Update multipleself.update_context(user_id=124,user_role="user")# Removeself.remove_context("user_role")
classThemeController(FletXController):defset_theme(self,theme):# Store globally - accessible from all controllersself.set_global_context("current_theme",theme)classAnyOtherController(FletXController):defon_ready(self):# Access global contexttheme=self.get_global_context("current_theme")# Get reactive versionrx_theme=self.get_global_context_reactive("current_theme")rx_theme.listen(lambda:print(f"Theme changed: {rx_theme.value}"))
classFullLifecycleController(FletXController):def__init__(self):super().__init__()self.data=self.create_rx_str("initial")defon_initialized(self):"""Called when controller is created and initialized"""print("Controller initialized")defon_ready(self):"""Called when controller is ready (page is showing)"""print("Controller ready")# Setup listeners, load dataself.load_data()defon_disposed(self):"""Called when controller is destroyed"""print("Controller disposed")# Cleanup, cancel requests, close connectionsdefload_data(self):# Load initial dataself.data.value="loaded"
classApiController(FletXController):def__init__(self):super().__init__()self.api_data=self.create_rx_list([])deffetch_data(self):# Use built-in stateself.set_loading(True)self.clear_error()try:# Simulate API calldata=self._call_api()self.api_data.value=dataexceptExceptionase:self.set_error(str(e))finally:self.set_loading(False)def_call_api(self):return[{"id":1,"name":"Item"}]# In pageclassDataPage(FletXPage):def__init__(self):super().__init__()self.controller=ApiController()@obxdefbuild(self):ifself.controller.is_loading:returnft.ProgressRing()ifself.controller.error_message:returnft.Text(f"Error: {self.controller.error_message}")returnft.Column([ft.Text(f"Items: {len(self.controller.api_data)}")])
classParentController(FletXController):def__init__(self):super().__init__()self.child1=ChildController()self.child2=ChildController()# Register childrenself.add_child(self.child1)self.add_child(self.child2)defdispose(self):# Children are automatically disposedsuper().dispose()classChildController(FletXController):def__init__(self):super().__init__()self.name=self.create_rx_str("")
# ✅ Good - setup in on_readydefon_ready(self):self.load_data()self.email.listen(self._validate_email)# ❌ Avoid - setup in __init__def__init__(self):self.load_data()# May not work correctly