Skip to content

声明式对话框 (Declarative dialogs) ​


ft.use_dialog() 允许组件以声明式的方式显示和更新对话框。你无需以命令式的方式调用 page.show_dialog() 并在稍后记住手动关闭或移除对话框,而是直接从组件状态中渲染 DialogControl:

  • 传入对话框实例以显示它;
  • 传入 None 以隐藏它。

这使对话框逻辑与声明式应用的其余部分保持在相同的状态流中:

  • 状态(state)决定了对话框是否可见;
  • 状态改变时,对话框的内容也会随之更新;
  • 组件中不需要调用 page.update()。

基本模式 (Basic pattern) ​

在每次渲染时调用 ft.use_dialog()。当对话框需要打开时,返回对话框控件;否则返回 None。

python
import flet as ft


@ft.component
def App():
    show, set_show = ft.use_state(False)

    ft.use_dialog(
        ft.AlertDialog(
            modal=True,
            title=ft.Text("Delete report.pdf?"),
            content=ft.Text("This cannot be undone."),
            actions=[
                ft.TextButton("Delete", on_click=lambda: set_show(False)),
                ft.TextButton("Cancel", on_click=lambda: set_show(False)),
            ],
            on_dismiss=lambda: set_show(False),
        )
        if show
        else None
    )

    return ft.Column(
        controls=[
            ft.TextButton("Open dialog", on_click=lambda: set_show(True)),
        ]
    )


ft.run(lambda page: page.render(App))

最核心的一点是:show 是单一数据源(source of truth)。对话框并不是通过直接修改页面控件树来打开的,而是因为组件渲染了它,它才得以显示。


根据状态更新对话框内容 (Updating dialog content from state) ​

因为对话框是声明式的,它的内容可以在对话框打开时对状态变化做出响应。这对于确认框、表单验证和异步工作流非常有用:

python
import asyncio
import flet as ft


@ft.component
def App():
    show, set_show = ft.use_state(False)
    deleting, set_deleting = ft.use_state(False)

    async def handle_delete():
        set_deleting(True)
        await asyncio.sleep(2)
        set_deleting(False)
        set_show(False)

    ft.use_dialog(
        ft.AlertDialog(
            modal=True,
            title=ft.Text("Delete report.pdf?"),
            content=ft.Text(
                "Deleting, please wait..." if deleting else "This cannot be undone."
            ),
            actions=[
                ft.Button(
                    "Deleting..." if deleting else "Delete",
                    disabled=deleting,
                    on_click=handle_delete,
                ),
                ft.TextButton(
                    "Cancel",
                    disabled=deleting,
                    on_click=lambda: set_show(False),
                ),
            ],
            on_dismiss=lambda: set_show(False),
        )
        if show
        else None
    )

    return ft.TextButton("Delete file", on_click=lambda: set_show(True))

ft.run(lambda page: page.render(App))

这种模式在 Flet 应用中与 asyncio 和其他异步 API 配合得非常好。有关更多背景信息,请参阅异步应用。


链式对话框 (Chaining dialogs) ​

你可以在同一个组件中多次调用 ft.use_dialog()。这使得后续的工作流变得非常直观,例如:

  • 确认对话框;
  • 异步操作;
  • 第一个对话框完全关闭后,弹出成功对话框。
python
import flet as ft


@ft.component
def App():
    show_confirm, set_show_confirm = ft.use_state(False)
    show_success, set_show_success = ft.use_state(False)
    should_chain = ft.use_ref(False)

    def confirm_delete():
        should_chain.current = True
        set_show_confirm(False)

    def on_confirm_dismiss():
        if should_chain.current:
            should_chain.current = False
            set_show_success(True)

    ft.use_dialog(
        ft.AlertDialog(
            title=ft.Text("Delete file?"),
            actions=[
                ft.TextButton("Delete", on_click=confirm_delete),
                ft.TextButton("Cancel", on_click=lambda: set_show_confirm(False)),
            ],
            on_dismiss=on_confirm_dismiss,
        )
        if show_confirm
        else None
    )

    ft.use_dialog(
        ft.AlertDialog(
            title=ft.Text("Done"),
            content=ft.Text("The file was deleted."),
            actions=[
                ft.TextButton("OK", on_click=lambda: set_show_success(False)),
            ],
        )
        if show_success
        else None
    )

    return ft.TextButton("Open", on_click=lambda: set_show_confirm(True))

ft.run(lambda page: page.render(App))

在这里使用 ft.use_ref() 非常有帮助,因为它的值在重新渲染时可以得以保留,而其本身的变化不会触发额外的重新渲染。


on_dismiss 触发时机 (on_dismiss timing) ​

DialogControl.on_dismiss 在对话框的关闭动画完成后触发,而不是在 open 变为 False 时立即触发。这使得在对话框确实完全关闭后安全地启动后续 UI 成为可能。

使用 on_dismiss 来处理需要在对话框完全消失后执行的逻辑,例如:

  • 在对话框链中打开下一个对话框;
  • 重置临时的对话框局部状态;
  • 启动后续动画或显示 Toast 提示。

何时应该使用 page.show_dialog() 替代 (When to use page.show_dialog() instead) ​

ft.use_dialog() 更加契合 @ft.component 函数和其他声明式流。

但在以下情况下,page.show_dialog() 仍然是一个很好的选择:

  • 应用程序采用命令式编写;
  • 对话框的生命周期在组件树外部进行管理;
  • 你需要从现有的页面级事件代码中触发对话框,并且暂时不想将该部分应用转换为声明式风格。

在实践中,这两种 API 分别服务于不同的风格:

  • 当 UI 需要随组件状态改变而改变时,使用声明式对话框;
  • 当 UI 是通过直接的页面修改来进行管理时,使用命令式对话框。

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