{"id":43941895,"url":"https://github.com/fasilwdr/flet-stack","last_synced_at":"2026-02-07T02:05:37.275Z","repository":{"id":318284051,"uuid":"1070621346","full_name":"fasilwdr/flet-stack","owner":"fasilwdr","description":"Simple, intuitive routing with automatic view stacking for Flet applications.","archived":false,"fork":false,"pushed_at":"2025-12-19T22:21:07.000Z","size":84,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-22T09:53:47.943Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://pypi.org/project/flet-stack","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/fasilwdr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-10-06T07:57:46.000Z","updated_at":"2025-11-10T09:17:10.000Z","dependencies_parsed_at":"2025-10-06T10:15:23.036Z","dependency_job_id":"cba31ee2-a20e-4283-bd75-53af0d797e5e","html_url":"https://github.com/fasilwdr/flet-stack","commit_stats":null,"previous_names":["fasilwdr/flet-stack"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/fasilwdr/flet-stack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasilwdr%2Fflet-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasilwdr%2Fflet-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasilwdr%2Fflet-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasilwdr%2Fflet-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fasilwdr","download_url":"https://codeload.github.com/fasilwdr/flet-stack/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasilwdr%2Fflet-stack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29184977,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T00:44:15.062Z","status":"online","status_checked_at":"2026-02-07T02:00:07.217Z","response_time":63,"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":"2026-02-07T02:05:36.667Z","updated_at":"2026-02-07T02:05:37.270Z","avatar_url":"https://github.com/fasilwdr.png","language":"Python","funding_links":[],"categories":["Libraries"],"sub_categories":["Community Extensions"],"readme":"# flet-stack\n\n**Simple, intuitive routing with automatic view stacking for Flet applications.**\n\n[![PyPI version](https://badge.fury.io/py/flet-stack.svg)](https://badge.fury.io/py/flet-stack)\n[![Python versions](https://img.shields.io/pypi/pyversions/flet-stack.svg)](https://pypi.org/project/flet-stack/)\n[![PyPI Downloads](https://static.pepy.tech/personalized-badge/flet-stack?period=total\u0026units=INTERNATIONAL_SYSTEM\u0026left_color=GREY\u0026right_color=BLUE\u0026left_text=downloads)](https://pepy.tech/projects/flet-stack)\n\n## ✨ What's New in 0.3.0\n\nVersion 0.3.0 brings a major simplification to the API:\n\n- 🎯 **Even simpler routing** - Views are now just functions that return `ft.View` objects\n- 🔄 **Stack navigation** - Use `+/route` to stack views, `/route` to replace the entire stack\n- ❌ **No more `@ft.component`** - Just simple functions, no decorator boilerplate\n- ⚡ **Automatic reactivity** - State changes trigger re-renders automatically\n- 🧹 **Cleaner code** - Less boilerplate, more straightforward\n\n**[See the migration guide](#migration-from-02x) if upgrading from 0.2.x**\n\n## Features\n\n- 🎯 **Decorator-based routing** - Clean `@route()` decorator for route definitions\n- 📚 **Stack navigation** - Intuitive stack vs replace navigation with \"+\" prefix\n- 🔄 **Observable state management** - Built-in state with `@ft.observable` dataclasses\n- ⚡ **Async support** - Handle async data loading with automatic loading indicators\n- 🎨 **URL parameters** - Extract parameters from routes like `/user/{id}`\n- 🚀 **Simple setup** - Just call `page.render_views(FletStack)` in your app\n- 🔗 **No boilerplate** - Views are simple functions returning `ft.View` objects\n\n## Requirements\n\n- Python 3.9+\n- Flet \u003e= 0.70.0.dev6281\n\n## Installation\n\n### From PyPI\n\n```bash\npip install flet-stack\n```\n\n### From GitHub\n\n```bash\npip install git+https://github.com/fasilwdr/flet-stack.git\n```\n\n### Install Specific Version\n\n```bash\npip install git+https://github.com/fasilwdr/flet-stack.git@v0.3.0\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/fasilwdr/flet-stack.git\ncd flet-stack\npip install .\n```\n\n## Quick Start\n\n```python\nimport flet as ft\nfrom flet_stack import route, FletStack\nimport asyncio\n\n# Define your routes with the @route decorator\n@route(\"/\")\ndef home_view():\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(\"Home\")),\n        controls=[\n            ft.Text(\"Home Page\", size=30),\n            ft.Button(\n                \"Go to Profile\",\n                on_click=lambda _: asyncio.create_task(\n                    ft.context.page.push_route(\"+/profile\")\n                )\n            ),\n        ]\n    )\n\n@route(\"/profile\")\ndef profile_view():\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(\"Profile\")),\n        controls=[\n            ft.Text(\"Profile Page\", size=30),\n        ]\n    )\n\n# Run your app\nft.run(lambda page: page.render_views(FletStack))\n```\n\nThat's it! Clean, simple routing with no boilerplate.\n\n## Navigation: Stack vs Replace\n\nflet-stack supports two navigation modes:\n\n### Stack Navigation (Add to Stack)\n\nUse the **\"+\" prefix** to add a view on top of the current stack:\n\n```python\n# Adds /profile on top of the current view\nasyncio.create_task(ft.context.page.push_route(\"+/profile\"))\n\n# User can press back to return to previous view\n```\n\n### Replace Navigation (Replace Stack)\n\nUse **no prefix** to replace the entire navigation stack:\n\n```python\n# Replaces entire stack with just /home\nasyncio.create_task(ft.context.page.push_route(\"/home\"))\n\n# Previous views are cleared - back button goes to previous view in new stack\n```\n\n**Common Pattern:**\n\n```python\n# From home, stack other views\nft.Button(\"Products\", on_click=lambda _: push_route(\"+/products\"))\n\n# From anywhere, return home (clearing stack)\nft.IconButton(icon=ft.Icons.HOME, on_click=lambda _: push_route(\"/\"))\n```\n\n## Advanced Usage\n\n### URL Parameters\n\nExtract parameters from your routes:\n\n```python\n@route(\"/user/{user_id}\")\ndef user_view(user_id):\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(f\"User {user_id}\")),\n        controls=[\n            ft.Text(f\"User Profile: {user_id}\", size=30),\n        ]\n    )\n```\n\n### State Management\n\nUse observable dataclasses to manage component state. State automatically triggers re-renders when methods are called:\n\n```python\nfrom dataclasses import dataclass\n\n@ft.observable\n@dataclass\nclass CounterState:\n    count: int = 0\n    \n    def increment(self, e):\n        self.count += 1\n    \n    def decrement(self, e):\n        self.count -= 1\n\n@route(\"/counter\", state_class=CounterState)\ndef counter_view(state):\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(\"Counter\")),\n        controls=[\n            ft.Text(f\"Count: {state.count}\", size=30),\n            ft.Row([\n                ft.Button(\"Decrement\", on_click=state.decrement),\n                ft.Button(\"Increment\", on_click=state.increment),\n            ]),\n        ]\n    )\n```\n\n**State automatically triggers re-renders** when you call methods like `increment()` or `decrement()` - no manual update needed!\n\n### Async Data Loading\n\nLoad data asynchronously before showing your view:\n\n```python\n@ft.observable\n@dataclass\nclass UserState:\n    user_data: dict = None\n\nasync def load_user_data(state, user_id):\n    # Simulate API call\n    await asyncio.sleep(1)\n    state.user_data = {\n        \"id\": user_id,\n        \"name\": f\"User {user_id}\",\n        \"email\": f\"user{user_id}@example.com\"\n    }\n\n@route(\"/user/{user_id}\", state_class=UserState, on_load=load_user_data)\ndef user_detail_view(state, user_id):\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(state.user_data['name'])),\n        controls=[\n            ft.Text(f\"Name: {state.user_data['name']}\", size=20),\n            ft.Text(f\"Email: {state.user_data['email']}\", size=16),\n            ft.Text(f\"ID: {state.user_data['id']}\", size=16),\n        ]\n    )\n```\n\nWhile `on_load` executes, a loading spinner is automatically displayed.\n\n### Sync Data Loading\n\nYou can also use synchronous loading functions:\n\n```python\ndef load_item_info(state, category, item_id):\n    \"\"\"Sync data loading\"\"\"\n    state.info = {\n        \"category\": category.capitalize(),\n        \"item_id\": item_id,\n        \"name\": f\"{category.capitalize()} Item #{item_id}\",\n        \"price\": f\"${int(item_id) * 10}.99\"\n    }\n\n@route(\n    \"/category/{category}/item/{item_id}\",\n    state_class=ItemState,\n    on_load=load_item_info\n)\ndef item_view(state, category, item_id):\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(state.info['name'])),\n        controls=[\n            ft.Text(f\"{state.info['name']}\", size=20),\n            ft.Text(f\"Price: {state.info['price']}\", size=18),\n        ]\n    )\n```\n\n### Multiple URL Parameters\n\nHandle routes with multiple parameters:\n\n```python\n@route(\"/category/{category}/item/{item_id}\")\ndef item_view(category, item_id):\n    return ft.View(\n        appbar=ft.AppBar(title=ft.Text(f\"{category} Items\")),\n        controls=[\n            ft.Text(f\"Category: {category}\", size=20),\n            ft.Text(f\"Item ID: {item_id}\", size=20),\n        ]\n    )\n```\n\n### Setting Initial Route\n\nYou can start your app at any route instead of the default `/`:\n\n```python\ndef main(page: ft.Page):\n    page.title = \"My App\"\n    page.route = \"/login\"  # Start at login page\n    page.render_views(FletStack)\n\nft.run(main)\n```\n\n## API Reference\n\n### `@route` Decorator\n\n```python\n@route(path: str, state_class: Type = None, on_load: Optional[Callable] = None)\n```\n\n- **path**: The route path for this view (e.g., `/`, `/user/{user_id}`)\n- **state_class**: Optional dataclass decorated with `@ft.observable` for state management\n- **on_load**: Optional function to call before rendering (can be async)\n  - Parameters are automatically injected based on function signature\n  - Can accept: `state` (if state_class provided) and any URL parameters\n  - While executing, a loading view is displayed automatically\n\n### `FletStack` Component\n\nMain component that manages the routing stack and renders views.\n\n```python\n# Option 1: Direct render\nft.run(lambda page: page.render_views(FletStack))\n\n# Option 2: In main function\ndef main(page: ft.Page):\n    page.title = \"My App\"\n    page.route = \"/login\"  # Optional: Set initial route\n    page.render_views(FletStack)\n\nft.run(main)\n```\n\n### Navigation\n\nUse `asyncio.create_task` with `ft.context.page.push_route`:\n\n```python\n# Stack navigation - add to current stack\nasyncio.create_task(ft.context.page.push_route(\"+/profile\"))\n\n# Replace navigation - replace entire stack\nasyncio.create_task(ft.context.page.push_route(\"/\"))\n\n# In button click handler\nft.Button(\n    \"Go to Profile\",\n    on_click=lambda _: asyncio.create_task(\n        ft.context.page.push_route(\"+/profile\")\n    )\n)\n```\n\n## Examples\n\nCheck the `examples/` directory for more detailed examples:\n- `basic_example.py` - Simple routing and navigation\n- `advanced_example.py` - State management, async loading, and URL parameters\n\n## How It Works\n\n**flet-stack** provides a `FletStack` component that:\n\n1. Registers all `@route` decorated functions\n2. Manages a navigation stack with stack vs replace modes\n3. Handles state management with observable dataclasses and automatic re-renders\n4. Manages async/sync loading with automatic progress indicators\n5. Renders views with proper navigation support\n6. Supports custom initial routes via `page.route`\n7. Isolates state per route instance for parameterized routes\n\n## Migration from 0.2.x\n\nIf you're upgrading from version 0.2.x, here are the key changes:\n\n### 1. Decorator Renamed\n\n```python\n# Before (0.2.x)\nfrom flet_stack import view\n\n# After (0.3.0)\nfrom flet_stack import route\n```\n\n### 2. Views Return ft.View Objects\n\nViews no longer return lists of controls wrapped in `@ft.component`. They now return `ft.View` objects directly:\n\n```python\n# Before (0.2.x)\n@view(\"/profile\", appbar=ft.AppBar())\n@ft.component\ndef profile_view():\n    return [\n        ft.Text(\"Profile\"),\n        ft.Button(\"Click me\")\n    ]\n\n# After (0.3.0)\n@route(\"/profile\")\ndef profile_view():\n    return ft.View(\n        appbar=ft.AppBar(),\n        controls=[\n            ft.Text(\"Profile\"),\n            ft.Button(\"Click me\")\n        ]\n    )\n```\n\n### 3. Stack Navigation Syntax\n\nUse the \"+\" prefix for stacking views:\n\n```python\n# Before (0.2.x) - always stacked\nasyncio.create_task(ft.context.page.push_route(\"/products\"))\n\n# After (0.3.0) - explicit stack vs replace\nasyncio.create_task(ft.context.page.push_route(\"+/products\"))  # Stack\nasyncio.create_task(ft.context.page.push_route(\"/\"))  # Replace\n```\n\n### 4. Simplified on_load\n\n`on_load` no longer accepts `page` or `view` parameters:\n\n```python\n# Before (0.2.x)\nasync def load_user(state, view, user_id):\n    state.user = fetch_user(user_id)\n    view.appbar = ft.AppBar(title=ft.Text(state.user['name']))\n\n# After (0.3.0)\nasync def load_user(state, user_id):\n    state.user = fetch_user(user_id)\n    # Set appbar directly in view function\n```\n\n### 5. No More @ft.component\n\nSimply remove the `@ft.component` decorator:\n\n```python\n# Before (0.2.x)\n@view(\"/counter\", state_class=CounterState)\n@ft.component\ndef counter_view(state):\n    return [ft.Text(f\"Count: {state.count}\")]\n\n# After (0.3.0)\n@route(\"/counter\", state_class=CounterState)\ndef counter_view(state):\n    return ft.View(\n        controls=[ft.Text(f\"Count: {state.count}\")]\n    )\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Acknowledgments\n\nBuilt on top of the amazing [Flet](https://docs.flet.dev) framework by Feodor Fitsner.\n\n## Support\n\nIf you encounter any issues or have questions:\n- Open an issue on [GitHub](https://github.com/fasilwdr/flet-stack/issues)\n- Check the [examples](examples/) directory\n- Read the [Flet documentation](https://docs.flet.dev)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffasilwdr%2Fflet-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffasilwdr%2Fflet-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffasilwdr%2Fflet-stack/lists"}