导航和路由

导航和路由是单页应用程序 (SPA) 的基本功能,它允许将应用程序用户界面组织成虚拟页面(视图)并在它们之间“导航”,而应用程序 URL 反映了应用程序的当前状态。

对于移动应用程序,导航和路由充当到特定应用程序部分的深层链接。

好吧,由于实现是基于Navigator 2.0 Flutter API 并且需要将 Flet 的“页面”抽象替换为“页面和视图” ,因此将导航和路由添加到 Flet 中花费了比预期更多的努力。Flutter 较新的导航和路由 API 具有重大改进,例如:

  1. 对历史堆栈的编程控制。
  2. 在 AppBar 中拦截对“返回”按钮的调用的简单方法。
  3. 与浏览器历史记录的强大同步。

探索上述示例的源代码:

import flet
from flet import AppBar, ElevatedButton, Page, Text, View, colors


def main(page: Page):
    page.title = "Routes Example"

    print("Initial route:", page.route)

    def route_change(e):
        print("Route change:", e.route)
        page.views.clear()
        page.views.append(
            View(
                "/",
                [
                    AppBar(title=Text("Flet app")),
                    ElevatedButton("Go to settings", on_click=open_settings),
                ],
            )
        )
        if page.route == "/settings" or page.route == "/settings/mail":
            page.views.append(
                View(
                    "/settings",
                    [
                        AppBar(title=Text("Settings"), bgcolor=colors.SURFACE_VARIANT),
                        Text("Settings!", style="bodyMedium"),
                        ElevatedButton(
                            "Go to mail settings", on_click=open_mail_settings
                        ),
                    ],
                )
            )
        if page.route == "/settings/mail":
            page.views.append(
                View(
                    "/settings/mail",
                    [
                        AppBar(
                            title=Text("Mail Settings"), bgcolor=colors.SURFACE_VARIANT
                        ),
                        Text("Mail settings!"),
                    ],
                )
            )
        page.update()

    def view_pop(e):
        print("View pop:", e.view)
        page.views.pop()
        top_view = page.views[-1]
        page.go(top_view.route)

    page.on_route_change = route_change
    page.on_view_pop = view_pop

    def open_mail_settings(e):
        page.go("/settings/mail")

    def open_settings(e):
        page.go("/settings")

    page.go(page.route)


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

页面

页面路由是#符号后的应用程序 URL 的一部分:

如果用户未在应用程序 URL 中设置默认应用程序路由,则为/. /例如/store,所有路线都以 开头/authors/1/books/2

应用路由可以通过读取page.route属性获取,例如:

import flet
from flet import Page, Text

def main(page: Page):
    page.add(Text(f"Initial route: {page.route}"))

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

获取应用程序 URL,打开一个新的浏览器选项卡,粘贴 URL,在#to之后修改其部分/test并按 Enter。您应该看到“初始路由:/test”。

每次更改 URL 中的路由(通过编辑 URL 或使用后退/前进按钮导航浏览器历史记录)时,Flet 都会调用page.on_route_change事件处理程序:

import flet
from flet import Page, Text

def main(page: Page):
    page.add(Text(f"Initial route: {page.route}"))

    def route_change(route):
        page.add(Text(f"New route: {route}"))

    page.on_route_change = route_change
    page.update()

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

现在尝试更新 URL 哈希几次,然后使用后退/前进按钮!每次路由更改时,您应该会看到一条新消息添加到页面:

可以通过更新page.route属性以编程方式更改路线:

import flet
from flet import ElevatedButton, Page, Text

def main(page: Page):
    page.add(Text(f"Initial route: {page.route}"))

    def route_change(route):
        page.add(Text(f"New route: {route}"))

    def go_store(e):
        page.route = "/store"
        page.update()

    page.on_route_change = route_change
    page.add(ElevatedButton("Go to Store", on_click=go_store))

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

单击“转到商店”按钮,您将看到应用程序 URL 已更改,并且在浏览器历史记录中推送了一个新项目。您可以使用浏览器“返回”按钮导航到上一条路线。

页面

Flet 的Page现在不仅仅是一个页面,而是一个View容器,它们像三明治一样层叠在一起:

视图集合代表导航器历史。页面具有page.views访问视图集合的属性。

列表中的最后一个视图是页面上当前显示的视图。视图列表必须至少有一个元素(根视图)。

模拟页面更改之间的转换并在列表末尾page.route添加一个新的。View page.view

弹出集合中的最后一个视图并将路由更改为page.on_view_pop事件处理程序中的“上一个”视图以返回。

建立对路线

要构建可靠的导航,程序中必须有一个位置,该位置根据当前路线构建视图列表。换句话说,导航历史堆栈(由视图列表表示)必须是路线的函数。

这个地方是page.on_route_change事件处理程序。

让我们将所有内容放在一个完整的示例中,该示例允许在两个页面之间导航:

import flet
from flet import AppBar, ElevatedButton, Page, Text, View, colors

def main(page: Page):
    page.title = "Routes Example"

    def route_change(route):
        page.views.clear()
        page.views.append(
            View(
                "/",
                [
                    AppBar(title=Text("Flet app"), bgcolor=colors.SURFACE_VARIANT),
                    ElevatedButton("Visit Store", on_click=lambda _: page.go("/store")),
                ],
            )
        )
        if page.route == "/store":
            page.views.append(
                View(
                    "/store",
                    [
                        AppBar(title=Text("Store"), bgcolor=colors.SURFACE_VARIANT),
                        ElevatedButton("Go Home", on_click=lambda _: page.go("/")),
                    ],
                )
            )
        page.update()

    def view_pop(view):
        page.views.pop()
        top_view = page.views[-1]
        page.go(top_view.route)

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


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

尝试使用“访问商店”和“返回主页”按钮、后退/前进浏览器按钮、手动更改 URL 中的路径在页面之间导航 - 无论如何它都有效!:)

注意 为了在我们使用的页面之间“导航” page.go(route)- 一个更新的辅助方法page.route,调用page.on_route_change事件处理程序来更新视图,最后调用page.update().

注意page.on_view_pop事件处理程序的使用。它在用户单击控件中的自动“返回”按钮时触发AppBar。在处理程序中,我们从视图集合中删除最后一个元素并导航到视图的根“下方”。

路线

Flet 提供TemplateRoute- 一个基于repath库的实用程序类,它允许匹配类似 ExpressJS 的路由并解析它们的参数,例如/account/:account_id/orders/:order_id.

TemplateRoute非常适合路线更改事件:

troute = TemplateRoute(page.route)

if troute.match("/books/:id"):
    print("Book view ID:", troute.id)
elif troute.match("/account/:account_id/orders/:order_id"):
    print("Account:", troute.account_id, "Order:", troute.order_id)
else:
    print("Unknown route")

您可以在此处阅读有关repath库支持的模板语法的更多信息。

URL 策略

Flet Web 应用程序支持两种配置基于 URL 的路由的方式:

  • 散列(默认)- 读取和写入散列片段的路径。例如,fletapp.dev/#/path/to/view
  • 路径- 在没有哈希的情况下读取和写入路径。例如,fletapp.dev/path/to/view

要更改 URL 策略,请使用方法route_url_strategy参数flet.app(),例如:

flet.app(target=main, route_url_strategy="path")

Flet Server 的 URL 策略可以使用FLET_ROUTE_URL_STRATEGY环境变量进行配置,该变量可以设置为path或者hash(默认)。