ποΈ FletX Architecture¶
FletX is built on a modular and reactive architecture designed to help developers structure Flet applications in a clean, maintainable, and scalable way. It is inspired by principles like separation of concerns and dependency injection.
π Overview¶
FletX architecture revolves around three core components:
- Pages (
FletXPage
) β declarative, reactive UI components. - Controllers (
FletXController
) β business logic and state management. - Services (optional) β reusable utilities for API calls, database access, etc.
π Typical Flow¶
A typical user interaction flows like this:
User Action β Controller Logic β State Update β UI Re-render
When routing/navigation happens, it flows like this:
Routing β Page Instantiation β Controller Injection β Build UI
π§± Core Building Blocks¶
1. FletXPage¶
A FletXPage is a class that represents a visual page (screen) in your app. It inherits from FletXPage
and defines a build()
method that returns a reactive Flet UI.
Example:¶
class HomePage(FletXPage):
ctrl = HomeController()
def build(self):
return ft.Column([
ft.Text(lambda: str(self.ctrl.counter()), size=40),
ft.ElevatedButton("Increment", on_click=lambda e: self.ctrl.counter.increment())
])
2. FletXController¶
A FletXController handles business logic, manages reactive state, and is tied to a specific page. It uses observable values to trigger UI updates automatically.
Example:¶
class HomeController(FletXController):
def __init__(self):
self.counter = RxInt(0)
super().__init__()
RxInt
is a reactive object provided by FletX. Updating it automatically refreshes all widgets that depend on it.
π Navigation & Routing¶
FletX provides a centralized router configuration (router_config
) for managing navigation across your app:
router_config.add_route("/", HomePage)
router_config.add_route("/about", AboutPage)
# Or register a list of routes
router_config.add_routes([
{"path": "/", "component": HomePage},
{"path": "/settings", "component": SettingsPage}
])
You can define dynamic routes like:
router_config.add_route("/user/:id", UserPage)
router_config.add_route("/user/*category", CategoryPage)
In your page:
def build(self):
user_id = self.route_info.params["id"]
π§ Reactive State Management¶
FletX provides reactive variables: RxInt
, RxStr
, RxList
, etc., which track their values and trigger UI updates when modified.
Example:¶
class CounterController(FletXController):
def __init__(self):
self.count = RxInt(0)
super().__init__()
class CounterPage(FletXPage):
ctrl = CounterController()
def build(self):
return MyReactiveText(rx_text=self.ctrl.count, size=200, weight="bold"),
lambda:
makes the widget reactive β it will re-render automatically when the value changes.
π§© Services (Optional)¶
Services are reusable, testable classes used for accessing APIs, databases, or any shared logic. They can be injected into controllers.
Example:¶
class UserService:
def fetch_user(self, user_id):
return {"id": user_id, "name": "John Doe"}
Used in a controller:
class UserController(FletXController):
def __init__(self):
self.user_service = FletX.find(UserService)
self.user = RxDict({})
super().__init__()
def load_user(self, user_id):
self.user.value = self.user_service.fetch_user(user_id)
π§ͺ Minimal Architecture Example¶
Hereβs a minimal FletX app putting all the pieces together:
# main.py
from fletx.app import FletXApp
from fletx.navigation import router_config
from .pages.counter import CounterPage
router_config.add_route("/", CounterPage)
app = FletXApp(title="Demo App")
app.run()
# pages/counter.py
from fletx.core import FletXPage
from .controllers.counter import CounterController
from .components.reactive_text import MyReactiveText
import flet as ft
class CounterPage(FletXPage):
ctrl = CounterController()
def build(self):
return ft.Column([
MyReactiveText(rx_text=self.ctrl.count, size=40, weight="bold"),
ft.ElevatedButton("Increment", on_click=lambda e: self.ctrl.count.increment())
])
# components/reactive_text.py
import flet as ft
from fletx.decorators import simple_reactive
@simple_reactive(bindings={'value': 'text'})
class MyReactiveText(ft.Text):
def __init__(self, rx_text: RxStr, **kwargs):
self.text: RxStr = rx_text
super().__init__(**kwargs)
# controllers/counter.py
from fletx.core import FletXController, RxInt
class CounterController(FletXController):
def __init__(self):
self.count = RxInt(0)
super().__init__()
β Summary Table¶
Component | Responsibility |
---|---|
FletXPage |
Builds the UI, binds to controller |
FletXController |
Holds business logic and reactive state |
Rx* objects |
Reactive state (trigger UI rebuilds) |
router_config |
Defines app navigation routes |
Services | Shared utilities for APIs, storage, etc. |
π§ Next Steps¶
- Explore reactive UI binding
- Learn about the Architecture
- Dive into dependency injection