跳到主要内容

导航与路由

· 阅读需 7 分钟
Feodor Fitsner
Flet创始人兼开发者

Flet 0.1.42已发布,其中包含导航和路由功能!

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

对于移动应用程序,导航和路由还可以作为特定应用程序部分的深度链接。

为了将导航和路由添加到Flet中,我们花费了比预期更多的努力。这个实现是基于Flutter的Navigator 2.0 API,并需要用“Page和Views”替换Flet的“Page”抽象。Flutter的新导航和路由API有很多改进,包括:

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

探索上述示例的源代码

页面路由

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

默认的应用程序路由(如果用户没有在应用程序URL中设置)是/。所有路由都以/开头,例如/store/authors/1/books/2

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

import flet as ft

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

ft.app(target=main, view=ft.AppView.WEB_BROWSER)

获取应用程序URL,打开一个新的浏览器标签页,粘贴URL,将#后面的部分修改为/test,然后按回车键。您应该看到"Initial route: /test"。

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

import flet as ft

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

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

page.on_route_change = route_change
page.update()

ft.app(target=main, view=ft.AppView.WEB_BROWSER)

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

路由可以通过在代码中更新page.route属性来进行程序化更改:

import flet as ft

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

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

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

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

ft.app(target=main, view=ft.AppView.WEB_BROWSER)

点击"Go to Store"按钮,您将看到应用程序URL已更改,并且在浏览器历史记录中推入了一个新项。您可以使用浏览器的"返回"按钮导航到以前的路由。

页面视图

Flet的Page现在不仅仅是一个单独的页面,而是一个包含叠加在一起的View的容器,就像三明治一样:

视图的集合表示导航器历史记录。Page具有page.views属性以访问视图集合。

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

要模拟页面之间的过渡,请更改page.route并在page.view列表的末尾添加一个新的View

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

在路由更改时构建视图

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

这个位置是page.on_route_change事件处理程序。

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

import flet as ft

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

def route_change(route):
page.views.clear()
page.views.append(
ft.View(
"/",
[
ft.AppBar(title=ft.Text("Flet app"), bgcolor=ft.colors.SURFACE_VARIANT),
ft.ElevatedButton("Visit Store", on_click=lambda _: page.go("/store")),
],
)
)
if page.route == "/store":
page.views.append(
ft.View(
"/store",
[
ft.AppBar(title=ft.Text("Store"), bgcolor=ft.colors.SURFACE_VARIANT),
ft.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)


ft.app(target=main, view=ft.AppView.WEB_BROWSER)

尝试使用"Visit Store"和"Go Home"按钮、后退/前进浏览器按钮、手动更改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库支持的模板语法的更多信息。

今天就介绍这么多!

试用一下Flet,并告诉我们您的想法!