mcpField
P
python-flet

python-flet

github.com/the-perfect-developer开发1539 次安装26 周前更新
代码仓库

关于 python-flet

python-flet 是用于 Flet 开发的技能。Flet 是一个 Python 框架,将 Flutter widget 封装为 Python 控件,用于构建 Web、桌面和移动应用;该技能涵盖应用结构、界面、导航、异步任务和性能实践。

主要功能

  1. 01使用 Python 控件构建应用
  2. 02创建布局和自定义控件
  3. 03配置路由与主题
  4. 04使用异步任务和生命周期方法
  5. 05优化大型可滚动列表
  6. 06运行 Web 或桌面应用

适用场景

  1. 01使用 Python 开发跨平台应用
  2. 02通过自定义控件构建带路由的界面
  3. 03高效显示大型列表

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/the-perfect-developer/the-perfect-opencode --skill python-flet

安装后在 Claude Code 中输入/python-flet确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
counter-app.py
custom-control.py
references/
controls-and-layout.md
patterns-and-best-practices.md

详细介绍

Python Flet 开发

Flet 是一个 Python 框架,可在无需前端经验的情况下构建跨平台 Web、桌面和移动应用。它封装 Flutter 组件,并将其作为 Python 控件提供。

应用入口点

每个 Flet 应用都有一个接收 ft.Page 的 main 函数,并以 ft.run(main) 结尾:

import flet as ft

def main(page: ft.Page):
    page.title = "My App"
    page.add(ft.Text("Hello, Flet!"))

ft.run(main)

安装:pip install 'flet[all]'

运行桌面应用:flet run main.py

运行 Web 应用:flet run --web main.py

热重载(递归监视目录):flet run --recursive main.py

项目结构

my-app/
├── pyproject.toml
└── src/
    ├── assets/
    │   └── icon.png
    └── main.py

通过以下命令创建:flet create(或 uv run flet create)

核心概念

Page

ft.Page 是根容器。添加控件前先进行配置:

def main(page: ft.Page):
    page.title = "App Title"
    page.theme_mode = ft.ThemeMode.LIGHT
    page.theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
    page.vertical_alignment = ft.MainAxisAlignment.CENTER
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
    page.scroll = ft.ScrollMode.AUTO
控件与 update()

控件是 Python 对象。修改后需要调用 page.update() 或 control.update():

text = ft.Text("Initial")
page.add(text)

def change(e):
    text.value = "Changed"
    text.update()          # prefer: update only this control
    # page.update()        # fallback: updates entire page

规则:优先使用 control.update() 而非 page.update(),因为它发送的差异更小。

布局控件
控件用途
ft.Column垂直排列
ft.Row水平排列
ft.Stack绝对定位
ft.Container支持内边距、颜色、边框和动画的盒子
ft.ListView高效的垂直/水平可滚动列表
ft.GridView高效的可滚动网格
page.add(
    ft.Row(
        controls=[ft.Text("Left"), ft.Text("Right")],
        alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
    )
)

扩展:使用 expand=True 填满可用空间:

ft.ListView(expand=True, spacing=10)

自定义控件

样式化控件(继承单个 Flet 控件)
@ft.control
class PrimaryButton(ft.Button):
    bgcolor: ft.Colors = ft.Colors.BLUE_700
    color: ft.Colors = ft.Colors.WHITE

使用 @ft.control 或 @dataclass 装饰器。字段类型必须添加注解。

复合控件(组合多个控件)
@ft.control
class TaskItem(ft.Row):
    text: str = ""

    def init(self):
        self.checkbox = ft.Checkbox()
        self.label = ft.Text(value=self.text)
        self.controls = [self.checkbox, self.label]

    def toggle(self, e):
        self.label.color = ft.Colors.GREY if self.checkbox.value else None
        self.update()
生命周期方法
方法调用时机用途
init()__init__ 之后设置子控件
build()分配 self.page 时平台相关逻辑
did_mount()添加到页面之后启动计时器、获取数据
will_unmount()移除之前清理、取消任务
before_update()每次 update() 时同步派生状态

隔离规则:任何在自身方法中调用 self.update() 的自定义控件都必须设置 is_isolated = True(或重写 is_isolated 属性并使其返回 True)。

导航与路由

使用 page.on_route_change + page.views 作为唯一事实来源:

def main(page: ft.Page):
    def route_change():
        page.views.clear()
        page.views.append(ft.View("/", controls=[...]))
        if page.route == "/settings":
            page.views.append(ft.View("/settings", controls=[...]))
        page.update()

    async def view_pop(e):
        page.views.remove(e.view)
        await page.push_route(page.views[-1].route)

    page.on_route_change = route_change
    page.on_view_pop = view_pop
    route_change()

ft.run(main)

导航:await page.push_route("/settings")

参数化路由:

troute = ft.TemplateRoute(page.route)
if troute.match("/items/:id"):
    print(troute.id)

路由规则:

  • 始终在 page.views 中保留根 / 视图
  • 将所有路由逻辑集中到 page.on_route_change
  • 始终处理 page.on_view_pop,以保持同步

主题

# App-wide theme
page.theme = ft.Theme(color_scheme_seed=ft.Colors.GREEN)
page.dark_theme = ft.Theme(color_scheme_seed=ft.Colors.BLUE)
page.theme_mode = ft.ThemeMode.SYSTEM  # LIGHT | DARK | SYSTEM

# Nested theme (scoped to a container)
ft.Container(
    theme=ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.PINK)),
    content=ft.Button("Pink button"),
)

异步应用

将 main 标记为 async 以支持 asyncio。使用 asyncio.sleep()(而非 time.sleep())。

import asyncio
import flet as ft

async def main(page: ft.Page):
    async def on_click(e):
        await asyncio.sleep(1)
        page.add(ft.Text("Done!"))

    page.add(ft.Button("Click me", on_click=on_click))

ft.run(main)

使用 page.run_task(coro) 从 did_mount() 运行后台协程。

性能:大型列表

包含数百个项目时,使用 ListView 或 GridView,而非 Column/Row:

# Efficient: renders only visible items
lv = ft.ListView(expand=True, spacing=10, item_extent=50)
for i in range(5000):
    lv.controls.append(ft.Text(f"Line {i}"))
page.add(lv)

# Batch updates to avoid large WebSocket messages
for i in range(5000):
    lv.controls.append(ft.Text(f"Line {i}"))
    if i % 500 == 0:
        page.update()
page.update()

隐式动画

通过在控件上设置 animate_* 属性启用:

# Opacity fade
container = ft.Container(
    width=150, height=150,
    bgcolor=ft.Colors.BLUE,
    animate_opacity=300,  # ms
)

# Scale with bounce curve
ft.Container(
    animate_scale=ft.Animation(
        duration=600,
        curve=ft.AnimationCurve.BOUNCE_OUT,
    )
)

# Position animation (inside Stack or page.overlay)
ft.Container(animate_position=1000)

# Animated content switcher
ft.AnimatedSwitcher(
    content,
    transition=ft.AnimatedSwitcherTransition.SCALE,
    duration=500,
)

窗口控制(桌面应用)

page.title = "My App"
page.window.width = 800
page.window.height = 600
page.window.resizable = True
page.window.always_on_top = False
page.window.center()

快速参考:常用控件

ft.Text("Hello", size=20, weight=ft.FontWeight.BOLD)
ft.Button("Click", on_click=handler)
ft.IconButton(ft.Icons.ADD, on_click=handler)
ft.TextField(label="Name", on_change=handler)
ft.Checkbox(label="Check me", on_change=handler)
ft.Dropdown(options=[ft.dropdown.Option("A"), ft.dropdown.Option("B")])
ft.Image(src="photo.jpg")                   # from assets/
ft.Image(src="https://example.com/img.png") # remote
ft.AppBar(title=ft.Text("Title"))
ft.NavigationDrawer(controls=[...])
ft.AlertDialog(title=ft.Text("Alert"), content=ft.Text("Body"))

更多资源

参考文件
  • references/controls-and-layout.md - 完整的布局模式、Container 样式、Stack 定位和控件属性
  • references/patterns-and-best-practices.md - 状态管理、会话/客户端存储、PubSub、键盘快捷键、错误处理
示例文件
  • examples/counter-app.py - 最精简的可运行计数器示例,展示页面设置和事件处理程序
  • examples/custom-control.py - 带生命周期方法和隔离机制的复合自定义控件

评论

支持 Markdown