Page Management (Pagesy)¶
The Pagesy class is the foundation for defining pages in Flet-Easy applications. It provides comprehensive configuration options for routing, middleware, authentication, caching, and parameter validation.
Overview¶
Pagesy enables you to:
- Define Routes: Create URL patterns with dynamic parameters
- Configure Security: Set up route protection and authentication
- Add Middleware: Implement request/response processing
- Enable Caching: Preserve page state during navigation
- Validate Parameters: Custom validation for route parameters
- Share Data: Enable cross-page data sharing
Class Definition¶
class Pagesy:
def __init__(
self,
route: str,
view: ViewHandler,
title: Optional[str] = None,
index: Optional[int] = None,
clear: bool = False,
share_data: bool = False,
protected_route: bool = False,
custom_params: Optional[Dict[str, Callable[[], bool]]] = None,
middleware: Optional[Middleware] = None,
cache: bool = False
)
Parameters¶
route (Required)¶
- Type:
str - Description: URL pattern for the page, supports dynamic parameters
Static Routes:
# Simple static route
Pagesy("/home", home_view)
Pagesy("/about", about_view)
Pagesy("/contact", contact_view)
Dynamic Routes:
# Integer parameter
Pagesy("/user/{id:int}", user_view)
# String parameter
Pagesy("/category/{name:str}", category_view)
# Multiple parameters
Pagesy("/blog/{year:int}/{month:int}/{slug:str}", blog_post_view)
# Optional parameters with defaults
Pagesy("/search/{query:str}/{page:int}", search_view)
Parameter Types:
{name:int}- Integer{name:str}- String{name:str}- Lowercase string{name:float}- Float
view (Required)¶
- Type:
ViewHandler(Callable[[Datasy], View]) - Description: Function that returns a Flet View
def my_page_view(data: fs.Datasy) -> ft.View:
return ft.View(
controls=[
ft.Text("Hello World!")
]
)
# Register the page
page = Pagesy("/my-page", my_page_view)
title¶
- Type:
Optional[str] - Default:
None - Description: Page title displayed in browser/window title bar
Pagesy("/dashboard", dashboard_view, title="User Dashboard")
Pagesy("/settings", settings_view, title="Application Settings")
index¶
New in v0.3.0
- Type:
Optional[int] - Default:
None - Description: Navigation index for use with
ft.NavigationBarandft.CupertinoNavigationBar
# Define pages with navigation indices
Pagesy("/home", home_view, title="Home", index=0)
Pagesy("/search", search_view, title="Search", index=1)
Pagesy("/profile", profile_view, title="Profile", index=2)
# Use with NavigationBar
@app.view
def main_view(data: fs.Datasy):
return fs.Viewsy(
navigation_bar=ft.NavigationBar(
destinations=[
ft.NavigationBarDestination(icon=ft.Icons.HOME, label="Home"),
ft.NavigationBarDestination(icon=ft.Icons.SEARCH, label="Search"),
ft.NavigationBarDestination(icon=ft.Icons.PERSON, label="Profile"),
],
on_change=data.go_navigation_bar # Automatically handles index mapping
)
)
cache¶
New in v0.3.0
- Type:
bool - Default:
False - Description: Preserve page state when navigating. Controls retain their values instead of resetting
# Enable caching - page state preserved
Pagesy("/form", form_view, cache=True)
# Disable caching - page resets on each visit (default)
Pagesy("/live-feed", feed_view, cache=False)
# Example: Form with caching
@app.page("/user-form", cache=True)
def user_form(data: fs.Datasy):
# These fields will retain their values when navigating away and back
name_field = ft.TextField(label="Name")
email_field = ft.TextField(label="Email")
return ft.View(
controls=[
ft.Text("User Information Form"),
name_field,
email_field,
ft.ElevatedButton("Save Draft", on_click=lambda _: data.go("/dashboard")),
ft.ElevatedButton("Reset", on_click=lambda _: data.page_reload())
]
)
clear¶
- Type:
bool - Default:
False - Description: Remove pages from the
page.viewslist of Flet (deprecated - use with caution)
# Clear navigation history when accessing this page
Pagesy("/login", login_view, clear=True)
Pagesy("/profile", profile_view, index=2)
# Use in NavigationBar
navigation_bar = ft.NavigationBar(
selected_index=data.current_page_index, # Use the index
destinations=[
ft.NavigationDestination(icon=ft.Icons.HOME, label="Home"),
ft.NavigationDestination(icon=ft.Icons.SEARCH, label="Search"),
ft.NavigationDestination(icon=ft.Icons.PERSON, label="Profile"),
]
)
clear¶
- Type:
bool - Default:
False - Description: Clears navigation history when navigating to this page
# Clear history for main landing pages
Pagesy("/home", home_view, clear=True)
Pagesy("/login", login_view, clear=True)
# Preserve history for detail pages
Pagesy("/user/{id:int}", user_detail_view, clear=False)
share_data¶
- Type:
bool - Default:
False - Description: Enables data sharing between pages using session storage
# Enable data sharing for form wizard pages
Pagesy("/wizard/step1", step1_view, share_data=True)
Pagesy("/wizard/step2", step2_view, share_data=True)
Pagesy("/wizard/step3", step3_view, share_data=True)
def step1_view(data: fs.Datasy):
def save_and_continue(_):
# Save form data
data.share.set("user_info", {
"name": name_field.value,
"email": email_field.value
})
data.go("/wizard/step2")
return ft.View("/wizard/step1", controls=[...])
def step2_view(data: fs.Datasy):
# Access data from step1
user_info = data.share.get("user_info")
return ft.View("/wizard/step2", controls=[...])
protected_route¶
- Type:
bool - Default:
False - Description: Requires authentication to access the page
# Public pages
Pagesy("/home", home_view, protected_route=False)
Pagesy("/login", login_view, protected_route=False)
# Protected pages
Pagesy("/dashboard", dashboard_view, protected_route=True)
Pagesy("/admin", admin_view, protected_route=True)
Pagesy("/user/{id:int}/edit", edit_user_view, protected_route=True)
# Configure authentication handler in FletEasy
@app.login
def check_auth(data: fs.Datasy):
token = data.page.client_storage.get("auth_token")
return token is not None and verify_token(token)
custom_params¶
- Type:
Optional[Dict[str, Callable[[], bool]]] - Default:
None - Description: Custom validators for route parameters
def validate_user_id(user_id: str) -> bool:
"""Validate that user ID exists in database"""
return user_exists(int(user_id))
def validate_email(email: str) -> bool:
"""Validate email format"""
import re
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
def validate_date(date_str: str) -> bool:
"""Validate date format YYYY-MM-DD"""
try:
datetime.strptime(date_str, "%Y-%m-%d")
return True
except ValueError:
return False
# Apply custom validators
Pagesy(
"/user/{id:id_profile}/profile",
user_profile_view,
custom_params={"id_profile": validate_user_id}
)
Pagesy(
"/contact/{email:email_contact}",
contact_view,
custom_params={"email_contact": validate_email}
)
Pagesy(
"/events/{date:date_event}",
events_view,
custom_params={"date_event": validate_date}
)
middleware¶
- Type:
Optional[Middleware] - Description: Request/response processing pipeline
Function-based Middleware:
def auth_middleware(data: fs.Datasy) -> Optional[fs.Redirect]:
"""Check if user is authenticated"""
if not data.page.client_storage.get("auth_token"):
return fs.Redirect("/login")
return None
def admin_middleware(data: fs.Datasy) -> Optional[fs.Redirect]:
"""Check if user has admin privileges"""
user_role = data.page.client_storage.get("user_role")
if user_role != "admin":
return fs.Redirect("/unauthorized")
return None
# Apply middleware
Pagesy(
"/admin/users",
admin_users_view,
middleware=[auth_middleware, admin_middleware]
)
Class-based Middleware:
class LoggingMiddleware(fs.MiddlewareRequest):
def before_request(self):
print(f"Accessing route: {self.data.route}")
# Log request details
def after_request(self):
print(f"Finished processing: {self.data.route}")
# Log response details
class RateLimitMiddleware(fs.MiddlewareRequest):
def before_request(self):
user_id = self.data.page.client_storage.get("user_id")
if is_rate_limited(user_id):
return fs.Redirect("/rate-limited")
# Apply class-based middleware
Pagesy(
"/api/data",
api_data_view,
middleware=[LoggingMiddleware, RateLimitMiddleware]
)
cache¶
- Type:
bool - Default:
False - Description: Preserves page state during navigation
# Cache expensive-to-render pages
Pagesy("/reports/dashboard", title="Dashboard", view=reports_view, cache=True)
Pagesy("/data/visualization", title="Visualization", view=charts_view, cache=True)
# Don't cache dynamic pages
Pagesy("/live/feed", title="Live Feed", view=live_feed_view, cache=False)
Pagesy("/user/{id:int}/messages", title="Messages", view=messages_view, cache=False)
def reports_view(data: fs.Datasy):
# This page state will be preserved when user navigates away and back
expensive_chart = generate_complex_chart()
return ft.View(
controls=[expensive_chart]
)
Complete Examples¶
Multi-step Form with Data Sharing¶
This example demonstrates how to create a multi-step registration form where data persists across different pages using share_data=True. This is ideal for wizards, checkout processes, or any workflow that requires multiple steps.
🎯 What You'll Learn¶
- Share data between pages using
data.share - Validate and persist form data across navigation
- Implement navigation guards to prevent skipping steps
- Handle back navigation while preserving user input
📋 How It Works¶
The form has 3 steps:
- Step 1: Collect personal information (name, email, phone)
- Step 2: Collect address information (address, city)
- Step 3: Display a summary of all collected data
All data is stored in data.share with the key "registration_form", making it accessible across all pages.
💡 Key Concepts¶
data.share.get(key): Retrieves shared data by key. Returns None if not found.
data.share.set(key, value): Stores data that persists across page navigation.
data.share.clear(): Removes all shared data (useful for resetting the form).
share_data=True: Must be enabled in Pagesy to use shared data functionality.
import flet as ft
import flet_easy as fs
# ========================================
# STEP 1: Personal Information
# ========================================
def step1_view(data: fs.Datasy):
"""First step: Collect user's personal information"""
# 1. Load existing data (if user navigates back to this step)
form_data = data.share.get("registration_form") or {}
# 2. Create text fields with saved values (if any)
name_field = ft.TextField(
label="Full Name",
value=form_data.get("name", "")
)
email_field = ft.TextField(
label="Email",
value=form_data.get("email", "")
)
phone_field = ft.TextField(
label="Phone",
value=form_data.get("phone", "")
)
def save_and_continue(_):
"""Validate, save data, and navigate to next step"""
# Validate required fields
if not name_field.value or not email_field.value:
data.page.open(
ft.SnackBar(content=ft.Text("Please fill all required fields"))
)
return
# Get current form data and update with new values
form_data = data.share.get("registration_form") or {}
form_data.update({
"name": name_field.value,
"email": email_field.value,
"phone": phone_field.value
})
# Save to shared storage
data.share.set("registration_form", form_data)
# Navigate to step 2
data.go_route("/register/step2")
def reload_page(e):
"""Clear all form data and reload the page"""
data.share.clear()
data.page_reload()
return ft.View(
controls=[
ft.Text("Registration - Step 1", size=24, weight="bold"),
ft.Text("Personal Information", size=16, color="grey"),
ft.Divider(height=20),
name_field,
email_field,
phone_field,
ft.Row(
[
ft.ElevatedButton("Next →", on_click=save_and_continue),
ft.OutlinedButton("Reset", on_click=reload_page),
],
alignment="center",
),
],
vertical_alignment="center",
horizontal_alignment="center",
padding=20,
)
# ========================================
# STEP 2: Address Information
# ========================================
def step2_view(data: fs.Datasy):
"""Second step: Collect user's address information"""
# 1. Load form data
form_data = data.share.get("registration_form") or {}
# 2. Navigation guard: Redirect to step 1 if not completed
if not form_data.get("name"):
data.go_route("/register/step1")
return ft.View("/register/step2", controls=[])
# 3. Create address fields with saved values
address_field = ft.TextField(
label="Address",
value=form_data.get("address", "")
)
city_field = ft.TextField(
label="City",
value=form_data.get("city", "")
)
def save_and_continue(_):
"""Save address data and go to confirmation"""
form_data = data.share.get("registration_form") or {}
form_data.update({
"address": address_field.value,
"city": city_field.value
})
data.share.set("registration_form", form_data)
data.go_route("/register/step3")
def go_back(_):
"""Save current data before going back to step 1"""
form_data = data.share.get("registration_form") or {}
form_data.update({
"address": address_field.value,
"city": city_field.value
})
data.share.set("registration_form", form_data)
data.go_route("/register/step1")
return ft.View(
controls=[
ft.Text("Registration - Step 2", size=24, weight="bold"),
ft.Text("Address Information", size=16, color="grey"),
ft.Divider(height=20),
address_field,
city_field,
ft.Row(
[
ft.OutlinedButton("← Back", on_click=go_back),
ft.ElevatedButton("Next →", on_click=save_and_continue),
],
alignment="center",
),
],
vertical_alignment="center",
horizontal_alignment="center",
padding=20,
)
# ========================================
# STEP 3: Confirmation
# ========================================
def step3_view(data: fs.Datasy):
"""Final step: Display summary of all collected data"""
# Load all form data
form_data = data.share.get("registration_form") or {}
return ft.View(
controls=[
ft.Text("Registration - Step 3", size=24, weight="bold"),
ft.Text("Confirmation", size=16, color="grey"),
ft.Divider(height=20),
# Display collected information
ft.Container(
content=ft.Column([
ft.Text("📋 Summary", size=18, weight="bold"),
ft.Divider(),
ft.Text(f"Name: {form_data.get('name', 'N/A')}"),
ft.Text(f"Email: {form_data.get('email', 'N/A')}"),
ft.Text(f"Phone: {form_data.get('phone', 'N/A')}"),
ft.Text(f"Address: {form_data.get('address', 'N/A')}"),
ft.Text(f"City: {form_data.get('city', 'N/A')}"),
]),
padding=20,
border=ft.border.all(1, "grey"),
border_radius=10,
),
ft.Row(
[
ft.OutlinedButton("← Back", on_click=data.go_back),
ft.ElevatedButton("🏠 Home", on_click=data.go(data.route_init)),
],
alignment="center",
),
],
vertical_alignment="center",
horizontal_alignment="center",
padding=20,
)
# ========================================
# MAIN APPLICATION
# ========================================
def main(page: ft.Page):
"""Initialize and configure the multi-step form application"""
# Create app starting at step 1
app = fs.FletEasy(route_init="/register/step1")
# Register all steps with share_data=True
app.add_routes([
fs.Pagesy(
"/register/step1",
step1_view,
title="Registration - Personal Info",
share_data=True, # Enable data sharing
),
fs.Pagesy(
"/register/step2",
step2_view,
title="Registration - Address",
share_data=True, # Enable data sharing
),
fs.Pagesy(
"/register/step3",
step3_view,
title="Registration - Confirmation",
share_data=True, # Enable data sharing
),
])
# Start the application
app.start(page)
# Run the app
ft.app(target=main)
🎬 Demo¶
The Pagesy class provides the foundation for creating sophisticated, secure, and user-friendly page routing in Flet-Easy applications. Use these features to build robust navigation systems that enhance your application's functionality and user experience.