{"id":32633031,"url":"https://github.com/brunobrown/flet-asp","last_synced_at":"2025-10-30T23:49:19.133Z","repository":{"id":304514421,"uuid":"1015383219","full_name":"brunobrown/flet-asp","owner":"brunobrown","description":"FASP - Flet Atomic State Pattern","archived":false,"fork":false,"pushed_at":"2025-10-17T00:55:13.000Z","size":261,"stargazers_count":10,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-25T05:03:17.309Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brunobrown.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-07T12:22:51.000Z","updated_at":"2025-10-17T00:18:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"c103732c-ebca-4072-8803-0f1a89977dec","html_url":"https://github.com/brunobrown/flet-asp","commit_stats":null,"previous_names":["brunobrown/flet-asp"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/brunobrown/flet-asp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-asp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-asp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-asp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-asp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brunobrown","download_url":"https://codeload.github.com/brunobrown/flet-asp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunobrown%2Fflet-asp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281902524,"owners_count":26581164,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-30T02:00:06.501Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-30T23:49:17.450Z","updated_at":"2025-10-30T23:49:19.127Z","avatar_url":"https://github.com/brunobrown.png","language":"Python","funding_links":[],"categories":["Libraries"],"sub_categories":["Community Extensions"],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/user-attachments/assets/84c2835b-9356-42ae-8a78-7c3ac11679c1\" width=\"25%\" alt=\"flet-asp\"\u003e\u003c/p\u003e\n\u003ch1 align=\"center\"\u003e Flet ASP - Flet Atomic State Pattern\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/brunobrown/flet-asp/actions?query=workflow%3AMain+event%3Apush+branch%3Amain\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://github.com/brunobrown/flet-asp/actions/workflows/main.yml/badge.svg?event=push\u0026branch=main\" alt=\"Main\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/brunobrown/flet-asp/actions?query=workflow%3ADev+event%3Apush+branch%3ADev\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://github.com/brunobrown/flet-asp/actions/workflows/dev.yml/badge.svg?event=push\u0026branch=dev\" alt=\"Dev\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/flet-asp\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/flet-asp?color=%2334D058\u0026label=pypi%20package\" alt=\"Package version\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/flet-asp\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/pyversions/flet-asp.svg?color=%2334D058\" alt=\"Supported Python versions\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pepy.tech/projects/flet-asp\"\u003e\u003cimg src=\"https://static.pepy.tech/personalized-badge/flet-asp?period=total\u0026units=INTERNATIONAL_SYSTEM\u0026left_color=GREY\u0026right_color=BRIGHTGREEN\u0026left_text=Downloads\" alt=\"PyPI Downloads\"\u003e\u003c/a\u003e\n\n---\n\n## 📖 Overview\n\n**Flet ASP** (Flet Atomic State Pattern) is a reactive state management library for [Flet](https://flet.dev), bringing atom-based architecture and separation of concerns into Python apps — inspired by Flutter's [Riverpod](https://riverpod.dev) and [ASP](https://pub.dev/packages/asp).\n\nIt provides predictable, testable, and declarative state through:\n- `Atom` – single reactive unit of state\n- `Selector` – derived/computed state\n- `Action` – handles async workflows like login, fetch, etc.\n\n---\n\n## 📦 Installation\n\nInstall using your package manager of choice:\n\n```bash\n# Pip\npip install flet-asp\n\n# Poetry\npoetry add flet-asp\n\n# UV\nuv add flet-asp\n```\n\n---\n\n## ✨ Key Features\n\n✅ **Reactive atoms** - Automatic UI updates when state changes  \n✅ **Selectors** - Derived/computed state (sync \u0026 async)  \n✅ **Actions** - Async-safe workflows for API calls, auth, etc.  \n✅ **One-way \u0026 two-way binding** - Seamless form input synchronization  \n✅ **Hybrid update strategy** - Bindings work even before controls are mounted  \n✅ **Python 3.14+ optimizations** - Free-threading, incremental GC, 3-5% faster  \n✅ **Lightweight** - No dependencies beyond Flet  \n✅ **Type-safe** - Full type hints support\n\n---\n\n## 🚀 Quick Start\n\n### 1. Basic Counter (Your First Atom)\n\nThe simplest way to use Flet-ASP: create an atom, bind it to a control, and update it.\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\ndef main(page: ft.Page):\n    # Initialize state manager\n    fa.get_state_manager(page)\n\n    # Create a reactive atom\n    page.state.atom(\"count\", 0)\n\n    # Create UI references\n    count_text = ft.Ref[ft.Text]()\n\n    def increment(e):\n        # Update the atom - UI updates automatically!\n        current = page.state.get(\"count\")\n        page.state.set(\"count\", current + 1)\n\n    # Build UI\n    page.add(\n        ft.Column([\n            ft.Text(\"Counter\", size=30),\n            ft.Text(ref=count_text, size=50),\n            ft.ElevatedButton(\"Increment\", on_click=increment)\n        ])\n    )\n\n    # Bind atom to UI - the Text will update automatically\n    page.state.bind(\"count\", count_text)\n\nft.app(target=main)\n```\n\n**What's happening here?**\n1. `atom(\"count\", 0)` - Creates a reactive piece of state\n2. `bind(\"count\", count_text)` - Connects state to UI\n3. `set(\"count\", value)` - Updates state → UI updates automatically!\n\n---\n\n### 2. Form with Two-Way Binding\n\nPerfect for input fields that need to sync with state.\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    # Create atoms for form fields\n    page.state.atom(\"email\", \"\")\n    page.state.atom(\"password\", \"\")\n\n    # UI references\n    email_field = ft.Ref[ft.TextField]()\n    password_field = ft.Ref[ft.TextField]()\n    message_text = ft.Ref[ft.Text]()\n\n    def login(e):\n        email = page.state.get(\"email\")\n        password = page.state.get(\"password\")\n\n        if email == \"user@example.com\" and password == \"123\":\n            message_text.current.value = f\"Welcome, {email}!\"\n        else:\n            message_text.current.value = \"Invalid credentials\"\n        page.update()\n\n    page.add(\n        ft.Column([\n            ft.Text(\"Login Form\", size=24),\n            ft.TextField(ref=email_field, label=\"Email\"),\n            ft.TextField(ref=password_field, label=\"Password\", password=True),\n            ft.ElevatedButton(\"Login\", on_click=login),\n            ft.Text(ref=message_text)\n        ])\n    )\n\n    # Two-way binding: TextField ↔ Atom\n    page.state.bind_two_way(\"email\", email_field)\n    page.state.bind_two_way(\"password\", password_field)\n\nft.app(target=main)\n```\n\n**Key concept:** `bind_two_way()` keeps the TextField and atom in perfect sync!\n\n---\n\n### 3. Computed State with Selectors\n\nDerive new values from existing state automatically.\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    # Base atoms\n    page.state.atom(\"first_name\", \"John\")\n    page.state.atom(\"last_name\", \"Doe\")\n\n    # Computed state - automatically recalculates when dependencies change\n    @page.state.selector(\"full_name\")\n    def compute_full_name(get):\n        return f\"{get('first_name')} {get('last_name')}\"\n\n    # UI\n    first_field = ft.Ref[ft.TextField]()\n    last_field = ft.Ref[ft.TextField]()\n    full_name_text = ft.Ref[ft.Text]()\n\n    page.add(\n        ft.Column([\n            ft.Text(\"Name Builder\", size=24),\n            ft.TextField(ref=first_field, label=\"First Name\"),\n            ft.TextField(ref=last_field, label=\"Last Name\"),\n            ft.Divider(),\n            ft.Text(\"Full Name:\", weight=ft.FontWeight.BOLD),\n            ft.Text(ref=full_name_text, size=20, color=ft.Colors.BLUE)\n        ])\n    )\n\n    # Bind inputs\n    page.state.bind_two_way(\"first_name\", first_field)\n    page.state.bind_two_way(\"last_name\", last_field)\n\n    # Bind computed state\n    page.state.bind(\"full_name\", full_name_text)\n\nft.app(target=main)\n```\n\n**Magic!** The full name updates automatically when first or last name changes.\n\n---\n\n### 4. Async Operations with Actions\n\nHandle API calls, async operations, and side effects cleanly.\n\n```python\nimport asyncio\nimport flet as ft\nimport flet_asp as fa\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    page.state.atom(\"user\", None)\n    page.state.atom(\"loading\", False)\n\n    # Define async action\n    async def login_action(get, set_value, params):\n        set_value(\"loading\", True)\n\n        # Simulate API call\n        await asyncio.sleep(2)\n\n        # Validate credentials\n        email = params.get(\"email\")\n        password = params.get(\"password\")\n\n        if email == \"test@test.com\" and password == \"123\":\n            set_value(\"user\", {\"email\": email, \"name\": \"Test User\"})\n        else:\n            set_value(\"user\", None)\n\n        set_value(\"loading\", False)\n\n    # Create action\n    login = fa.Action(login_action)\n\n    # UI\n    email_field = ft.Ref[ft.TextField]()\n    password_field = ft.Ref[ft.TextField]()\n    status_text = ft.Ref[ft.Text]()\n\n    async def handle_login(e):\n        await login.run_async(\n            page.state,\n            {\n                \"email\": email_field.current.value,\n                \"password\": password_field.current.value\n            }\n        )\n\n        user = page.state.get(\"user\")\n        if user:\n            status_text.current.value = f\"Welcome, {user['name']}!\"\n        else:\n            status_text.current.value = \"Login failed\"\n        page.update()\n\n    # Listen to loading state\n    def on_loading_change(is_loading):\n        status_text.current.value = \"Logging in...\" if is_loading else \"\"\n        page.update()\n\n    page.state.listen(\"loading\", on_loading_change)\n\n    page.add(\n        ft.Column([\n            ft.Text(\"Async Login\", size=24),\n            ft.TextField(ref=email_field, label=\"Email\"),\n            ft.TextField(ref=password_field, label=\"Password\", password=True),\n            ft.ElevatedButton(\"Login\", on_click=handle_login),\n            ft.Text(ref=status_text)\n        ])\n    )\n\nft.app(target=main)\n```\n\n**Actions** encapsulate complex async logic in a testable, reusable way.\n\n---\n\n## 📚 Advanced Usage\n\n### Custom Controls with Reactive State\n\nCreate reusable components with encapsulated state.\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\nclass Counter(ft.Column):\n    \"\"\"Reusable counter component with its own state.\"\"\"\n\n    def __init__(self, page: ft.Page, counter_id: str, title: str):\n        super().__init__()\n        self.page = page\n        self.counter_id = counter_id\n        self.value_text = ft.Ref[ft.Text]()\n\n        # Initialize state for this counter\n        page.state.atom(f\"{counter_id}_count\", 0)\n\n        self.controls = [\n            ft.Container(\n                content=ft.Column([\n                    ft.Text(title, size=20, weight=ft.FontWeight.BOLD),\n                    ft.Text(ref=self.value_text, size=40, color=ft.Colors.BLUE),\n                    ft.Row([\n                        ft.IconButton(\n                            icon=ft.Icons.REMOVE,\n                            on_click=self.decrement\n                        ),\n                        ft.IconButton(\n                            icon=ft.Icons.ADD,\n                            on_click=self.increment\n                        )\n                    ], alignment=ft.MainAxisAlignment.CENTER)\n                ], horizontal_alignment=ft.CrossAxisAlignment.CENTER),\n                padding=20,\n                border=ft.border.all(2, ft.Colors.BLUE),\n                border_radius=10\n            )\n        ]\n\n    def did_mount(self):\n        # Bind when component is mounted\n        self.page.state.bind(f\"{self.counter_id}_count\", self.value_text)\n\n    def increment(self, e):\n        current = self.page.state.get(f\"{self.counter_id}_count\")\n        self.page.state.set(f\"{self.counter_id}_count\", current + 1)\n\n    def decrement(self, e):\n        current = self.page.state.get(f\"{self.counter_id}_count\")\n        self.page.state.set(f\"{self.counter_id}_count\", current - 1)\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    page.add(\n        ft.Column([\n            ft.Text(\"Multiple Counters\", size=30),\n            ft.Row([\n                Counter(page, \"counter1\", \"Counter A\"),\n                Counter(page, \"counter2\", \"Counter B\"),\n                Counter(page, \"counter3\", \"Counter C\")\n            ])\n        ])\n    )\n\nft.app(target=main)\n```\n\n---\n\n### Navigation with State Preservation\n\nState persists across navigation automatically!\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\ndef home_screen(page: ft.Page):\n    \"\"\"Home screen with shared state.\"\"\"\n    count_text = ft.Ref[ft.Text]()\n\n    def go_to_settings(e):\n        page.views.clear()\n        page.views.append(settings_screen(page))\n        page.update()\n\n    return ft.View(\n        \"/\",\n        [\n            ft.AppBar(title=ft.Text(\"Home\"), bgcolor=ft.Colors.BLUE),\n            ft.Column([\n                ft.Text(\"Counter Value:\", size=20),\n                ft.Text(ref=count_text, size=50, color=ft.Colors.BLUE),\n                ft.ElevatedButton(\"Go to Settings\", on_click=go_to_settings)\n            ])\n        ]\n    )\n\ndef settings_screen(page: ft.Page):\n    \"\"\"Settings screen - modifies shared state.\"\"\"\n\n    def increment(e):\n        current = page.state.get(\"count\")\n        page.state.set(\"count\", current + 1)\n\n    def go_back(e):\n        page.views.clear()\n        page.views.append(home_screen(page))\n        page.update()\n\n    return ft.View(\n        \"/settings\",\n        [\n            ft.AppBar(title=ft.Text(\"Settings\"), bgcolor=ft.Colors.GREEN),\n            ft.Column([\n                ft.Text(\"Modify Counter\", size=20),\n                ft.ElevatedButton(\"Increment\", on_click=increment),\n                ft.ElevatedButton(\"Go Back\", on_click=go_back)\n            ])\n        ]\n    )\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    # Shared state across screens\n    page.state.atom(\"count\", 0)\n\n    page.views.append(home_screen(page))\n\n    # Bind state after adding view (works with hybrid strategy!)\n    count_ref = page.views[0].controls[1].controls[1]  # Get the count text\n    page.state.bind(\"count\", ft.Ref[ft.Text]())\n\nft.app(target=main)\n```\n\n---\n\n### Global State Outside Page Scope\n\nFor advanced scenarios like testing, multi-window applications, or complex state architectures, you can create a `StateManager` **outside** the page scope.\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\n# Create global StateManager OUTSIDE the page\nglobal_state = fa.StateManager()\n\ndef screen_a(page: ft.Page):\n    \"\"\"Main screen with counter.\"\"\"\n    count_ref = ft.Ref[ft.Text]()\n\n    def increment(e):\n        # Use global_state instead of page.state\n        global_state.set(\"count\", global_state.get(\"count\") + 1)\n\n    def go_to_b(e):\n        page.go(\"/b\")\n\n    view = ft.View(\n        \"/\",\n        [\n            ft.Text(\"Screen A - Global State\", size=24, weight=ft.FontWeight.BOLD),\n            ft.Text(ref=count_ref, size=40, color=ft.Colors.BLUE_700),\n            ft.ElevatedButton(\"Increment\", on_click=increment),\n            ft.ElevatedButton(\"Go to Screen B\", on_click=go_to_b),\n        ],\n        padding=20,\n    )\n\n    # Bind using global_state\n    global_state.bind(\"count\", count_ref)\n    return view\n\ndef screen_b(page: ft.Page):\n    \"\"\"Secondary screen displaying the counter.\"\"\"\n    def go_back(e):\n        page.go(\"/\")\n\n    return ft.View(\n        \"/b\",\n        [\n            ft.Text(\"Screen B - Global State\", size=24, weight=ft.FontWeight.BOLD),\n            ft.Text(f\"Counter value: {global_state.get('count')}\", size=16),\n            ft.Text(\"State is managed globally!\", color=ft.Colors.GREEN_700),\n            ft.ElevatedButton(\"Go back\", on_click=go_back),\n        ],\n        padding=20,\n    )\n\ndef main(page: ft.Page):\n    \"\"\"App entry point.\"\"\"\n    # IMPORTANT: Attach the page to the global StateManager\n    global_state.page = page\n\n    # Initialize atoms\n    global_state.atom(\"count\", 0)\n\n    def route_change(e):\n        page.views.clear()\n        if page.route == \"/b\":\n            page.views.append(screen_b(page))\n        else:\n            page.views.append(screen_a(page))\n        page.update()\n\n    page.on_route_change = route_change\n    page.go(\"/\")\n\nft.app(target=main)\n```\n\n**When to use global state:**\n\n| Use Case | Why Global State? |\n|----------|-------------------|\n| **Unit Testing** | Test state logic without creating a Flet page |\n| **Multi-Window Apps** | Share state between multiple page instances |\n| **Advanced Architectures** | State exists independently of UI lifecycle |\n| **Framework Integration** | Flet-ASP as part of a larger system |\n\n**Key differences:**\n\n| Aspect | `page.state` | `global_state` |\n|--------|--------------|----------------|\n| **Creation** | `fa.get_state_manager(page)` | `fa.StateManager()` |\n| **Page binding** | Automatic | Manual (`global_state.page = page`) |\n| **Scope** | Inside `main()` | Global (module level) |\n| **Lifecycle** | Managed by page | Manual |\n| **When to use** | ✅ Most cases | ⚠️ Specific scenarios |\n\n**Common pitfalls:**\n\n```python\n# ❌ WRONG - Forgot to attach page\nglobal_state = fa.StateManager()\n\ndef main(page: ft.Page):\n    global_state.atom(\"count\", 0)  # Error: page not attached!\n\n# ✅ CORRECT - Attach page first\nglobal_state = fa.StateManager()\n\ndef main(page: ft.Page):\n    global_state.page = page  # Attach first!\n    global_state.atom(\"count\", 0)\n```\n\n**Testing example:**\n\n```python\nimport unittest\nimport flet_asp as fa\n\n# Global state for testing\ntest_state = fa.StateManager()\n\nclass TestMyLogic(unittest.TestCase):\n    def setUp(self):\n        test_state._atoms.clear()\n        test_state.atom(\"count\", 0)\n\n    def test_increment(self):\n        # Test logic without creating a Flet page\n        test_state.set(\"count\", test_state.get(\"count\") + 1)\n        self.assertEqual(test_state.get(\"count\"), 1)\n\n    def test_computed_value(self):\n        test_state.atom(\"double\", lambda: test_state.get(\"count\") * 2)\n        test_state.set(\"count\", 5)\n        self.assertEqual(test_state.get(\"double\"), 10)\n```\n\nFor a complete example, see [`11.1_global_state_outside.py`](./examples/11.1_global_state_outside.py).\n\n---\n\n### Complex Selectors with Async Data\n\nFetch and compute data asynchronously.\n\n```python\nimport asyncio\nimport flet as ft\nimport flet_asp as fa\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    # Base atoms\n    page.state.atom(\"user_id\", 1)\n\n    # Async selector - fetches user data\n    @page.state.selector(\"user_data\")\n    async def fetch_user(get):\n        user_id = get(\"user_id\")\n\n        # Simulate API call\n        await asyncio.sleep(1)\n\n        # Mock user data\n        users = {\n            1: {\"name\": \"Alice\", \"email\": \"alice@example.com\"},\n            2: {\"name\": \"Bob\", \"email\": \"bob@example.com\"},\n            3: {\"name\": \"Charlie\", \"email\": \"charlie@example.com\"}\n        }\n\n        return users.get(user_id, {\"name\": \"Unknown\", \"email\": \"N/A\"})\n\n    # UI\n    user_info = ft.Ref[ft.Text]()\n\n    def update_user_info(user_data):\n        # Async selectors may return coroutines on first call, check the type\n        import inspect\n        if inspect.iscoroutine(user_data):\n            # Skip coroutine objects - they will be resolved automatically\n            return\n        if user_data:\n            user_info.current.value = f\"{user_data['name']} ({user_data['email']})\"\n        else:\n            user_info.current.value = \"Loading...\"\n        page.update()\n\n    def next_user(e):\n        current_id = page.state.get(\"user_id\")\n        page.state.set(\"user_id\", (current_id % 3) + 1)\n\n    # Listen to selector changes\n    page.state.listen(\"user_data\", update_user_info)\n\n    page.add(\n        ft.Column([\n            ft.Text(\"User Profile\", size=24),\n            ft.Text(ref=user_info, size=18),\n            ft.ElevatedButton(\"Next User\", on_click=next_user)\n        ])\n    )\n\nft.app(target=main)\n```\n\n---\n\n### Shopping Cart Example\n\nReal-world e-commerce state management.\n\n```python\nimport flet as ft\nimport flet_asp as fa\n\ndef main(page: ft.Page):\n    fa.get_state_manager(page)\n\n    # State\n    page.state.atom(\"cart_items\", [])\n\n    # Selectors\n    @page.state.selector(\"cart_total\")\n    def calculate_total(get):\n        items = get(\"cart_items\")\n        return sum(item[\"price\"] * item[\"quantity\"] for item in items)\n\n    @page.state.selector(\"cart_count\")\n    def count_items(get):\n        items = get(\"cart_items\")\n        return sum(item[\"quantity\"] for item in items)\n\n    # Available products\n    products = [\n        {\"id\": 1, \"name\": \"Laptop\", \"price\": 999.99},\n        {\"id\": 2, \"name\": \"Mouse\", \"price\": 29.99},\n        {\"id\": 3, \"name\": \"Keyboard\", \"price\": 79.99}\n    ]\n\n    # UI refs\n    cart_list = ft.Ref[ft.Column]()\n    cart_count_text = ft.Ref[ft.Text]()\n    cart_total_text = ft.Ref[ft.Text]()\n\n    def add_to_cart(product):\n        items = page.state.get(\"cart_items\")\n\n        # Check if item already in cart\n        existing = next((item for item in items if item[\"id\"] == product[\"id\"]), None)\n\n        if existing:\n            existing[\"quantity\"] += 1\n        else:\n            items.append({**product, \"quantity\": 1})\n\n        page.state.set(\"cart_items\", items)\n\n    def render_cart():\n        items = page.state.get(\"cart_items\")\n\n        cart_list.current.controls = [\n            ft.ListTile(\n                title=ft.Text(item[\"name\"]),\n                subtitle=ft.Text(f\"${item['price']:.2f} × {item['quantity']}\"),\n                trailing=ft.Text(f\"${item['price'] * item['quantity']:.2f}\")\n            ) for item in items\n        ] if items else [ft.Text(\"Cart is empty\")]\n\n        page.update()\n\n    # Listen to cart changes\n    page.state.listen(\"cart_items\", lambda _: render_cart())\n\n    # Build UI\n    page.add(\n        ft.Row([\n            # Products column\n            ft.Column([\n                ft.Text(\"Products\", size=24),\n                *[\n                    ft.ElevatedButton(\n                        f\"{p['name']} - ${p['price']:.2f}\",\n                        on_click=lambda e, product=p: add_to_cart(product)\n                    ) for p in products\n                ]\n            ], expand=1),\n\n            # Cart column\n            ft.Column([\n                ft.Text(\"Shopping Cart\", size=24),\n                ft.Text(ref=cart_count_text),\n                ft.Column(ref=cart_list),\n                ft.Divider(),\n                ft.Text(ref=cart_total_text, size=20, weight=ft.FontWeight.BOLD)\n            ], expand=1)\n        ])\n    )\n\n    # Bind computed values\n    page.state.bind(\"cart_count\", cart_count_text, prop=\"value\")\n    page.state.bind(\"cart_total\", cart_total_text, prop=\"value\")\n\n    render_cart()\n\nft.app(target=main)\n```\n\n---\n\n## ⚡ Performance \u0026 Python 3.14+\n\n**Flet-ASP** includes a **hybrid update strategy** that ensures bindings work reliably, even when controls are bound before being added to the page.\n\n### Hybrid Strategy:\n1. **Lazy updates** - Property is always set (never fails)\n2. **Immediate updates** - Tries to update if control is mounted (99% of cases)\n3. **Lifecycle hooks** - Hooks into `did_mount` for custom controls\n4. **Queue fallback** - Retries when `page.update()` is called\n\n### Python 3.14+ Optimizations:\n\n| Feature | Benefit | Performance Gain |\n|---------|---------|------------------|\n| **Free-threading** | Process bindings in parallel without GIL | Up to 4x faster for large apps |\n| **Incremental GC** | Smaller garbage collection pauses | 10x smaller pauses (20ms → 2ms) |\n| **Tail call interpreter** | Faster Python execution | 3-5% overall speedup |\n\n**Configuration** (optional):\n```python\nfrom flet_asp.atom import Atom\n\n# For giant apps with 1000+ bindings on Python 3.14+\nAtom.MAX_PARALLEL_BINDS = 8\n\n# For small apps or to disable free-threading\nAtom.ENABLE_FREE_THREADING = False\n```\n\nFor more details, see [PERFORMANCE.md](./PERFORMANCE.md).\n\n---\n\n## 📁 More Examples\n\nExplore the [`examples/`](./examples/) folder for complete applications:\n\n**Basic Examples:**\n- [`1.0_counter_atom.py`](./examples/1.0_counter_atom.py) - Simple counter\n- [`1.1_counter_atom_using_state_alias.py`](./examples/1.1_counter_atom_using_state_alias.py) - Counter with page.state\n- [`2_counter_atom_bind_dynamic.py`](./examples/2_counter_atom_bind_dynamic.py) - Dynamic binding\n\n**Intermediate Examples:**\n- [`3_computed_fullname.py`](./examples/3_computed_fullname.py) - Computed state\n- [`4_action_login.py`](./examples/4_action_login.py) - Async actions\n- [`5_selector_user_email.py`](./examples/5_selector_user_email.py) - Selectors\n- [`6_listen_user_login.py`](./examples/6_listen_user_login.py) - State listeners\n- [`7_bind_two_way_textfield.py`](./examples/7_bind_two_way_textfield.py) - Two-way binding\n- [`8_session_reset_clear.py`](./examples/8_session_reset_clear.py) - State cleanup\n\n**Advanced Examples:**\n- [`9_todo.py`](./examples/9_todo.py) - Complete ToDo app\n- [`10_cart_app.py`](./examples/10_cart_app.py) - Shopping cart\n- [`11_screen_a_navigation_screen_b.py`](./examples/11_screen_a_navigation_screen_b.py) - Navigation with page.state\n- [`11.1_global_state_outside.py`](./examples/11.1_global_state_outside.py) - Global state outside page scope\n- [`12_python314_performance.py`](./examples/12_python314_performance.py) - Performance demo\n- [`13_hybrid_binding_advanced.py`](./examples/13_hybrid_binding_advanced.py) - Hybrid binding\n\n**Atomic Design Examples:**\n- [`14_atomic_design_dashboard/`](./examples/14_atomic_design_dashboard/) - Complete dashboard with atoms, molecules, organisms, templates, and pages\n- [`15_atomic_design_theming/`](./examples/15_atomic_design_theming/) - Theme-aware component library with design tokens\n- [`16_reactive_atomic_components/`](./examples/16_reactive_atomic_components/) - Reactive components with built-in state management\n\n---\n\n## 🧩 Building Design Systems with Atomic Design\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/user-attachments/assets/2d31c317-8b76-4d4d-8d9b-ab64da600ddd\" alt=\"Atomic Design System\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n**Flet-ASP** is designed from the ground up to work seamlessly with the **Atomic Design methodology** - a powerful approach for building scalable, maintainable design systems.\n\n### What is Atomic Design?\n\nAtomic Design is a methodology for creating design systems by breaking down interfaces into fundamental building blocks, inspired by chemistry:\n\n**🔬 Atoms** → **🧪 Molecules** → **🧬 Organisms** → **📄 Templates** → **📱 Pages**\n\n### How Flet-ASP Maps to Atomic Design\n\n| Atomic Design Layer | Flet-ASP Feature | Example |\n|---------------------|------------------|---------|\n| **Atoms** | Reactive state values | `page.state.atom(\"email\", \"\")` |\n| **Molecules** | Computed state | `@page.state.selector(\"full_name\")` |\n| **Organisms** | Actions \u0026 workflows | `Action(login_function)` |\n| **Templates** | State bindings | `page.state.bind(\"count\", ref)` |\n| **Pages** | Complete screens | Custom controls with encapsulated state |\n\n### Real-World Atomic Design with Flet-ASP\n\nWe provide **two comprehensive examples** that demonstrate professional design system architecture:\n\n#### 📊 Example 14: Dashboard Design System\n\nA complete dashboard application showcasing the full Atomic Design hierarchy:\n\n- **Atoms**: Buttons, inputs, text styles, icons, dividers\n- **Molecules**: Stat cards, menu items, form fields, search bars\n- **Organisms**: Sidebar, top bar, data tables, stats grid\n- **Templates**: Dashboard layouts with different content arrangements\n- **Pages**: Dashboard, analytics, users, orders, settings screens\n\n```python\n# Atoms define the foundation\nfrom atoms import heading1, primary_button\n\n# Molecules combine atoms\nfrom molecules import stat_card\n\n# Organisms compose molecules\nfrom organisms import stats_grid\n\n# Templates arrange organisms\nfrom templates import dashboard_template\n\n# Pages bring it all together\nfrom pages import dashboard_page\n```\n\n**Features:**\n- ✅ Complete component hierarchy following Atomic Design\n- ✅ Real-time data updates with reactive state bindings\n- ✅ Navigation with state preservation\n- ✅ Reusable components across multiple pages\n- ✅ Consistent design language\n\n[**View Example →**](./examples/14_atomic_design_dashboard/)\n\n#### 🎨 Example 15: Theme-Aware Component Library\n\nAn advanced example demonstrating **design tokens** and dynamic theming:\n\n- **Design Tokens**: Colors, typography, spacing, border radius\n- **Theme-Aware Atoms**: Components that adapt to light/dark modes\n- **Reactive Theming**: Real-time theme switching with flet-asp\n- **Semantic Colors**: Success, warning, error, info states\n\n```python\nfrom theme_tokens import get_theme\nfrom atoms import filled_button, text_field\nfrom molecules import alert, stat_card\n\n# All components automatically adapt to current theme\ntheme = get_theme()\nbutton = filled_button(\"Submit\")  # Uses theme.colors.primary\n```\n\n**Features:**\n- ✅ Complete design token system (colors, typography, spacing)\n- ✅ Light and dark mode support\n- ✅ Theme switching without page reload\n- ✅ Semantic color system for alerts and states\n- ✅ Professional design system architecture\n\n[**View Example →**](./examples/15_atomic_design_theming/)\n\n#### ⚛️ Example 16: Reactive Atomic Components\n\nComponents that **combine visual structure + reactive state** in a single, reusable package:\n\n```python\nfrom reactive_atoms import ReactiveCounter, ReactiveStatCard, ReactiveForm\n\n# Create counter with built-in state!\ncounter = ReactiveCounter(page, \"Counter A\", initial_count=0)\npage.add(counter.control)\n\n# Interact via clean API\ncounter.increment()  # +1\ncounter.decrement()  # -1\ncounter.reset()      # Set to 0\nprint(counter.value) # Get current value\n\n# Stat card with auto-updates\nusers_card = ReactiveStatCard(\n    page,\n    title=\"Total Users\",\n    atom_key=\"users\",\n    initial_value=\"1,234\",\n    icon_name=ft.Icons.PEOPLE,\n    show_trend=True\n)\n\n# Update programmatically\nusers_card.update_with_trend(\"2,500\", \"+15%\")  # ✨ UI updates automatically!\n```\n\n**Features:**\n- ✅ Components with built-in reactive state\n- ✅ No manual binding needed\n- ✅ Clean, intuitive API\n- ✅ Encapsulated state management\n- ✅ Reusable across projects\n\n[**View Example →**](./examples/16_reactive_atomic_components/)\n\n### Why Atomic Design + Flet-ASP?\n\n**🎯 Consistency**: Design tokens and atoms ensure uniform styling across your app\n\n**🔄 Reusability**: Build components once, use them everywhere with different state bindings\n\n**📈 Scalability**: Add new features by combining existing atoms and molecules\n\n**🧪 Testability**: Test atoms, molecules, and organisms in isolation\n\n**🤝 Collaboration**: Designers and developers work with the same component language\n\n**⚡ Reactivity**: State changes propagate automatically through the component hierarchy\n\n### Learn More About Atomic Design\n\n- [Atomic Design by Brad Frost](https://atomicdesign.bradfrost.com/chapter-2/) - The definitive guide\n- [Building Design Systems with Atomic Design](https://www.designbetter.co/design-systems-handbook)\n- [Material Design System](https://m3.material.io/) - Real-world example\n- [Flet-ASP Examples](./examples/) - Practical implementations\n\n---\n\n## 🌐 Community\n\nJoin the community to contribute or get help:\n\n- [Discord](https://discord.gg/dzWXP8SHG8)\n- [GitHub Issues](https://github.com/brunobrown/flet-asp/issues)\n\n## ⭐ Support\n\nIf you like this project, please give it a [GitHub star](https://github.com/brunobrown/flet-asp) ⭐\n\n---\n\n## 🤝 Contributing\n\nContributions and feedback are welcome!\n\n1. Fork the repository\n2. Create a feature branch\n3. Submit a pull request with detailed explanation\n\nFor feedback, [open an issue](https://github.com/brunobrown/flet-asp/issues) with your suggestions.\n\n---\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/user-attachments/assets/431aa05f-5fbc-4daa-9689-b9723583e25a\" width=\"50%\"\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://www.bible.com/bible/116/PRO.16.NLT\"\u003e Commit your work to the LORD, and your plans will succeed. Proverbs 16:3\u003c/a\u003e\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunobrown%2Fflet-asp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrunobrown%2Fflet-asp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunobrown%2Fflet-asp/lists"}