编程 Flet 构建跨平台应用的 Python 框架

2025-03-21 08:40:53 +0800 CST views 222

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应用)
  • 应用发布和部署方法
复制全文 生成海报 开发框架 跨平台 Python编程 用户界面

推荐文章

api远程把word文件转换为pdf
2024-11-19 03:48:33 +0800 CST
nuxt.js服务端渲染框架
2024-11-17 18:20:42 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
在 Docker 中部署 Vue 开发环境
2024-11-18 15:04:41 +0800 CST
纯CSS实现3D云动画效果
2024-11-18 18:48:05 +0800 CST
Golang Select 的使用及基本实现
2024-11-18 13:48:21 +0800 CST
php指定版本安装php扩展
2024-11-19 04:10:55 +0800 CST
回到上次阅读位置技术实践
2025-04-19 09:47:31 +0800 CST
H5保险购买与投诉意见
2024-11-19 03:48:35 +0800 CST
JavaScript数组 splice
2024-11-18 20:46:19 +0800 CST
PHP中获取某个月份的天数
2024-11-18 11:28:47 +0800 CST
Nginx 跨域处理配置
2024-11-18 16:51:51 +0800 CST
Vue3中的v-model指令有什么变化?
2024-11-18 20:00:17 +0800 CST
Vue 中如何处理跨组件通信?
2024-11-17 15:59:54 +0800 CST
虚拟DOM渲染器的内部机制
2024-11-19 06:49:23 +0800 CST
智慧加水系统
2024-11-19 06:33:36 +0800 CST
一些实用的前端开发工具网站
2024-11-18 14:30:55 +0800 CST
程序员茄子在线接单