Skip to content

Flet 以 FastAPI(ASGI)应用的形式运行你的应用, 从而提供动态网站服务。

它默认使用 Uvicorn,但也可以改用任何兼容 ASGI 的服务器。

同步与异步事件处理器 ​

在 Flet Web 应用中,你可以混合使用同步和异步事件处理器。

例如,你可以这样编写应用:

python
import flet as ft
import time
import asyncio

def main(page: ft.Page):

    def handler(e):
        time.sleep(3)
        page.add(ft.Text("Handler clicked"))

    async def handler_async(e):
        await asyncio.sleep(3)
        page.add(ft.Text("Async handler clicked"))

    page.add(
        ft.Button("Call handler", on_click=handler),
        ft.Button("Call async handler", on_click=handler_async)
    )

ft.run(main)

在这个例子中,点击第一个按钮由阻塞式事件处理器处理,点击第二个按钮则调用一个异步事件处理器。第一个处理器运行在 threading.Thread 中,第二个则作为 asyncio.Task 运行。

在 Web 应用中,线程是一种有限的资源。通常会使用线程池,而随着用户数量增长,线程池可能成为瓶颈。

如果你的应用主要在做 I/O(数据库、Web API)操作,并且可以使用支持异步的库,则优先使用异步事件处理器。

建议阅读 FastAPI 关于 async/await 的文章 以更好地理解并发与并行的区别。

在本地运行应用 ​

使用 --web(-w)选项以 Web 应用方式启动 Flet 应用:

bash
flet run --web app.py

会打开一个新的浏览器窗口/标签页,应用使用随机的 TCP 端口。

要在固定端口上运行,使用 --port(-p):

bash
flet run --web --port 8000 app.py

在生产环境运行应用 ​

你可以直接用 python 运行程序:

bash
python app.py

默认使用 Uvicorn Web 服务器来托管 Web 应用。

如果 Flet 检测到无图形界面的 Linux 环境(例如 Docker 容器或 EC2 虚拟机):

  1. 将使用端口 8000 运行应用。
  2. 不会打开浏览器窗口。

如果 Flet 无法检测到无图形界面环境,你可以通过设置以下环境变量来强制启用该行为:

bash
FLET_FORCE_WEB_SERVER=true

ASGI Web 服务器 ​

你可以用任何兼容 ASGI 的服务器来托管 Flet Web 应用,例如 Uvicorn(默认使用)、Hypercorn 或 Daphne。

只需让 Flet 导出 ASGI 应用:

python
import flet as ft

def main(page: ft.Page):
    page.add(ft.Text("Hello ASGI!"))

app = ft.run(main, export_asgi_app=True)

Hypercorn ​

Hypercorn 是另一个受 Gunicorn 启发的 ASGI Web 服务器。

使用 Hypercorn 运行应用:

bash
hypercorn main:app --bind 0.0.0.0:8000

Daphne ​

Daphne 是一个用于 ASGI 和 ASGI-HTTP 的 HTTP、HTTP2 以及 WebSocket 协议服务器,由 Django Channels 开发并为其提供支持。

使用 Daphne 运行应用:

bash
daphne -b 0.0.0.0 -p 8000 main:app

Gunicorn ​

Gunicorn 是运行 Python Web 应用的热门服务器。虽然它实现的是 WSGI 规范,但借助 Uvicorn 提供的 worker 进程类,也可以运行 ASGI FastAPI 应用:

bash
gunicorn --bind 0.0.0.0:8000 -k uvicorn.workers.UvicornWorker counter:app

静态资源 ​

当在浏览器中打开一个 Flet 应用时,其 index.html、Flutter 引擎、favicon、图片以及其他 Web 应用资源都由 Web 服务器提供。这些资源随 flet Python 包一起分发。不过,有些情况下你需要修改某些文件的内容来自定义应用的外观或行为,例如:

  • Favicon。
  • 应用加载动画。
  • 包含 PWA 详情的 manifest.json。

你可以在 flet.run() 中指定 assets_dir,以设置应用可用的静态资源所在位置。assets_dir 应相对于你的 main.py 目录,或使用绝对路径。assets_dir 参数的默认值为 assets。

例如,考虑以下程序结构:

/assets
   /images/my-image.png
main.py

你可以像下面这样在应用中访问你的图片:

python
import flet as ft

def main(page: ft.Page):
    page.add(ft.Image(src=f"/images/my-image.png"))

ft.run(main, assets_dir="assets")

自定义 Web 应用 ​

图标 ​

assets 目录中的所有内容都会被复制到构建好的站点上,因此任何生成的图标都可以通过在目录中放入同名同路径的文件来替换:

filewhere it is usedmasked by the platform
favicon.png浏览器标签页否
icons/Icon-192.png、icons/Icon-512.png已安装的应用、启动画面、任务切换器否
icons/Icon-maskable-192.png、icons/Icon-maskable-512.pngAndroid 安装是,裁剪为圆形
icons/apple-touch-icon-192.pngiOS「添加到主屏幕」是,圆角

你放入的文件会原样生效,所以上述两种需要遮罩的图标需要你自己处理好。保持遮罩图标不透明,并把图案放在覆盖 80% 宽度的圆内——其余部分会被裁剪掉,透明图标在某些 Android 启动器上会显示黑色边角。

如果想一次性改动所有图标,可以替换 icon.png(或仅针对 Web 的 icon_web.png),Flet 会重新生成整套图标,并分别适配各自的遮罩。

加载画面 ​

动态网站在 Flutter 绘制出第一帧之前,会显示一个静态的 Flet 标志。要替换它,把你的图片放在应用 assets 目录下的 icons/loading-animation.png。文件名保持不变是为了兼容;图片不再使用呼吸或缩放动画。

后续的 Flutter 启动画面通过 [tool.flet.boot_screen] 单独配置。此覆盖仅适用于动态网站;flet build web 使用的是配置好的 splash screen。

PWA ​

渐进式 Web 应用(Progressive Web Apps,简称 PWA)提供了一种方式,把类应用的网站变成类网站的 App。

有关 PWA 的入门介绍,参见 PWAs Turn Websites Into Apps: Here's How。

支持 PWA 的浏览器(安装说明):

  • 全平台的 Chrome
  • 全平台的 Edge
  • Android 上的 Firefox
  • iOS 和 iPadOS 上的 Safari

::: infoInfo 本节内容基于以下来源(查看更多细节请查阅):

清单 ​

你可以在 manifest.json 中修改 PWA 的名称、描述、颜色和其他信息,该文件必须放在 assets 目录的根位置。

以下是你最可能想自定义的常用清单项链接:

  • name —— 通常展示给用户的 Web 应用名称。
  • short_name —— 在空间不足以显示 name 时展示给用户的 Web 应用名称。
  • description —— 说明应用的功能。
  • theme_color —— 定义应用的默认主题色。
  • background_color —— 定义应用页面在样式表加载完成前显示的占位背景色。

图标 ​

自定义图标应放在 assets/icons 目录下:

  • icon-192.png、icon-512.png —— 在 Windows 任务栏中显示的应用图标。
  • icon-maskable-192.png、icon-maskable-512.png —— 在 Android 中显示的应用图标。
  • apple-touch-icon-192.png —— 在 iOS 中显示的应用图标。

环境变量 ​

Web 应用托管的各个方面都可以通过环境变量进行额外控制。

高级 FastAPI 场景 ​

Flet FastAPI 应用 ​

  • flet.fastapi.app() 创建一个 FastAPI 应用来处理 Flet 会话,并在应用根路径挂载以下端点:
    • /ws(WS)—— Flet 应用的 WebSocket 事件处理器。它在新 WebSocket 连接建立并创建新应用会话时调用 main()。
  • /upload(PUT)—— 文件上传事件处理器。
  • /oauth_callback(GET)—— OAuth 流程回调事件处理器。
  • /(GET)—— 带 SPA 全捕获事件处理器的 Flet 应用静态文件。

在同一域名下托管多个 Flet 应用 ​

python
import flet as ft
import flet.fastapi as flet_fastapi

async def root_main(page: ft.Page):
    page.add(ft.Text("This is root app!"))

async def sub_main(page: ft.Page):
    page.add(ft.Text("This is sub app!"))

app = flet_fastapi.FastAPI()

app.mount("/sub-app", flet_fastapi.app(sub_main))
app.mount("/", flet_fastapi.app(root_main))

子应用必须在根 Flet 应用之前挂载,因为根应用会为 SPA 配置全捕获的 index.html。

用 uvicorn 运行应用,然后访问 http://127.0.0.1:8000 以及 http://127.0.0.1:8000/sub-app/ 来查看两个 Flet 应用都在运行。

注意 /sub-app/ 末尾的斜杠——没有它的话,请求会被路由到根应用。

把 Flet 添加到已有的 FastAPI 应用 ​

python
from contextlib import asynccontextmanager

import flet as ft
import flet.fastapi as flet_fastapi
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    await flet_fastapi.app_manager.start()
    yield
    await flet_fastapi.app_manager.shutdown()

app = FastAPI(lifespan=lifespan)

async def main(page: ft.Page):
    page.add(ft.Text("Hello, Flet!"))

app.mount("/flet-app", flet_fastapi.app(main))

把 Flet 应用添加到已有的 FastAPI 应用时,需要在应用启动时调用 flet_fastapi.app_manager.start(),在关闭时调用 flet_fastapi.app_manager.shutdown()。选择最适合你的方式:lifespan(如上例)或 app 事件。

app_manager.start() 方法启动用于在后台清理过期会话和 OAuth 流程状态的后台任务。

app_manager.shutdown() 方法移除 Flet 应用创建的所有临时文件。

配置单个 Flet 端点 ​

静态文件 ​

一个用于提供静态 Flet 应用文件(index.html、manifest.json、Flutter JS 应用等)以及用户静态资源的 FastAPI 应用。

python
from flet.fastapi import FastAPI, FletStaticFiles

app = FastAPI()

# 挂载到 Web 应用的根路径
app.mount(path="/", app=FletStaticFiles())

WebSocket 事件处理器 ​

处理来自运行在浏览器中的 Flet 客户端的 WebSocket 连接。WebSocket 通道用于在浏览器和后端之间发送事件,并接收页面更新。

python
import asyncio
import flet as ft
from flet.fastapi import FletApp, app_manager

async def main(page: ft.Page):
    page.add(ft.Text("Hello, Flet!"))

@app.websocket("/app1/ws")
async def flet_app(websocket: WebSocket):
    await FletApp(
        loop=asyncio.get_running_loop(),
        executor=app_manager.executor,
        main=main,
        before_main=None,
    ).handle(websocket)

上传事件处理器 ​

处理由 FilePicker 控件触发的文件上传。该端点是可选的——如果你的应用不使用 FilePicker,则不需要它。

python
from flet.fastapi import FletUpload

@app.put("/upload")
async def flet_uploads(request: Request):
    await FletUpload("/Users/feodor/Downloads/123").handle(request)

OAuth 回调事件处理器 ​

处理 OAuth 流程的回调请求。如果你的应用不使用 authentication,则该端点是不必要的。

python
from flet.fastapi import FletOAuth

@app.get("/oauth_callback")
async def flet_oauth(request: Request):
    return await FletOAuth().handle(request)

关注我,学习大模型、大数据、Python等热门技术。