flet-web 0.1.0__py3-none-any.whl
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.
- flet_web/__init__.py +9 -0
- flet_web/fastapi/README.md +146 -0
- flet_web/fastapi/__init__.py +6 -0
- flet_web/fastapi/app.py +121 -0
- flet_web/fastapi/flet_app.py +430 -0
- flet_web/fastapi/flet_app_manager.py +167 -0
- flet_web/fastapi/flet_fastapi.py +128 -0
- flet_web/fastapi/flet_oauth.py +66 -0
- flet_web/fastapi/flet_static_files.py +188 -0
- flet_web/fastapi/flet_upload.py +95 -0
- flet_web/fastapi/oauth_state.py +11 -0
- flet_web/fastapi/serve_fastapi_web_app.py +94 -0
- flet_web/patch_index.py +112 -0
- flet_web/uploads.py +54 -0
- flet_web/version.py +1 -0
- flet_web/web/.last_build_id +1 -0
- flet_web/web/assets/AssetManifest.bin +1 -0
- flet_web/web/assets/AssetManifest.bin.json +1 -0
- flet_web/web/assets/AssetManifest.json +1 -0
- flet_web/web/assets/FontManifest.json +1 -0
- flet_web/web/assets/NOTICES +37535 -0
- flet_web/web/assets/fonts/MaterialIcons-Regular.otf +0 -0
- flet_web/web/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf +0 -0
- flet_web/web/assets/packages/flutter_map/lib/assets/flutter_map_logo.png +0 -0
- flet_web/web/assets/packages/media_kit/assets/web/hls1.4.10.js +2 -0
- flet_web/web/assets/packages/record_web/assets/js/record.fixwebmduration.js +507 -0
- flet_web/web/assets/packages/record_web/assets/js/record.worklet.js +400 -0
- flet_web/web/assets/packages/wakelock_plus/assets/no_sleep.js +230 -0
- flet_web/web/assets/shaders/ink_sparkle.frag +126 -0
- flet_web/web/favicon.png +0 -0
- flet_web/web/flutter.js +4 -0
- flet_web/web/flutter_bootstrap.js +31 -0
- flet_web/web/flutter_service_worker.js +214 -0
- flet_web/web/icons/apple-touch-icon-192.png +0 -0
- flet_web/web/icons/icon-192.png +0 -0
- flet_web/web/icons/icon-512.png +0 -0
- flet_web/web/icons/icon-maskable-192.png +0 -0
- flet_web/web/icons/icon-maskable-512.png +0 -0
- flet_web/web/icons/loading-animation.png +0 -0
- flet_web/web/index.html +99 -0
- flet_web/web/main.dart.js +234871 -0
- flet_web/web/manifest.json +35 -0
- flet_web/web/python-worker.js +47 -0
- flet_web/web/python.js +28 -0
- flet_web/web/version.json +1 -0
- flet_web-0.1.0.dist-info/METADATA +26 -0
- flet_web-0.1.0.dist-info/RECORD +48 -0
- flet_web-0.1.0.dist-info/WHEEL +4 -0
flet_web/__init__.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Flet - a better UI for FastAPI
|
|
2
|
+
|
|
3
|
+
Flet for FastAPI allows adding interactive real-time dashboards to your FastAPI app as well as host any Flet web app inside FastAPI with production-grade reliability.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install flet-fastapi
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## First app
|
|
12
|
+
|
|
13
|
+
Create `counter.py` with the following content:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import flet as ft
|
|
17
|
+
import flet_fastapi
|
|
18
|
+
|
|
19
|
+
async def main(page: ft.Page):
|
|
20
|
+
counter = ft.Text("0", size=50, data=0)
|
|
21
|
+
|
|
22
|
+
async def add_click(e):
|
|
23
|
+
counter.data += 1
|
|
24
|
+
counter.value = str(counter.data)
|
|
25
|
+
counter.update()
|
|
26
|
+
|
|
27
|
+
page.floating_action_button = ft.FloatingActionButton(
|
|
28
|
+
icon=ft.icons.ADD, on_click=add_click
|
|
29
|
+
)
|
|
30
|
+
await page.add_async(
|
|
31
|
+
ft.Container(counter, alignment=ft.alignment.center, expand=True)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
app = flet_fastapi.app(main)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
That's a simple app displaying a counter and a button at the right bottom to increment that counter.
|
|
38
|
+
|
|
39
|
+
`flet_fastapi.app()` configures a single Flet app at the root of FastAPI app with `main()` sessions handler and the following endpoints:
|
|
40
|
+
|
|
41
|
+
`/ws` (WS) - WebSocket handler for the Flet app.
|
|
42
|
+
|
|
43
|
+
`/upload` (PUT) - file uploads handler.
|
|
44
|
+
|
|
45
|
+
`/oauth_callback` (GET) - OAuth flow callback handler.
|
|
46
|
+
|
|
47
|
+
`/` (GET) - Flet app static files with SPA catch-all handler.
|
|
48
|
+
|
|
49
|
+
## Running the app locally
|
|
50
|
+
|
|
51
|
+
Install [Uvicorn](https://www.uvicorn.org/) web server:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
pip install uvicorn
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Start `uvicorn` with:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
uvicorn counter:app
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Open the browser and navigate to http://127.0.0.1:8000 to see the app running.
|
|
64
|
+
|
|
65
|
+
## Hosting multiple Flet apps under the same domain
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import flet as ft
|
|
69
|
+
import flet_fastapi
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def root_main(page: ft.Page):
|
|
73
|
+
await page.add_async(ft.Text("This is root app!"))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def sub_main(page: ft.Page):
|
|
77
|
+
await page.add_async(ft.Text("This is sub app!"))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
app = flet_fastapi.FastAPI()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
app.mount("/sub-app", flet_fastapi.app(sub_main))
|
|
84
|
+
app.mount("/", flet_fastapi.app(root_main))
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Sub-apps must be mapped before the root Flet app as it configures catch-all `index.html` for SPA.
|
|
88
|
+
|
|
89
|
+
Run the app with `uvicorn` and visit http://127.0.0.1:8000 and then http://127.0.0.1:8000/sub-app/ to see both Flet apps running. Notice the trailing slash in `/sub-app/` URL - without the slash the request will be routed to a root app.
|
|
90
|
+
|
|
91
|
+
## Adding Flet to the existing FastAPI app
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from contextlib import asynccontextmanager
|
|
95
|
+
|
|
96
|
+
import flet as ft
|
|
97
|
+
import flet_fastapi
|
|
98
|
+
from fastapi import FastAPI
|
|
99
|
+
|
|
100
|
+
@asynccontextmanager
|
|
101
|
+
async def lifespan(app: FastAPI):
|
|
102
|
+
await flet_fastapi.app_manager.start()
|
|
103
|
+
yield
|
|
104
|
+
await flet_fastapi.app_manager.shutdown()
|
|
105
|
+
|
|
106
|
+
app = FastAPI(lifespan=lifespan)
|
|
107
|
+
|
|
108
|
+
async def main(page: ft.Page):
|
|
109
|
+
await page.add_async(ft.Text("Hello, Flet!"))
|
|
110
|
+
|
|
111
|
+
app.mount("/flet-app", flet_fastapi.app(main))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
When adding Flet app to the existing FastAPI app you need to call `flet_fastapi.app_manager.start()` on app start and `flet_fastapi.app_manager.shutdown()` on shutdown. Use the way that best suites you: lifespan (in the example above) or app events.
|
|
115
|
+
|
|
116
|
+
`app_manager.start()` method starts background tasks cleaning up expired sessions and OAuth flow states.
|
|
117
|
+
|
|
118
|
+
`app_manager.shutdown()` method removes any temporary files created by a Flet app.
|
|
119
|
+
|
|
120
|
+
## Running the app in production
|
|
121
|
+
|
|
122
|
+
It is recommended to run FastAPI in production with [Hypercorn](https://github.com/pgjones/hypercorn/) which is ASGI web server, but it is also possible to run FastAPI apps with [Gunicorn](https://gunicorn.org/) which is a WSGI server, but has more features, like passing proxy headers.
|
|
123
|
+
|
|
124
|
+
To install Gunicorn:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
pip install gunicorn
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Start `gunicorn` with:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
gunicorn -k uvicorn.workers.UvicornWorker counter:app
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Reference
|
|
137
|
+
|
|
138
|
+
### Environment variables
|
|
139
|
+
|
|
140
|
+
`FLET_SECRET_KEY` - secret key to sign upload requests. Must be set if upload directory is configured.
|
|
141
|
+
|
|
142
|
+
`FLET_SESSION_TIMEOUT` - the number of seconds to keep session alive after user has disconnected. Default is 3,600 seconds.
|
|
143
|
+
|
|
144
|
+
`FLET_OAUTH_STATE_TIMEOUT` - OAuth state lifetime, in seconds, which is a maximum allowed time between starting OAuth flow and redirecting to OAuth callback URL. Default is 600 seconds.
|
|
145
|
+
|
|
146
|
+
`FLET_MAX_UPLOAD_SIZE` - max allowed size of an uploaded file, bytes.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from flet_web.fastapi.app import app
|
|
2
|
+
from flet_web.fastapi.flet_app import FletApp
|
|
3
|
+
from flet_web.fastapi.flet_app_manager import app_manager
|
|
4
|
+
from flet_web.fastapi.flet_fastapi import FastAPI
|
|
5
|
+
from flet_web.fastapi.flet_static_files import FletStaticFiles
|
|
6
|
+
from flet_web.fastapi.flet_upload import FletUpload
|
flet_web/fastapi/app.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
from typing import Awaitable, Callable, Optional, Union
|
|
4
|
+
|
|
5
|
+
from fastapi import Request, WebSocket
|
|
6
|
+
from flet.core.page import Page
|
|
7
|
+
from flet.core.types import WebRenderer
|
|
8
|
+
|
|
9
|
+
from flet_web.fastapi.flet_app import (
|
|
10
|
+
DEFAULT_FLET_OAUTH_STATE_TIMEOUT,
|
|
11
|
+
DEFAULT_FLET_SESSION_TIMEOUT,
|
|
12
|
+
FletApp,
|
|
13
|
+
)
|
|
14
|
+
from flet_web.fastapi.flet_fastapi import FastAPI
|
|
15
|
+
from flet_web.fastapi.flet_oauth import FletOAuth
|
|
16
|
+
from flet_web.fastapi.flet_static_files import FletStaticFiles
|
|
17
|
+
from flet_web.fastapi.flet_upload import FletUpload
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def app(
|
|
21
|
+
session_handler: Union[Callable[[Page], Awaitable], Callable[[Page], None]],
|
|
22
|
+
proxy_path: Optional[str] = None,
|
|
23
|
+
assets_dir: Optional[str] = None,
|
|
24
|
+
app_name: Optional[str] = None,
|
|
25
|
+
app_short_name: Optional[str] = None,
|
|
26
|
+
app_description: Optional[str] = None,
|
|
27
|
+
web_renderer: WebRenderer = WebRenderer.CANVAS_KIT,
|
|
28
|
+
use_color_emoji: bool = False,
|
|
29
|
+
route_url_strategy: str = "path",
|
|
30
|
+
upload_dir: Optional[str] = None,
|
|
31
|
+
upload_endpoint_path: Optional[str] = None,
|
|
32
|
+
max_upload_size: Optional[int] = None,
|
|
33
|
+
secret_key: Optional[str] = None,
|
|
34
|
+
session_timeout_seconds: int = DEFAULT_FLET_SESSION_TIMEOUT,
|
|
35
|
+
oauth_state_timeout_seconds: int = DEFAULT_FLET_OAUTH_STATE_TIMEOUT,
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
Mount all Flet FastAPI handlers in one call.
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
* `session_handler` (function or coroutine) - application entry point - a method called for newly connected user. Handler must have 1 parameter: `page` - `Page` instance.
|
|
42
|
+
* `assets_dir` (str, optional) - an absolute path to app's assets directory.
|
|
43
|
+
* `app_name` (str, optional) - PWA application name.
|
|
44
|
+
* `app_short_name` (str, optional) - PWA application short name.
|
|
45
|
+
* `app_description` (str, optional) - PWA application description.
|
|
46
|
+
* `web_renderer` (WebRenderer) - web renderer defaulting to `WebRenderer.CANVAS_KIT`.
|
|
47
|
+
* `use_color_emoji` (bool) - whether to load a font with color emoji. Default is `False`.
|
|
48
|
+
* `route_url_strategy` (str) - routing URL strategy: `path` (default) or `hash`.
|
|
49
|
+
* `upload_dir` (str) - an absolute path to a directory with uploaded files.
|
|
50
|
+
* `upload_endpoint_path` (str, optional) - absolute URL of upload endpoint, e.g. `/upload`.
|
|
51
|
+
* `max_upload_size` (str, int) - maximum size of a single upload, bytes. Unlimited if `None`.
|
|
52
|
+
* `secret_key` (str, optional) - secret key to sign and verify upload requests.
|
|
53
|
+
* `session_timeout_seconds` (int, optional)- session lifetime, in seconds, after user disconnected.
|
|
54
|
+
* `oauth_state_timeout_seconds` (int, optional) - OAuth state lifetime, in seconds, which is a maximum allowed time between starting OAuth flow and redirecting to OAuth callback URL.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
env_upload_dir = os.getenv("FLET_UPLOAD_DIR")
|
|
58
|
+
if env_upload_dir:
|
|
59
|
+
upload_dir = env_upload_dir
|
|
60
|
+
|
|
61
|
+
env_websocket_endpoint = os.getenv("FLET_WEBSOCKET_HANDLER_ENDPOINT")
|
|
62
|
+
websocket_endpoint = (
|
|
63
|
+
"ws" if not env_websocket_endpoint else env_websocket_endpoint.strip("/")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
env_upload_endpoint = os.getenv("FLET_UPLOAD_HANDLER_ENDPOINT")
|
|
67
|
+
upload_endpoint = (
|
|
68
|
+
"upload" if not env_upload_endpoint else env_upload_endpoint.strip("/")
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
env_oauth_callback_endpoint = os.getenv("FLET_OAUTH_CALLBACK_HANDLER_ENDPOINT")
|
|
72
|
+
oauth_callback_endpoint = (
|
|
73
|
+
"oauth_callback"
|
|
74
|
+
if not env_oauth_callback_endpoint
|
|
75
|
+
else env_oauth_callback_endpoint.strip("/")
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
fastapi_app = FastAPI()
|
|
79
|
+
|
|
80
|
+
@fastapi_app.websocket(f"/{websocket_endpoint}")
|
|
81
|
+
async def app_handler(websocket: WebSocket):
|
|
82
|
+
await FletApp(
|
|
83
|
+
asyncio.get_running_loop(),
|
|
84
|
+
session_handler,
|
|
85
|
+
session_timeout_seconds=session_timeout_seconds,
|
|
86
|
+
oauth_state_timeout_seconds=oauth_state_timeout_seconds,
|
|
87
|
+
upload_endpoint_path=upload_endpoint_path,
|
|
88
|
+
secret_key=secret_key,
|
|
89
|
+
).handle(websocket)
|
|
90
|
+
|
|
91
|
+
if upload_dir:
|
|
92
|
+
|
|
93
|
+
@fastapi_app.put(
|
|
94
|
+
f"/{upload_endpoint_path if upload_endpoint_path else upload_endpoint}"
|
|
95
|
+
)
|
|
96
|
+
async def upload_handler(request: Request):
|
|
97
|
+
await FletUpload(
|
|
98
|
+
upload_dir=upload_dir,
|
|
99
|
+
max_upload_size=max_upload_size,
|
|
100
|
+
secret_key=secret_key,
|
|
101
|
+
).handle(request)
|
|
102
|
+
|
|
103
|
+
@fastapi_app.get(f"/{oauth_callback_endpoint}")
|
|
104
|
+
async def oauth_redirect_handler(request: Request):
|
|
105
|
+
return await FletOAuth().handle(request)
|
|
106
|
+
|
|
107
|
+
fastapi_app.mount(
|
|
108
|
+
path="/",
|
|
109
|
+
app=FletStaticFiles(
|
|
110
|
+
proxy_path=proxy_path,
|
|
111
|
+
assets_dir=assets_dir,
|
|
112
|
+
app_name=app_name,
|
|
113
|
+
app_short_name=app_short_name,
|
|
114
|
+
app_description=app_description,
|
|
115
|
+
web_renderer=web_renderer,
|
|
116
|
+
use_color_emoji=use_color_emoji,
|
|
117
|
+
route_url_strategy=route_url_strategy,
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return fastapi_app
|