flet-routing 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,617 @@
1
+ Metadata-Version: 2.4
2
+ Name: flet-routing
3
+ Version: 1.0.0
4
+ Summary: A powerful routing library for Flet applications with authentication, middleware, and type safety
5
+ Author-email: MasterA5 <agustin090806090806@gmail.com>
6
+ Requires-Python: >=3.13
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: flet==0.28.3
9
+ Requires-Dist: cryptography
10
+
11
+ # Flet Router
12
+
13
+ [![PyPI version](https://badge.fury.io/py/flet-router.svg)](https://pypi.org/project/flet-router/)
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
15
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
16
+
17
+ 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.
18
+
19
+ ## Overview
20
+
21
+ 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.
22
+
23
+ ## Features
24
+
25
+ - **Declarative Routing**: Define routes using a clean, intuitive API
26
+ - **Page Navigation**: Seamless transitions between different pages and views
27
+ - **Route Parameters**: Support for dynamic URL parameters with automatic type casting
28
+ - **Query Parameters**: Handle URL query strings
29
+ - **Private Parameters**: Securely pass sensitive data between routes using encryption
30
+ - **Deep Linking**: Navigate directly to specific routes with parameters
31
+ - **History Management**: Built-in back/forward navigation support with stack management
32
+ - **Authentication & Guards**: Protect routes with authentication checks and custom guards
33
+ - **Middleware Support**: Execute custom logic before route transitions
34
+ - **Named Routes**: Navigate using human-readable route names
35
+ - **Route Preloading**: Preload views for improved performance
36
+ - **View Caching**: Optional caching system for frequently accessed views
37
+ - **Session Management**: Built-in session handling for user state
38
+ - **Async Support**: Full support for asynchronous operations and data loading
39
+ - **Type Safety**: Full type hints for better IDE support and error detection
40
+ - **Lightweight**: Minimal dependencies and overhead
41
+ - **Extensible**: Plugin system with middleware and guards
42
+
43
+ ## Installation
44
+
45
+ Install Flet Router using pip:
46
+
47
+ ```bash
48
+ pip install flet-router
49
+ ```
50
+
51
+ ### Requirements
52
+
53
+ - Python 3.9+
54
+ - Flet 0.28.3+
55
+
56
+ ### Async Support
57
+
58
+ Flet Router fully supports asynchronous operations in your route handlers and middleware. Use `page.run_task()` or `asyncio.run()` for async data loading:
59
+
60
+ ```python
61
+ import httpx
62
+ import asyncio
63
+
64
+ @router.route("/products")
65
+ def products_view(params):
66
+ products_list = ft.Column()
67
+
68
+ async def load_products():
69
+ async with httpx.AsyncClient() as client:
70
+ response = await client.get("https://api.example.com/products")
71
+ products = response.json()
72
+ # Update UI with loaded data
73
+ products_list.controls = [ft.Text(p["name"]) for p in products]
74
+ params.page.update()
75
+
76
+ # Start async task
77
+ params.page.run_task(load_products)
78
+
79
+ return ft.View(
80
+ route="/products",
81
+ controls=[
82
+ ft.Text("Products", size=30),
83
+ products_list,
84
+ ]
85
+ )
86
+ ```
87
+
88
+ ## Quick Start
89
+
90
+ ### Basic Usage with Decorators
91
+
92
+ ```python
93
+ import flet as ft
94
+ from flet_router import FletRouter
95
+
96
+ def main(page: ft.Page):
97
+ router = FletRouter(page=page)
98
+
99
+ @router.route("/")
100
+ def home_view(params):
101
+ return ft.View(
102
+ route="/",
103
+ controls=[
104
+ ft.Text("Home Page", size=30),
105
+ ft.ElevatedButton(
106
+ "Go to About",
107
+ on_click=lambda e: router.push("/about")
108
+ )
109
+ ]
110
+ )
111
+
112
+ @router.route("/about")
113
+ def about_view(params):
114
+ return ft.View(
115
+ route="/about",
116
+ controls=[
117
+ ft.Text("About Page", size=30),
118
+ ft.ElevatedButton(
119
+ "Go Back",
120
+ on_click=lambda e: router.back()
121
+ )
122
+ ]
123
+ )
124
+
125
+ router.push("/")
126
+
127
+ ft.app(target=main)
128
+ ```
129
+
130
+ ### Alternative: Using Route Class
131
+
132
+ ```python
133
+ from flet_router import FletRouter, Route
134
+
135
+ def main(page: ft.Page):
136
+ def home_view(params):
137
+ return ft.View(route="/", controls=[ft.Text("Home Page")])
138
+
139
+ def about_view(params):
140
+ return ft.View(route="/about", controls=[ft.Text("About Page")])
141
+
142
+ router = FletRouter(
143
+ page=page,
144
+ routes=[
145
+ Route("/", home_view),
146
+ Route("/about", about_view),
147
+ ]
148
+ )
149
+
150
+ router.push("/")
151
+
152
+ ft.app(target=main)
153
+ ```
154
+
155
+ ### Route Parameters
156
+
157
+ ```python
158
+ def user_view(params):
159
+ user_id = params.path["id"]
160
+ return ft.View(
161
+ route="/user/:id",
162
+ controls=[
163
+ ft.Text(f"User Profile: {user_id}", size=30),
164
+ ft.ElevatedButton("Back", on_click=lambda e: params.router.back())
165
+ ]
166
+ )
167
+
168
+ router = FletRouter(
169
+ page=page,
170
+ routes=[
171
+ Route("/user/:id", user_view),
172
+ ]
173
+ )
174
+
175
+ # Navigate with parameters
176
+ router.push("/user/123")
177
+ ```
178
+
179
+ ### Authentication & Protected Routes
180
+
181
+ ```python
182
+ import flet as ft
183
+ from flet_router import FletRouter, Route, MiddlewareContext
184
+
185
+ # Session state
186
+ class Session:
187
+ def __init__(self):
188
+ self.authenticated = False
189
+ self.user = None
190
+
191
+ session = Session()
192
+
193
+ # Authentication middleware
194
+ def auth_middleware(ctx: MiddlewareContext) -> bool:
195
+ if ctx.route and ctx.route.protected and not session.authenticated:
196
+ ctx.router.replace("/login", {"redirect": ctx.full_path})
197
+ return False
198
+ return True
199
+
200
+ # Custom guard for user profiles
201
+ def user_profile_guard(ctx: MiddlewareContext) -> bool:
202
+ if not session.authenticated:
203
+ return False
204
+ # Users can only view their own profile
205
+ user_id = ctx.params.path.get("id")
206
+ return str(session.user_id) == str(user_id)
207
+
208
+ def login_view(params):
209
+ username_field = ft.TextField(label="Username")
210
+ password_field = ft.TextField(label="Password", password=True)
211
+
212
+ def do_login(e):
213
+ # Your authentication logic here
214
+ session.authenticated = True
215
+ session.user = username_field.value
216
+ # Redirect to intended page or home
217
+ redirect_to = params.private.get("redirect", "/")
218
+ params.router.replace(redirect_to)
219
+
220
+ return ft.View(
221
+ route="/login",
222
+ controls=[
223
+ ft.Text("Login", size=30),
224
+ username_field,
225
+ password_field,
226
+ ft.ElevatedButton("Login", on_click=do_login),
227
+ ]
228
+ )
229
+
230
+ def dashboard_view(params):
231
+ return ft.View(
232
+ route="/dashboard",
233
+ controls=[
234
+ ft.Text(f"Welcome, {session.user}!", size=30),
235
+ ft.ElevatedButton("Logout", on_click=lambda e: logout_and_redirect(params.router)),
236
+ ]
237
+ )
238
+
239
+ def user_profile_view(params):
240
+ user_id = params.path["id"]
241
+ return ft.View(
242
+ route=f"/user/{user_id}",
243
+ controls=[
244
+ ft.Text(f"Profile for user {user_id}", size=30),
245
+ ]
246
+ )
247
+
248
+ def logout_and_redirect(router):
249
+ session.authenticated = False
250
+ session.user = None
251
+ router.replace("/")
252
+
253
+ router = FletRouter(
254
+ page=page,
255
+ routes=[
256
+ Route("/login", login_view),
257
+ Route("/dashboard", dashboard_view, protected=True),
258
+ Route("/user/:id", user_profile_view, protected=True, guard=user_profile_guard),
259
+ ],
260
+ auth_checker=lambda: session.authenticated,
261
+ not_auth_redirect_route="/login"
262
+ )
263
+
264
+ router.use(auth_middleware)
265
+ ```
266
+
267
+ ### Middleware
268
+
269
+ ```python
270
+ def logging_middleware(ctx):
271
+ print(f"Navigating to: {ctx.path}")
272
+ return True # Continue navigation
273
+
274
+ def auth_middleware(ctx):
275
+ if ctx.route and ctx.route.protected and not ctx.router.checker():
276
+ return False # Block navigation
277
+ return True
278
+
279
+ router = FletRouter(page=page, routes=routes)
280
+ router.use(logging_middleware)
281
+ router.use(auth_middleware)
282
+ ```
283
+
284
+ ### Named Routes
285
+
286
+ ```python
287
+ router = FletRouter(
288
+ page=page,
289
+ routes=[
290
+ Route("/user/:id", user_view, name="user_profile"),
291
+ ]
292
+ )
293
+
294
+ # Navigate using name
295
+ router.push_named("user_profile", path_params={"id": "123"})
296
+ ```
297
+
298
+ #### Decorators vs Route Class
299
+
300
+ You can define routes using either decorators or the `Route` class. Both approaches are equivalent:
301
+
302
+ **Using Decorators (Recommended):**
303
+ ```python
304
+ @router.route("/user/:id", protected=True, name="user_profile")
305
+ def user_view(params):
306
+ return ft.View(route=f"/user/{params.path['id']}", ...)
307
+ ```
308
+
309
+ **Using Route Class:**
310
+ ```python
311
+ def user_view(params):
312
+ return ft.View(route=f"/user/{params.path['id']}", ...)
313
+
314
+ routes = [
315
+ Route("/user/:id", user_view, protected=True, name="user_profile"),
316
+ ]
317
+
318
+ router = FletRouter(page=page, routes=routes)
319
+ ```
320
+
321
+ **When to use decorators:**
322
+ - When you have many routes in one file
323
+ - For cleaner, more readable code
324
+ - When routes are closely tied to their view functions
325
+ - For dynamic route registration
326
+
327
+ **When to use Route class:**
328
+ - When defining routes in configuration files
329
+ - When routes are defined separately from view functions
330
+ - For programmatic route generation
331
+ - When working with route metadata
332
+
333
+ #### Integration with Guards and Middleware
334
+
335
+ Decorated routes work seamlessly with the authentication and middleware system:
336
+
337
+ ```python
338
+ def admin_guard(ctx: MiddlewareContext) -> bool:
339
+ """Only allow admin users"""
340
+ return session.user_role == "admin"
341
+
342
+ def logging_middleware(ctx: MiddlewareContext) -> bool:
343
+ """Log all navigation"""
344
+ print(f"Navigating to: {ctx.path}")
345
+ return True
346
+
347
+ # Routes with guards
348
+ @router.route("/admin", protected=True, guard=admin_guard)
349
+ def admin_panel(params):
350
+ return ft.View(route="/admin", controls=[ft.Text("Admin Panel")])
351
+
352
+ @router.route("/user/:id", protected=True, guard=user_owns_resource)
353
+ def user_profile(params):
354
+ return ft.View(route=f"/user/{params.path['id']}", ...)
355
+
356
+ # Add global middleware
357
+ router.use(logging_middleware)
358
+ ```
359
+
360
+ The `protected=True` parameter automatically applies authentication checks, while custom `guard` functions provide additional authorization logic.
361
+
362
+ #### Async Routes with Decorators
363
+
364
+ Decorators work perfectly with async view functions for data loading:
365
+
366
+ ```python
367
+ import httpx
368
+
369
+ @router.route("/products", name="products")
370
+ def products_view(params):
371
+ products_list = ft.Column(scroll="auto")
372
+ loading = ft.Text("Loading products...")
373
+
374
+ async def load_products():
375
+ try:
376
+ async with httpx.AsyncClient() as client:
377
+ response = await client.get("https://api.example.com/products")
378
+ products = response.json()
379
+
380
+ products_list.controls = [
381
+ ft.Card(
382
+ content=ft.Container(
383
+ content=ft.Text(product["name"]),
384
+ padding=10
385
+ )
386
+ ) for product in products
387
+ ]
388
+ loading.visible = False
389
+ params.page.update()
390
+ except Exception as e:
391
+ loading.value = f"Error: {e}"
392
+ params.page.update()
393
+
394
+ # Start async loading
395
+ params.page.run_task(load_products)
396
+
397
+ return ft.View(
398
+ route="/products",
399
+ controls=[
400
+ ft.Text("Products", size=30, weight=ft.FontWeight.BOLD),
401
+ loading,
402
+ products_list,
403
+ ]
404
+ )
405
+ ```
406
+
407
+ ## API Reference
408
+
409
+ ### FletRouter Class
410
+
411
+ The main router class that manages navigation and routing.
412
+
413
+ #### Constructor Parameters
414
+
415
+ - `page` (Page): The Flet page instance
416
+ - `routes` (List[Route]): List of route definitions
417
+ - `auth_checker` (Optional[AuthChecker]): Authentication checker function or boolean
418
+ - `not_found_view` (Optional[ViewFactory]): Custom 404 view
419
+ - `not_auth_view` (Optional[ViewFactory]): Custom 401 view
420
+ - `not_auth_redirect_route` (Optional[str]): Route to redirect on auth failure
421
+ - `initial_route` (Optional[str]): Initial route to navigate to
422
+ - `enable_view_cache` (bool): Enable view caching (default: False)
423
+
424
+ #### Methods
425
+
426
+ - `push(path, private_params)`: Navigate to a new route
427
+ - `replace(path, private_params)`: Replace current route
428
+ - `back(steps)`: Go back in navigation history
429
+ - `push_named(name, path_params, private_params)`: Navigate using route name
430
+ - `use(middleware)`: Add middleware
431
+ - `route(path, **options)`: Decorator for defining routes (alternative to Route class)
432
+
433
+ #### Decorator Usage
434
+
435
+ The `@router.route()` decorator provides a clean, Pythonic way to define routes. It's an alternative to creating `Route` objects manually.
436
+
437
+ ```python
438
+ router = FletRouter(page=page)
439
+
440
+ # Basic route
441
+ @router.route("/home")
442
+ def home_view(params):
443
+ return ft.View(route="/home", controls=[ft.Text("Home Page")])
444
+
445
+ # Route with parameters
446
+ @router.route("/user/:id")
447
+ def user_view(params):
448
+ user_id = params.path["id"]
449
+ return ft.View(route=f"/user/{user_id}", controls=[ft.Text(f"User {user_id}")])
450
+
451
+ # Protected route with authentication
452
+ @router.route("/dashboard", protected=True)
453
+ def dashboard_view(params):
454
+ return ft.View(route="/dashboard", controls=[ft.Text("Dashboard")])
455
+
456
+ # Named route for easy navigation
457
+ @router.route("/profile", name="user_profile")
458
+ def profile_view(params):
459
+ return ft.View(route="/profile", controls=[ft.Text("Profile")])
460
+
461
+ # Route with custom guard
462
+ @router.route("/admin", protected=True, guard=admin_guard)
463
+ def admin_view(params):
464
+ return ft.View(route="/admin", controls=[ft.Text("Admin Panel")])
465
+
466
+ # Route with preloading for better performance
467
+ @router.route("/settings", preload=True)
468
+ def settings_view(params):
469
+ return ft.View(route="/settings", controls=[ft.Text("Settings")])
470
+
471
+ # Combined options
472
+ @router.route("/user/:id/posts",
473
+ name="user_posts",
474
+ protected=True,
475
+ guard=user_owns_resource)
476
+ def user_posts_view(params):
477
+ user_id = params.path["id"]
478
+ return ft.View(route=f"/user/{user_id}/posts",
479
+ controls=[ft.Text(f"Posts by user {user_id}")])
480
+ ```
481
+
482
+ #### Decorator Parameters
483
+
484
+ - `path` (str): Route path pattern (required)
485
+ - `protected` (bool): Requires authentication (default: False)
486
+ - `guard` (Callable): Custom guard function for additional checks
487
+ - `preload` (bool): Preload view on router initialization (default: False)
488
+ - `name` (str): Named route identifier for `push_named()` navigation
489
+
490
+ #### Navigation with Named Routes
491
+
492
+ ```python
493
+ # Navigate using route names
494
+ router.push_named("user_profile")
495
+ router.push_named("user_posts", path_params={"id": "123"})
496
+ ```
497
+
498
+ ### Route Class
499
+
500
+ Defines a route configuration.
501
+
502
+ #### Parameters
503
+
504
+ - `path` (str): Route path pattern (e.g., "/user/:id")
505
+ - `view` (ViewFactory): View factory function
506
+ - `protected` (bool): Whether route requires authentication
507
+ - `guard` (Optional[Callable]): Custom guard function
508
+ - `preload` (bool): Whether to preload the view
509
+ - `name` (Optional[str]): Named route identifier
510
+
511
+ ### Params Class
512
+
513
+ Container for route parameters.
514
+
515
+ #### Attributes
516
+
517
+ - `path` (Dict[str, Any]): URL path parameters
518
+ - `private` (Dict[str, Any]): Encrypted private parameters
519
+ - `router` (FletRouter): Router instance
520
+
521
+ ## Examples
522
+
523
+ The `examples` directory contains sample applications demonstrating various features:
524
+
525
+ ### Complete Demo Application
526
+
527
+ The `examples/example_app.py` provides a comprehensive demonstration of Flet Router's capabilities:
528
+
529
+ - **Authentication Integration**: Login/logout with DummyJSON API
530
+ - **Protected Routes**: User profiles and cart pages with authentication guards
531
+ - **Route Guards**: Custom logic to verify user permissions (e.g., users can only view their own profiles)
532
+ - **Middleware**: Global authentication middleware
533
+ - **Named Routes**: Navigation using human-readable route names
534
+ - **Async Data Loading**: Fetching data from APIs with httpx
535
+ - **Session Management**: Persistent user sessions across navigation
536
+ - **Error Handling**: Proper error states and user feedback
537
+ - **Real-world UI**: Cards, images, avatars, and responsive layouts
538
+
539
+ #### Running the Example
540
+
541
+ ```bash
542
+ # Install additional dependencies for the example
543
+ pip install httpx
544
+
545
+ # Run the example application
546
+ python examples/example_app.py
547
+ ```
548
+
549
+ **Demo Credentials:**
550
+ - Username: `kminchelle`
551
+ - Password: `0lelplR`
552
+
553
+ The example includes:
554
+ - Public pages (home, products)
555
+ - Protected pages (user profile, cart)
556
+ - Authentication flow with redirects
557
+ - API integration with real data
558
+ - Responsive design with dark theme
559
+
560
+ ### Basic Examples
561
+
562
+ - Basic navigation between pages
563
+ - Route parameters and query strings
564
+ - Authentication flows with guards
565
+ - Middleware usage for logging and validation
566
+ - Named routes for cleaner navigation
567
+ - Dynamic page creation with data fetching
568
+
569
+ ## Contributing
570
+
571
+ We welcome contributions! Please follow these steps:
572
+
573
+ 1. Fork the repository
574
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
575
+ 3. Write tests for your changes
576
+ 4. Ensure all tests pass
577
+ 5. Commit your changes (`git commit -m 'Add amazing feature'`)
578
+ 6. Push to the branch (`git push origin feature/amazing-feature`)
579
+ 7. Open a Pull Request
580
+
581
+ ### Development Setup
582
+
583
+ ```bash
584
+ git clone https://github.com/MasterA5/flet_router.git
585
+ cd flet_router
586
+ pip install -e .[dev]
587
+ ```
588
+
589
+ ## License
590
+
591
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
592
+
593
+ ## Support
594
+
595
+ - 📖 [Documentation](https://github.com/MasterA5/flet_router/wiki)
596
+ - 🐛 [Issue Tracker](https://github.com/MasterA5/flet_router/issues)
597
+ - 💬 [Discussions](https://github.com/MasterA5/flet_router/discussions)
598
+
599
+ For questions or suggestions, please open an issue on GitHub.
600
+
601
+ ## Changelog
602
+
603
+ ### v0.1.0
604
+ - Initial release
605
+ - Basic routing functionality
606
+ - Authentication support with guards and middleware
607
+ - Route parameters and private parameters with encryption
608
+ - View caching and preloading
609
+ - Named routes for cleaner navigation
610
+ - Session management
611
+ - Async support for data loading
612
+ - Comprehensive example application with DummyJSON API integration
613
+ - Type safety with full type hints
614
+
615
+ ## Acknowledgments
616
+
617
+ Built with [Flet](https://flet.dev) - A framework for building beautiful multi-platform apps with Python.