A powerful routing library for Flet applications with authentication, middleware, and type safety
Project description
Flet Router
A powerful and flexible routing library for Flet applications, enabling seamless navigation between pages and views with support for deep linking, route parameters, authentication, and middleware.
Overview
Flet Router simplifies navigation management in Flet applications by providing a declarative routing system. It handles page transitions, state management, and URL-based navigation, allowing developers to build multi-page applications with ease. Built with type safety, performance, and extensibility in mind.
Features
- Declarative Routing: Define routes using a clean, intuitive API
- Page Navigation: Seamless transitions between different pages and views
- Route Parameters: Support for dynamic URL parameters with automatic type casting
- Query Parameters: Handle URL query strings
- Private Parameters: Securely pass sensitive data between routes using encryption
- Deep Linking: Navigate directly to specific routes with parameters
- History Management: Built-in back/forward navigation support with stack management
- Authentication & Guards: Protect routes with authentication checks and custom guards
- Middleware Support: Execute custom logic before route transitions
- Named Routes: Navigate using human-readable route names
- Route Preloading: Preload views for improved performance
- View Caching: Optional caching system for frequently accessed views
- Session Management: Built-in session handling for user state
- Async Support: Full support for asynchronous operations and data loading
- Type Safety: Full type hints for better IDE support and error detection
- Lightweight: Minimal dependencies and overhead
- Extensible: Plugin system with middleware and guards
Installation
Install Flet Router using pip:
pip install flet-router
Requirements
- Python 3.9+
- Flet 0.28.3+
Async Support
Flet Router fully supports asynchronous operations in your route handlers and middleware. Use page.run_task() or asyncio.run() for async data loading:
import httpx
import asyncio
@router.route("/products")
def products_view(params):
products_list = ft.Column()
async def load_products():
async with httpx.AsyncClient() as client:
response = await client.get("https://api.example.com/products")
products = response.json()
# Update UI with loaded data
products_list.controls = [ft.Text(p["name"]) for p in products]
params.page.update()
# Start async task
params.page.run_task(load_products)
return ft.View(
route="/products",
controls=[
ft.Text("Products", size=30),
products_list,
]
)
Quick Start
Basic Usage with Decorators
import flet as ft
from flet_router import FletRouter
def main(page: ft.Page):
router = FletRouter(page=page)
@router.route("/")
def home_view(params):
return ft.View(
route="/",
controls=[
ft.Text("Home Page", size=30),
ft.ElevatedButton(
"Go to About",
on_click=lambda e: router.push("/about")
)
]
)
@router.route("/about")
def about_view(params):
return ft.View(
route="/about",
controls=[
ft.Text("About Page", size=30),
ft.ElevatedButton(
"Go Back",
on_click=lambda e: router.back()
)
]
)
router.push("/")
ft.app(target=main)
Alternative: Using Route Class
from flet_router import FletRouter, Route
def main(page: ft.Page):
def home_view(params):
return ft.View(route="/", controls=[ft.Text("Home Page")])
def about_view(params):
return ft.View(route="/about", controls=[ft.Text("About Page")])
router = FletRouter(
page=page,
routes=[
Route("/", home_view),
Route("/about", about_view),
]
)
router.push("/")
ft.app(target=main)
Route Parameters
def user_view(params):
user_id = params.path["id"]
return ft.View(
route="/user/:id",
controls=[
ft.Text(f"User Profile: {user_id}", size=30),
ft.ElevatedButton("Back", on_click=lambda e: params.router.back())
]
)
router = FletRouter(
page=page,
routes=[
Route("/user/:id", user_view),
]
)
# Navigate with parameters
router.push("/user/123")
Authentication & Protected Routes
import flet as ft
from flet_router import FletRouter, Route, MiddlewareContext
# Session state
class Session:
def __init__(self):
self.authenticated = False
self.user = None
session = Session()
# Authentication middleware
def auth_middleware(ctx: MiddlewareContext) -> bool:
if ctx.route and ctx.route.protected and not session.authenticated:
ctx.router.replace("/login", {"redirect": ctx.full_path})
return False
return True
# Custom guard for user profiles
def user_profile_guard(ctx: MiddlewareContext) -> bool:
if not session.authenticated:
return False
# Users can only view their own profile
user_id = ctx.params.path.get("id")
return str(session.user_id) == str(user_id)
def login_view(params):
username_field = ft.TextField(label="Username")
password_field = ft.TextField(label="Password", password=True)
def do_login(e):
# Your authentication logic here
session.authenticated = True
session.user = username_field.value
# Redirect to intended page or home
redirect_to = params.private.get("redirect", "/")
params.router.replace(redirect_to)
return ft.View(
route="/login",
controls=[
ft.Text("Login", size=30),
username_field,
password_field,
ft.ElevatedButton("Login", on_click=do_login),
]
)
def dashboard_view(params):
return ft.View(
route="/dashboard",
controls=[
ft.Text(f"Welcome, {session.user}!", size=30),
ft.ElevatedButton("Logout", on_click=lambda e: logout_and_redirect(params.router)),
]
)
def user_profile_view(params):
user_id = params.path["id"]
return ft.View(
route=f"/user/{user_id}",
controls=[
ft.Text(f"Profile for user {user_id}", size=30),
]
)
def logout_and_redirect(router):
session.authenticated = False
session.user = None
router.replace("/")
router = FletRouter(
page=page,
routes=[
Route("/login", login_view),
Route("/dashboard", dashboard_view, protected=True),
Route("/user/:id", user_profile_view, protected=True, guard=user_profile_guard),
],
auth_checker=lambda: session.authenticated,
not_auth_redirect_route="/login"
)
router.use(auth_middleware)
Middleware
def logging_middleware(ctx):
print(f"Navigating to: {ctx.path}")
return True # Continue navigation
def auth_middleware(ctx):
if ctx.route and ctx.route.protected and not ctx.router.checker():
return False # Block navigation
return True
router = FletRouter(page=page, routes=routes)
router.use(logging_middleware)
router.use(auth_middleware)
Named Routes
router = FletRouter(
page=page,
routes=[
Route("/user/:id", user_view, name="user_profile"),
]
)
# Navigate using name
router.push_named("user_profile", path_params={"id": "123"})
Decorators vs Route Class
You can define routes using either decorators or the Route class. Both approaches are equivalent:
Using Decorators (Recommended):
@router.route("/user/:id", protected=True, name="user_profile")
def user_view(params):
return ft.View(route=f"/user/{params.path['id']}", ...)
Using Route Class:
def user_view(params):
return ft.View(route=f"/user/{params.path['id']}", ...)
routes = [
Route("/user/:id", user_view, protected=True, name="user_profile"),
]
router = FletRouter(page=page, routes=routes)
When to use decorators:
- When you have many routes in one file
- For cleaner, more readable code
- When routes are closely tied to their view functions
- For dynamic route registration
When to use Route class:
- When defining routes in configuration files
- When routes are defined separately from view functions
- For programmatic route generation
- When working with route metadata
Integration with Guards and Middleware
Decorated routes work seamlessly with the authentication and middleware system:
def admin_guard(ctx: MiddlewareContext) -> bool:
"""Only allow admin users"""
return session.user_role == "admin"
def logging_middleware(ctx: MiddlewareContext) -> bool:
"""Log all navigation"""
print(f"Navigating to: {ctx.path}")
return True
# Routes with guards
@router.route("/admin", protected=True, guard=admin_guard)
def admin_panel(params):
return ft.View(route="/admin", controls=[ft.Text("Admin Panel")])
@router.route("/user/:id", protected=True, guard=user_owns_resource)
def user_profile(params):
return ft.View(route=f"/user/{params.path['id']}", ...)
# Add global middleware
router.use(logging_middleware)
The protected=True parameter automatically applies authentication checks, while custom guard functions provide additional authorization logic.
Async Routes with Decorators
Decorators work perfectly with async view functions for data loading:
import httpx
@router.route("/products", name="products")
def products_view(params):
products_list = ft.Column(scroll="auto")
loading = ft.Text("Loading products...")
async def load_products():
try:
async with httpx.AsyncClient() as client:
response = await client.get("https://api.example.com/products")
products = response.json()
products_list.controls = [
ft.Card(
content=ft.Container(
content=ft.Text(product["name"]),
padding=10
)
) for product in products
]
loading.visible = False
params.page.update()
except Exception as e:
loading.value = f"Error: {e}"
params.page.update()
# Start async loading
params.page.run_task(load_products)
return ft.View(
route="/products",
controls=[
ft.Text("Products", size=30, weight=ft.FontWeight.BOLD),
loading,
products_list,
]
)
API Reference
FletRouter Class
The main router class that manages navigation and routing.
Constructor Parameters
page(Page): The Flet page instanceroutes(List[Route]): List of route definitionsauth_checker(Optional[AuthChecker]): Authentication checker function or booleannot_found_view(Optional[ViewFactory]): Custom 404 viewnot_auth_view(Optional[ViewFactory]): Custom 401 viewnot_auth_redirect_route(Optional[str]): Route to redirect on auth failureinitial_route(Optional[str]): Initial route to navigate toenable_view_cache(bool): Enable view caching (default: False)
Methods
push(path, private_params): Navigate to a new routereplace(path, private_params): Replace current routeback(steps): Go back in navigation historypush_named(name, path_params, private_params): Navigate using route nameuse(middleware): Add middlewareroute(path, **options): Decorator for defining routes (alternative to Route class)
Decorator Usage
The @router.route() decorator provides a clean, Pythonic way to define routes. It's an alternative to creating Route objects manually.
router = FletRouter(page=page)
# Basic route
@router.route("/home")
def home_view(params):
return ft.View(route="/home", controls=[ft.Text("Home Page")])
# Route with parameters
@router.route("/user/:id")
def user_view(params):
user_id = params.path["id"]
return ft.View(route=f"/user/{user_id}", controls=[ft.Text(f"User {user_id}")])
# Protected route with authentication
@router.route("/dashboard", protected=True)
def dashboard_view(params):
return ft.View(route="/dashboard", controls=[ft.Text("Dashboard")])
# Named route for easy navigation
@router.route("/profile", name="user_profile")
def profile_view(params):
return ft.View(route="/profile", controls=[ft.Text("Profile")])
# Route with custom guard
@router.route("/admin", protected=True, guard=admin_guard)
def admin_view(params):
return ft.View(route="/admin", controls=[ft.Text("Admin Panel")])
# Route with preloading for better performance
@router.route("/settings", preload=True)
def settings_view(params):
return ft.View(route="/settings", controls=[ft.Text("Settings")])
# Combined options
@router.route("/user/:id/posts",
name="user_posts",
protected=True,
guard=user_owns_resource)
def user_posts_view(params):
user_id = params.path["id"]
return ft.View(route=f"/user/{user_id}/posts",
controls=[ft.Text(f"Posts by user {user_id}")])
Decorator Parameters
path(str): Route path pattern (required)protected(bool): Requires authentication (default: False)guard(Callable): Custom guard function for additional checkspreload(bool): Preload view on router initialization (default: False)name(str): Named route identifier forpush_named()navigation
Navigation with Named Routes
# Navigate using route names
router.push_named("user_profile")
router.push_named("user_posts", path_params={"id": "123"})
Route Class
Defines a route configuration.
Parameters
path(str): Route path pattern (e.g., "/user/:id")view(ViewFactory): View factory functionprotected(bool): Whether route requires authenticationguard(Optional[Callable]): Custom guard functionpreload(bool): Whether to preload the viewname(Optional[str]): Named route identifier
Params Class
Container for route parameters.
Attributes
path(Dict[str, Any]): URL path parametersprivate(Dict[str, Any]): Encrypted private parametersrouter(FletRouter): Router instance
Examples
The examples directory contains sample applications demonstrating various features:
Complete Demo Application
The examples/example_app.py provides a comprehensive demonstration of Flet Router's capabilities:
- Authentication Integration: Login/logout with DummyJSON API
- Protected Routes: User profiles and cart pages with authentication guards
- Route Guards: Custom logic to verify user permissions (e.g., users can only view their own profiles)
- Middleware: Global authentication middleware
- Named Routes: Navigation using human-readable route names
- Async Data Loading: Fetching data from APIs with httpx
- Session Management: Persistent user sessions across navigation
- Error Handling: Proper error states and user feedback
- Real-world UI: Cards, images, avatars, and responsive layouts
Running the Example
# Install additional dependencies for the example
pip install httpx
# Run the example application
python examples/example_app.py
Demo Credentials:
- Username:
kminchelle - Password:
0lelplR
The example includes:
- Public pages (home, products)
- Protected pages (user profile, cart)
- Authentication flow with redirects
- API integration with real data
- Responsive design with dark theme
Basic Examples
- Basic navigation between pages
- Route parameters and query strings
- Authentication flows with guards
- Middleware usage for logging and validation
- Named routes for cleaner navigation
- Dynamic page creation with data fetching
Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
git clone https://github.com/MasterA5/flet_router.git
cd flet_router
pip install -e .[dev]
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For questions or suggestions, please open an issue on GitHub.
Changelog
v0.1.0
- Initial release
- Basic routing functionality
- Authentication support with guards and middleware
- Route parameters and private parameters with encryption
- View caching and preloading
- Named routes for cleaner navigation
- Session management
- Async support for data loading
- Comprehensive example application with DummyJSON API integration
- Type safety with full type hints
Acknowledgments
Built with Flet - A framework for building beautiful multi-platform apps with Python.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flet_routing-1.0.0.tar.gz.
File metadata
- Download URL: flet_routing-1.0.0.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c9a014886e956e236e9382696cf9e3d2e8fafeb614bf99f04933a578ef4fc49
|
|
| MD5 |
fe054959f37dd10f11eb8c9d391d878c
|
|
| BLAKE2b-256 |
a46c8673537a8a63286572ab8392793dbb72bda6c372ceefc27a439810d94471
|
Provenance
The following attestation bundles were made for flet_routing-1.0.0.tar.gz:
Publisher:
pypi.yml on MasterA5/flet-router
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flet_routing-1.0.0.tar.gz -
Subject digest:
3c9a014886e956e236e9382696cf9e3d2e8fafeb614bf99f04933a578ef4fc49 - Sigstore transparency entry: 2038369861
- Sigstore integration time:
-
Permalink:
MasterA5/flet-router@fca6860bf71dc2eefd4f808e1512827a9d85286b -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/MasterA5
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@fca6860bf71dc2eefd4f808e1512827a9d85286b -
Trigger Event:
push
-
Statement type:
File details
Details for the file flet_routing-1.0.0-py3-none-any.whl.
File metadata
- Download URL: flet_routing-1.0.0-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fba549e94212d0725af06a450a048235026792263aa2105355b4fef1687424f7
|
|
| MD5 |
64f8fb98b65cb998dc9b88917d2ed100
|
|
| BLAKE2b-256 |
980e6b713c76461c7313150cc44e636cd1c4b826d4876eb1c4d6a018ea10682e
|
Provenance
The following attestation bundles were made for flet_routing-1.0.0-py3-none-any.whl:
Publisher:
pypi.yml on MasterA5/flet-router
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flet_routing-1.0.0-py3-none-any.whl -
Subject digest:
fba549e94212d0725af06a450a048235026792263aa2105355b4fef1687424f7 - Sigstore transparency entry: 2038371440
- Sigstore integration time:
-
Permalink:
MasterA5/flet-router@fca6860bf71dc2eefd4f808e1512827a9d85286b -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/MasterA5
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@fca6860bf71dc2eefd4f808e1512827a9d85286b -
Trigger Event:
push
-
Statement type: