Python + Flet:三行代码搞定漂亮界面,新手也能轻松开发APP
大家好,今天我要给各位Python爱好者推荐一个超级好用又简单的UI库 —— Flet。
一、Flet是啥?有啥特点?
Flet是一个让Python小白也能快速开发精美界面应用的库。不管是桌面应用、网页应用,还是手机应用,通通一套代码搞定!
主要特点:
- 超低门槛:不需要学HTML、CSS、JavaScript
- 现代UI:默认Material Design,颜值在线
- 全平台:一套代码,Windows、Mac、Linux、Web全覆盖
- 实时热更新:代码改动实时可见,开发超爽
- 丰富组件:按钮、输入框、图表、列表...应有尽有
先来个"Hello World"感受一下Flet有多简单:
import flet as ft
def main(page: ft.Page):
page.title = "我的第一个Flet应用"
page.add(ft.Text("Hello, 这是用Flet做的界面!"))
ft.app(target=main)
三行核心代码,就能显示一个窗口,是不是很简单?
二、安装Flet
先安装Flet,一行命令搞定:
pip install flet
如果想要完整体验(含图表等高级功能),可以安装:
pip install flet[all]
三、Flet基础概念
在深入学习前,先了解几个关键概念:
- Page(页面):应用的主窗口,所有内容都放在这里
- Controls(控件):界面元素,如按钮、文本框等
- 布局控件:如Row(行)、Column(列)、Container(容器),用来组织其他控件
- 更新机制:调用
page.update()
让修改生效
最基本的Flet程序结构:
import flet as ft
def main(page: ft.Page):
# 1. 配置页面属性
page.title = "应用标题"
# 2. 定义控件
my_text = ft.Text("这是一段文本")
# 3. 添加控件到页面
page.add(my_text)
# 4. 修改控件(如需要)
my_text.value = "文本已修改"
page.update() # 更新UI显示
# 启动应用
ft.app(target=main)
四、从简单控件开始
1. 文本控件
import flet as ft
def main(page: ft.Page):
page.title = "文本控件演示"
# 基本文本
page.add(ft.Text("普通文本"))
# 自定义样式文本
page.add(
ft.Text(
"大号红色粗体文本",
size=30, # 文字大小
color="red", # 文字颜色
weight=ft.FontWeight.BOLD, # 粗体
italic=True, # 斜体
)
)
# 带阴影效果
page.add(
ft.Text(
"带阴影的文本",
size=20,
shadow=ft.BoxShadow(
spread_radius=1,
blur_radius=10,
color=ft.colors.BLUE_GREY_300,
offset=ft.Offset(2, 2),
)
)
)
ft.app(target=main)
2. 按钮控件
import flet as ft
def main(page: ft.Page):
page.title = "按钮控件演示"
# 计数器变量
counter = 0
# 显示计数的文本
text_counter = ft.Text(f"计数: {counter}")
# 按钮点击事件处理
def button_clicked(e):
nonlocal counter
counter += 1
text_counter.value = f"计数: {counter}"
page.update()
# 普通按钮
btn1 = ft.ElevatedButton("点我计数", on_click=button_clicked)
# 带图标的按钮
btn2 = ft.ElevatedButton(
"带图标的按钮",
icon=ft.icons.ADD_CIRCLE,
on_click=button_clicked
)
# 图标按钮
btn3 = ft.IconButton(
icon=ft.icons.REMOVE_CIRCLE,
icon_color="red",
tooltip="图标按钮",
on_click=button_clicked
)
# 将所有控件添加到页面
page.add(
text_counter,
ft.Row([btn1, btn2, btn3]) # 使用Row排成一行
)
ft.app(target=main)
3. 输入控件
import flet as ft
def main(page: ft.Page):
page.title = "输入控件演示"
# 显示输入内容的文本
result_text = ft.Text("在下方输入内容后,点击按钮查看效果")
# 文本输入框
text_input = ft.TextField(
label="请输入文字",
hint_text="这里是提示文字",
prefix_icon=ft.icons.FORMAT_SIZE,
suffix_text="文本",
)
# 密码输入框
password_input = ft.TextField(
label="请输入密码",
password=True, # 显示为密码形式
can_reveal_password=True, # 允许显示密码
)
# 多行文本框
multiline_input = ft.TextField(
label="多行文本",
multiline=True,
min_lines=2,
max_lines=5,
)
# 按钮点击事件处理
def submit_clicked(e):
result_text.value = f"""
文本框内容: {text_input.value}
密码框内容: {password_input.value}
多行文本框内容: {multiline_input.value}
"""
page.update()
# 提交按钮
submit_button = ft.ElevatedButton("提交", on_click=submit_clicked)
# 将所有控件添加到页面
page.add(
result_text,
text_input,
password_input,
multiline_input,
submit_button,
)
ft.app(target=main)
五、页面布局
Flet提供了几种常用的布局方式:
1. Row和Column - 行与列布局
import flet as ft
def main(page: ft.Page):
page.title = "行列布局演示"
page.padding = 20 # 页面边距
# Row - 水平排列
page.add(ft.Text("Row示例 - 水平排列:"))
page.add(
ft.Row(
[
ft.Container(content=ft.Text("项目1"), bgcolor=ft.colors.RED, padding=10),
ft.Container(content=ft.Text("项目2"), bgcolor=ft.colors.GREEN, padding=10),
ft.Container(content=ft.Text("项目3"), bgcolor=ft.colors.BLUE, padding=10),
],
spacing=10, # 元素间距
)
)
# Column - 垂直排列
page.add(ft.Text("Column示例 - 垂直排列:"))
page.add(
ft.Column(
[
ft.Container(content=ft.Text("项目A"), bgcolor=ft.colors.ORANGE, padding=10),
ft.Container(content=ft.Text("项目B"), bgcolor=ft.colors.PURPLE, padding=10),
ft.Container(content=ft.Text("项目C"), bgcolor=ft.colors.TEAL, padding=10),
],
spacing=10, # 元素间距
)
)
# 对齐方式示例
page.add(ft.Text("对齐方式示例:"))
page.add(
ft.Row(
[
ft.Container(content=ft.Text("左"), bgcolor=ft.colors.RED, padding=10),
ft.Container(content=ft.Text("中"), bgcolor=ft.colors.GREEN, padding=10),
ft.Container(content=ft.Text("右"), bgcolor=ft.colors.BLUE, padding=10),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN, # 两端对齐
width=page.width, # 占满整行
)
)
ft.app(target=main)
2. Container - 容器布局
import flet as ft
def main(page: ft.Page):
page.title = "容器布局演示"
# 基本容器
page.add(ft.Text("基本容器:"))
page.add(
ft.Container(
content=ft.Text("带边框和阴影的容器"),
padding=10,
bgcolor=ft.colors.WHITE,
border=ft.border.all(2, ft.colors.BLUE),
border_radius=10,
shadow=ft.BoxShadow(
spread_radius=1,
blur_radius=10,
color=ft.colors.GREY_300,
offset=ft.Offset(2, 2),
),
width=200,
height=100,
alignment=ft.alignment.center,
)
)
# 渐变背景容器
page.add(ft.Text("渐变背景容器:"))
page.add(
ft.Container(
content=ft.Text("渐变色背景", color=ft.colors.WHITE),
padding=10,
gradient=ft.LinearGradient(
begin=ft.alignment.top_left,
end=ft.alignment.bottom_right,
colors=[ft.colors.BLUE, ft.colors.GREEN],
),
width=200,
height=100,
border_radius=10,
alignment=ft.alignment.center,
)
)
# 点击效果容器
def container_clicked(e):
e.control.content.value = "我被点击了!"
e.control.update()
page.add(ft.Text("可点击容器:"))
page.add(
ft.Container(
content=ft.Text("点击我试试", color=ft.colors.WHITE),
padding=10,
bgcolor=ft.colors.BLUE,
border_radius=10,
width=200,
height=100,
alignment=ft.alignment.center,
on_click=container_clicked, # 点击事件
ink=True, # 水波纹效果
)
)
ft.app(target=main)
3. 响应式布局
import flet as ft
def main(page: ft.Page):
page.title = "响应式布局演示"
# 页面大小变化事件处理
def page_resize(e):
# 更新宽度显示
width_text.value = f"窗口宽度: {page.width}"
# 根据宽度调整布局
if page.width < 600:
layout.horizontal = False # 窄屏使用垂直布局
else:
layout.horizontal = True # 宽屏使用水平布局
page.update()
# 监听页面大小变化
page.on_resize = page_resize
# 显示宽度的文本
width_text = ft.Text(f"窗口宽度: {page.width}")
# 自适应布局容器
layout = ft.ResponsiveRow(
[
ft.Container(
content=ft.Text("卡片1", size=20),
bgcolor=ft.colors.BLUE_200,
padding=15,
col={"sm": 12, "md": 6, "lg": 4, "xl": 3}, # 响应式列宽
height=120,
border_radius=10,
),
ft.Container(
content=ft.Text("卡片2", size=20),
bgcolor=ft.colors.GREEN_200,
padding=15,
col={"sm": 12, "md": 6, "lg": 4, "xl": 3},
height=120,
border_radius=10,
),
ft.Container(
content=ft.Text("卡片3", size=20),
bgcolor=ft.colors.ORANGE_200,
padding=15,
col={"sm": 12, "md": 6, "lg": 4, "xl": 3},
height=120,
border_radius=10,
),
ft.Container(
content=ft.Text("卡片4", size=20),
bgcolor=ft.colors.PURPLE_200,
padding=15,
col={"sm": 12, "md": 6, "lg": 4, "xl": 3},
height=120,
border_radius=10,
),
],
spacing=10,
)
# 添加控件到页面
page.add(
width_text,
ft.Text("尝试调整窗口大小,查看布局变化", italic=True),
layout,
)
# 初始调用一次设置正确的布局
page_resize(None)
ft.app(target=main)
六、实用控件进阶
1. 列表和网格
import flet as ft
def main(page: ft.Page):
page.title = "列表和网格演示"
page.padding = 20
# 简单列表
fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜", "猕猴桃", "芒果", "草莓"]
# ListView示例
page.add(ft.Text("列表视图(ListView):"))
listview = ft.ListView(
height=150,
spacing=5,
padding=10,
auto_scroll=True,
)
for fruit in fruits:
listview.controls.append(
ft.Container(
content=ft.Text(fruit),
bgcolor=ft.colors.BLUE_50,
padding=15,
border_radius=5,
)
)
page.add(listview)
# GridView示例
page.add(ft.Text("网格视图(GridView):"))
grid = ft.GridView(
expand=1,
runs_count=3, # 固定3列
max_extent=150, # 最大宽度
spacing=10,
run_spacing=10,
padding=10,
child_aspect_ratio=1.0, # 宽高比1:1
)
colors = [
ft.colors.RED_200, ft.colors.GREEN_200, ft.colors.BLUE_200,
ft.colors.ORANGE_200, ft.colors.PURPLE_200, ft.colors.TEAL_200,
ft.colors.PINK_200, ft.colors.CYAN_200,
]
for i, fruit in enumerate(fruits):
grid.controls.append(
ft.Container(
content=ft.Column(
[
ft.Icon(ft.icons.FOOD_BANK, size=30),
ft.Text(fruit, size=16),
],
alignment=ft.MainAxisAlignment.CENTER,
spacing=5,
),
bgcolor=colors[i % len(colors)],
border_radius=10,
alignment=ft.alignment.center,
)
)
page.add(grid)
ft.app(target=main)
七、实战案例:Todo待办事项应用
现在,让我们结合前面学到的内容,做一个实用的待办事项应用:
import flet as ft
def main(page: ft.Page):
page.title = "Todo待办事项"
page.theme_mode = ft.ThemeMode.LIGHT
page.padding = 20
# 待办项类
class TodoItem:
def __init__(self, name, due_date=None):
self.name = name
self.completed = False
self.due_date = due_date
# 待办事项列表
todos = []
# 构建待办项UI
def build_todo_item(todo, index):
due_date_text = ""
if todo.due_date:
due_date_text = f"截止: {todo.due_date}"
return ft.Container(
content=ft.Row(
[
ft.Checkbox(
value=todo.completed,
on_change=lambda e: toggle_todo(e, index),
),
ft.Column(
[
ft.Text(
todo.name,
size=18,
style=ft.TextStyle(
decoration=ft.TextDecoration.LINE_THROUGH if todo.completed else None,
decoration_thickness=3 if todo.completed else 0,
) if todo.completed else None,
),
ft.Text(
due_date_text,
size=12,
color=ft.Colors.GREY,
) if todo.due_date else ft.Container(),
],
spacing=2,
expand=True,
),
ft.IconButton(
icon=ft.Icons.DELETE,
icon_color=ft.Colors.RED_400,
on_click=lambda e: delete_todo(index),
),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
bgcolor=ft.Colors.BLUE_50 if index % 2 == 0 else ft.Colors.WHITE,
padding=10,
border_radius=5,
)
# 更新待办事项列表UI
def update_todo_list():
todo_list.controls.clear()
if not todos:
todo_list.controls.append(
ft.Container(
content=ft.Text(
"暂无待办事项,点击下方按钮添加",
italic=True,
color=ft.Colors.GREY,
),
padding=20,
alignment=ft.alignment.center,
)
)
else:
for i, todo in enumerate(todos):
todo_list.controls.append(build_todo_item(todo, i))
page.update()
# 切换完成状态
def toggle_todo(e, index):
todos[index].completed = e.control.value
update_todo_list()
# 删除待办事项
def delete_todo(index):
todos.pop(index)
update_todo_list()
# 添加待办事项
def add_todo(e):
if not new_todo_name.value:
new_todo_name.error_text = "请输入待办事项名称"
page.update()
return
due_date = None
if new_todo_date.value:
try:
# 尝试解析日期
due_date = new_todo_date.value
except:
pass
todos.append(TodoItem(new_todo_name.value, due_date))
new_todo_name.value = ""
new_todo_date.value = ""
new_todo_name.error_text = None
update_todo_list()
# 创建输入控件(先不添加到页面)
new_todo_name = ft.TextField(
label="待办事项",
hint_text="输入待办事项...",
expand=True,
)
new_todo_date = ft.TextField(
label="截止日期(可选)",
hint_text="例如: 2023-12-31",
expand=True,
)
# 添加待办事项对话框
add_dialog = ft.AlertDialog(
title=ft.Text("添加新待办事项"),
content=ft.Column(
[
new_todo_name,
new_todo_date,
],
spacing=10,
),
actions=[
ft.TextButton("取消", on_click=lambda e: close_dialog(e)),
ft.ElevatedButton("添加", on_click=lambda e: (add_todo(e), close_dialog(e))),
],
actions_alignment=ft.MainAxisAlignment.END,
)
# 显示添加对话框
def show_add_dialog(e):
# 使用新的推荐方式添加对话框
page.overlay.append(add_dialog)
add_dialog.open = True
page.update()
# 关闭对话框
def close_dialog(e):
add_dialog.open = False
page.update()
# 待办事项列表视图
todo_list = ft.ListView(
expand=True,
spacing=10,
padding=10,
)
# 添加初始数据
todos.append(TodoItem("学习Flet库基础", "2023-12-20"))
todos.append(TodoItem("完成Todo应用开发"))
todos.append(TodoItem("分享给朋友", "2023-12-25"))
# 页面结构
page.add(
ft.Column(
[
ft.Row(
[
ft.Icon(ft.Icons.CHECK_CIRCLE, color=ft.Colors.BLUE),
ft.Text("我的待办事项", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.CENTER,
),
ft.Container(height=10), # 间隔
ft.Container(
content=todo_list,
border=ft.border.all(1, ft.Colors.GREY_300),
border_radius=10,
padding=10,
expand=True,
),
ft.Row(
[
ft.FilledTonalButton(
"添加待办事项",
icon=ft.Icons.ADD,
on_click=show_add_dialog,
expand=True,
),
],
),
],
spacing=20,
expand=True,
)
)
# 初始化更新列表
update_todo_list()
# 启动应用
ft.app(target=main)
这个Todo应用演示了如何使用Flet创建一个实用的待办事项管理器,它包含了添加、标记完成、删除等基本功能,还支持设置截止日期。
Flet的优缺点分析
优点:
- 上手简单:纯Python代码,不需要学HTML/CSS/JS
- 界面美观:Material Design风格,默认就很好看
- 跨平台:一套代码支持桌面、Web、移动
- 实时热更新:代码修改后立即可见
- 丰富组件:提供了大量现成的UI组件
- 活跃社区:版本更新快,文档丰富
缺点:
- 性能:复杂应用可能比原生应用慢一些
- 自定义限制:高度自定义UI时不如原生灵活
- 库尚年轻:相比Qt、Tkinter等成熟框架,生态仍在发展中
实用技巧
1. 调试技巧
import flet as ft
def main(page: ft.Page):
# 开启检查器
page.window_bgcolor = ft.colors.TRANSPARENT
page.window_title_bar_hidden = True
page.window_frameless = True
# 显示控件边界以便调试
page.show_semantics_debugger = True
# 开启热重载(在开发阶段很有用)
# 运行: python -m flet run your_app.py --hot
# 添加控件...
page.add(ft.Text("调试模式演示"))
ft.app(target=main)
2. 主题定制
import flet as ft
def main(page: ft.Page):
page.title = "主题定制"
# 定制主题色
page.theme = ft.Theme(
color_scheme=ft.ColorScheme(
primary=ft.colors.BLUE,
primary_container=ft.colors.BLUE_100,
secondary=ft.colors.ORANGE,
),
)
# 切换亮暗主题
def toggle_theme(e):
if page.theme_mode == ft.ThemeMode.DARK:
page.theme_mode = ft.ThemeMode.LIGHT
theme_button.icon = ft.icons.DARK_MODE
theme_button.text = "深色模式"
else:
page.theme_mode = ft.ThemeMode.DARK
theme_button.icon = ft.icons.LIGHT_MODE
theme_button.text = "浅色模式"
page.update()
theme_button = ft.ElevatedButton(
"深色模式",
icon=ft.icons.DARK_MODE,
on_click=toggle_theme
)
# 添加控件
page.add(
ft.Column(
[
ft.Text("自定义主题示例", size=20, weight=ft.FontWeight.BOLD),
theme_button,
ft.ElevatedButton("主按钮", style=ft.ButtonStyle(color=ft.colors.ON_PRIMARY)),
ft.OutlinedButton("次按钮"),
ft.TextField(label="带主题的输入框"),
],
spacing=20,
)
)
ft.app(target=main)
3. 性能优化技巧
import flet as ft
import time
def main(page: ft.Page):
page.title = "性能优化示例"
progress = ft.ProgressBar(width=400, visible=False)
status = ft.Text()
# 反例:直接更新
def inefficient_operation(e):
progress.visible = True
page.update()
status.value = "开始处理..."
page.update()
# 模拟大量操作
container = ft.Column(spacing=10)
for i in range(200):
container.controls.append(ft.Text(f"项目 {i}"))
# 每次都更新UI,非常inefficient
page.update()
time.sleep(0.01)
status.value = "处理完成!"
progress.visible = False
page.update()
# 正例:批量更新
def efficient_operation(e):
progress.visible = True
status.value = "开始处理..."
page.update()
# 模拟大量操作
container = ft.Column(spacing=10)
page.add(container)
# 提前创建所有控件
items = []
for i in range(200):
items.append(ft.Text(f"项目 {i}"))
time.sleep(0.01)
# 一次性添加所有控件
container.controls.extend(items)
status.value = "处理完成!"
progress.visible = False
page.update()
page.add(
ft.Text("性能优化示例", size=20, weight=ft.FontWeight.BOLD),
ft.Row([
ft.ElevatedButton("低效方式", on_click=inefficient_operation),
ft.ElevatedButton("高效方式", on_click=efficient_operation),
]),
progress,
status,
)
ft.app(target=main)
总结
Flet是一个为Python开发者提供的强大UI工具库,它让我们能用纯Python创建精美的跨平台应用,无需学习前端知识。对于Python爱好者和初学者来说,Flet绝对是一个友好且实用的选择。
通过本文的学习,你已经掌握了:
- Flet的基本概念和使用方法
- 常用控件的用法(按钮、文本、输入框等)
- 布局技巧(行、列、容器、响应式)
- 高级功能(列表、网格、导航、对话框)
- 实际项目开发(Todo应用)
- 应用发布和部署方法