在 Python 中创建 Flet 应用程序

要编写 Flet 应用程序,您无需成为前端专家,但建议您具备 Python 和面向对象编程的基本知识。

在本指南中,我们将研究 Flet 应用程序的结构,了解如何使用 Flet 控件输出数据、向用户请求数据以及构建基本页面布局。我们还将介绍一些打包和部署选项,以便为您的用户提供现成的应用程序。

安装flet

Flet 需要 Python 3.7 或更高版本。要开始使用 Flet,您需要先安装flet模块:

pip install flet

要升级flet模块运行: ```sh pip install flet --upgrade

## 基本应用

一个非常小的 Flet 应用程序具有以下结构

```sh
import flet
from flet import Page

def main(page: Page):
    # add/update controls on Page
    pass

flet.app(target=main)

笔记 本节特意称为“基本”部分,因为在本指南的后面,我们将看到更多具有可重用控件的应用程序结构的实际方法。

典型的 Flet 程序以调用flet.app()应用程序开始等待新用户会话的位置结束。函数main()是 Flet 应用程序中的入口点。每个用户会话都在一个新线程上调用它,并Page传入一个实例。在浏览器中运行 Flet 应用程序时,会为每个打开的选项卡或页面启动一个新的用户会话。作为桌面应用程序运行时,只会创建一个会话。

Page就像是特定于用户的“画布”,是用户会话的视觉状态。要构建向页面添加和删除控件的应用程序 UI,请更新其属性。上面的代码示例将只向每个用户显示一个空白页面。

默认情况下,Flet 应用程序在本机操作系统窗口中启动,这对于开发来说非常方便。但是,您可以通过修改以下调用在新的浏览器窗口中打开它flet.app

flet.app(target=main, view=flet.WEB_BROWSER)

信息 在内部,每个 Flet 应用程序都是一个 Web 应用程序,即使它是在本机操作系统窗口中打开的,内置的 Web 服务器仍然在后台启动。Flet Web 服务器称为“Fletd”,默认情况下它正在侦听随机 TCP 端口。您可以指定自定义 TCP 端口,然后在浏览器中打开应用程序以及桌面视图: ```python flet.app(port=8550, target=main)

> 在浏览器中打开`http://localhost:<port>`以查看 Flet 应用程序的 Web 版本。


用户界面由**控件**(又名小部件)组成。要使控件对用户可见,必须将它们添加到`Page`其他控件或内部。页面是最顶层的控件。将控件相互嵌套可以表示为以 **Page** 为根的树。

控件只是普通的 Python 类。通过具有与其属性匹配的参数的构造函数创建控件实例,例如:

```python
t = Text(value="Hello, world!", color="green")

要在页面上显示控件,请将其添加到页面controls列表并调用page.update()以将页面更改发送到浏览器或桌面客户端:

import flet
from flet import Page, Text

def main(page: Page):
    t = Text(value="Hello, world!", color="green")
    page.controls.append(t)
    page.update()

flet.app(target=main)

笔记 在以下示例中,我们将仅显示main函数的内容。

您可以修改控件属性,UI 将在下一个更新page.update()

t = Text()
page.add(t) # it's a shortcut for page.controls.add(t) and then page.update()

for i in range(10):
    t.value = f"Step {i}"
    page.update()
    sleep(1)

一些控件是“容器”控件(如 Page),可以包含其他控件。例如,Row控件允许将其他控件一个接一个地排列在一行中:

page.add(
    Row(controls=[
        Text("A"),
        Text("B"),
        Text("C")
    ])
)

page.update()足够聪明,只发送自上次调用以来所做的更改,因此您可以向页面添加几个新控件,删除其中一些,更改其他控件的属性,然后调用page.update()以进行批量更新,例如:

for i in range(10):
    page.controls.append(Text(f"Line {i}"))
    if i > 4:
        page.controls.pop(0)
    page.update()
    sleep(0.3)

某些控件(例如按钮)可能具有对用户输入做出反应的事件处理程序,例如ElevatedButton.on_click

def button_clicked(e):
    page.add(Text("Clicked!"))

page.add(ElevatedButton(text="Click me", on_click=button_clicked))

以及更高级的简单待办事项示例:

import flet
from flet import Checkbox, ElevatedButton, Row, TextField

def main(page):
    def add_clicked(e):
        page.add(Checkbox(label=new_task.value))

    new_task = TextField(hint_text="Whats needs to be done?", width=300)
    page.add(Row([new_task, ElevatedButton("Add", on_click=add_clicked)]))

flet.app(target=main)

信息 Flet 实现了命令式UI 模型,您可以在其中“手动”使用有状态控件构建应用程序 UI,然后通过更新控件属性对其进行变异。Flutter 实现了声明式模型,其中 UI 根据应用程序数据更改自动重新构建。在现代前端应用程序中管理应用程序状态本质上是一项复杂的任务,而 Flet 的“老派”方法可能对没有前端经验的程序员更具吸引力。

visible

每个控件都有默认的visible属性true- 控件呈现在页面上。设置visiblefalse完全阻止控件(及其所有子项,如果有)在页面画布上呈现。隐藏的控件不能用键盘或鼠标聚焦或选择,它们不会发出任何事件。

disabled

每个控件都有默认的disabled属性false- 控件及其所有子控件都已启用。 disabled属性主要用于数据输入控件,如TextField, Dropdown, Checkbox, 按钮。但是,disabled可以将其设置为父控件,并且其值将递归地向下传播到所有子控件。

例如,如果您有一个带有多个条目控件的表单,您可以disabled为每个控件单独设置属性:

first_name = TextField()
last_name = TextField()
first_name.disabled = True
last_name.disabled = True
page.add(first_name, last_name)

或者您可以将表单控件放入容器中,例如Column,然后disabled为列设置:

first_name = TextField()
last_name = TextField()
c = Column(controls=[
    first_name,
    last_name
])
c.disabled = True
page.add(c)

控制

Flet 控件是对象,要访问它们的属性,我们需要保留对这些对象的引用(变量)。

考虑以下示例:

import flet
from flet import Column, ElevatedButton, Text, TextField

def main(page):

    first_name = TextField(label="First name", autofocus=True)
    last_name = TextField(label="Last name")
    greetings = Column()

    def btn_click(e):
        greetings.controls.append(Text(f"Hello, {first_name.value} {last_name.value}!"))
        first_name.value = ""
        last_name.value = ""
        page.update()
        first_name.focus()

    page.add(
        first_name,
        last_name,
        ElevatedButton("Say hello!", on_click=btn_click),
        greetings,
    )

flet.app(target=main)

main()方法的最开始,我们创建了三个控件,我们将在按钮的on_click处理程序中使用它们:两个TextField用于名字和姓氏,一个Column- 用于问候消息的容器。我们创建具有所有属性集的控件,并且在main()方法结束时,在page.add()调用中,我们使用它们的引用(变量)。

当添加更多和模式控件和事件处理程序时,将所有控件定义保存在一个地方变得具有挑战性,因此它们分散在main()正文中。看一眼page.add()参数很难想象(没有不断跳转到 IDE 中的变量定义)最终形式会是什么样子:

    page.add(
        first_name,
        last_name,
        ElevatedButton("Say hello!", on_click=btn_click),
        greetings,
    )

first_nameTextField,是否设置了自动对焦?问候是 aRow还是 a Column

Flet 提供了Ref实用程序类,它允许定义对控件的引用,在事件处理程序中使用该引用,并稍后在构建树时设置对真实控件的引用。这个想法来自React。

要定义新的类型化控件引用:

first_name = Ref[TextField]()

要访问引用的控件(控件取消引用),请使用Ref.current属性:

# empty first name
first_name.current.value = ""

要将控件分配给引用集Control.ref属性到引用:

page.add(
    TextField(ref=first_name, label="First name", autofocus=True)
)

笔记 所有 Flet 控件都有ref属性。

我们可以重写我们的程序来使用引用:

import flet
from flet import Column, ElevatedButton, Text, TextField
from flet.ref import Ref

def main(page):

    first_name = Ref[TextField]()
    last_name = Ref[TextField]()
    greetings = Ref[Column]()

    def btn_click(e):
        greetings.current.controls.append(
            Text(f"Hello, {first_name.current.value} {last_name.current.value}!")
        )
        first_name.current.value = ""
        last_name.current.value = ""
        page.update()
        first_name.current.focus()

    page.add(
        TextField(ref=first_name, label="First name", autofocus=True),
        TextField(ref=last_name, label="Last name"),
        ElevatedButton("Say hello!", on_click=btn_click),
        Column(ref=greetings),
    )

flet.app(target=main)

现在我们可以清楚地看到page.add()页面的结构和它所构建的所有控件。

是的,逻辑变得有点冗长,因为您需要添加.current.访问 ref 的控件,但这是个人喜好问题:)