跳到主要内容

导航和路由 Navigation and routing

导航和路由是单页面应用(SPA)的基本功能,它允许将应用用户界面组织成虚拟页面(视图),并在应用URL反映应用的当前状态时进行“导航”到这些页面。

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

在Flet中,将导航和路由添加到应用程序中比预期要费力一些,因为它的实现基于Flutter的Navigator 2.0 API,并且需要用“Page和Views”替换Flet的“Page”抽象。Flutter的新导航和路由API具有重大改进,例如:

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

探索上述示例的源代码

页面路由

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

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

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

import flet as ft

def main(page: ft.Page):
page.add(ft.Text(f"初始路由:{page.route}"))

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

获取应用程序URL,打开一个新的浏览器选项卡,粘贴URL,修改#后面的部分为/test,然后按回车键。您应该看到"初始路由:/test"。

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

import flet as ft

def main(page: ft.Page):
page.add(ft.Text(f"初始路由:{page.route}"))

def route_change(e: ft.RouteChangeEvent):
page.add(ft.Text(f"新路由:{e.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"初始路由:{page.route}"))

def route_change(e: ft.RouteChangeEvent):
page.add(ft.Text(f"新路由:{e.route}"))

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

page.on_route_change = route_change
page.add(ft.ElevatedButton("访问商店", on_click=go_store))

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

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

页面视图

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

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

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

为了模拟页面之间的过渡,请更改page.route并将一个新的View添加到page.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("访问商店", 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("返回首页", 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)

使用"访问商店"和"返回首页"按钮、后退/前进浏览器按钮、手动更改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库支持的模板语法的更多信息。

Web的URL策略

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

  • Path(默认)-路径在没有哈希的情况下进行读取和写入。例如:fletapp.dev/path/to/view
  • Hash -路径在hash片段中进行

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

ft.app(target=main, route_url_strategy="hash")

可以使用FLET_ROUTE_URL_STRATEGY环境变量来配置Flet服务器的URL策略,可以将其设置为path(默认)或hash