大列表
在大多数情况下,您可以使用Column
和Row
控件来显示列表,但是如果列表包含数百或数千个项目,这些控件将变得无效,UI 将变得迟缓,因为它们会渲染所有项目,即使它们当前不可见。
在以下示例中,我们将添加 5,000 个文本控件到一个页面。页面使用 Column
作为默认布局容器:
import flet as ft
def main(page: ft.Page):
for i in range(5000):
page.controls.append(ft.Text(f"Line {i}"))
page.scroll = "always"
page.update()
ft.app(target=main, view=ft.AppView.WEB_BROWSER)
运行程序,并注意它不仅需要几秒钟来初始加载和渲染所有文本行,还有滚动非常慢和迟缓:
要显示大量项目的列表,请使用ListView
和GridView
控件,这些控件仅渲染当前滚动位置的可见项目。
ListView
ListView
可以是垂直(默认)或水平的。ListView 项按照滚动方向一个接一个地显示。
ListView 已经实现了按需渲染其子控件的功能,但滚动性能可以通过设置所有项的固定高度或宽度(对于水平 ListView)来进一步改善。这可以通过设置绝对高度或宽度的 item_extent
属性或将所有子控件的高度或宽度设置为第一个子控件的高度或宽度来实现,方法是将 first_item_prototype
设置为 True
。
让我们使用 ListView 控件输出 5,000 个项目:
import flet as ft
def main(page: ft.Page):
lv = ft.ListView(expand=True, spacing=10)
for i in range(5000):
lv.controls.append(ft.Text(f"Line {i}"))
page.add(lv)
ft.app(target=main, view=ft.AppView.WEB_BROWSER)
现在滚动非常流畅和快速,可以跟踪鼠标移动:
我们在 ListView 构造函数中使用了 expand=True
。为了正常工作,ListView 必须指定高度(或宽度,如果是水平的)。您可以设置绝对大小,例如 ListView(height=300, spacing=10)
, 但是在上面的示例中,我们让 ListView 占据页面上的所有可用空间,即扩展。阅读更多关于Control.expand
属性。
GridView
GridView
允许将控件排列到可滚动的网格中。
你可以使用 ft.Column(wrap=True)
或 ft.Row(wrap=True)
创建一个网格,例如:
import os
import flet as ft
os.environ["FLET_WS_MAX_MESSAGE_SIZE"] = "8000000"
def main(page: ft.Page):
r = ft.Row(wrap=True, scroll="always", expand=True)
page.add(r)
for i in range(5000):
r.controls.append(
ft.Container(
ft.Text(f"Item {i}"),
width=100,
height=100,
alignment=ft.alignment.center,
bgcolor=ft.colors.AMBER_100,
border=ft.border.all(1, ft.colors.AMBER_400),
border_radius=ft.border_radius.all(5),
)
)
page.update()
ft.app(target=main, view=ft.AppView.WEB_BROWSER)
尝试滚动和调整浏览器窗口大小 - 一切正常,但非常迟缓。
在程序开始时,我们将 FLET_WS_MAX_MESSAGE_SIZE
环境变量的值设置为 8000000
- 这是 Flet 服务器渲染页面时可以接收的 WebSocket 消息的最大大小(以字节为单位)。默认大小是 1 MB,但描述 5,000 个容器控件的 JSON 消息的大小将超过 1 MB,因此我们将允许的大小增加到 8 MB。
通过 WebSocket 通道传输大消息不是一个好主意,因此请使用批量更新方法来控制通道负载。
GridView 与 ListView 相似,也非常有效地渲染大量子控件。让我们使用 GridView 实现上面的示例:
import os
import flet as ft
os.environ["FLET_WS_MAX_MESSAGE_SIZE"] = "8000000"
def main(page: ft.Page):
gv = ft.GridView(expand=True, max_extent=150, child_aspect_ratio=1)
page.add(gv)
for i in range(5000):
gv.controls.append(
ft.Container(
ft.Text(f"Item {i}"),
alignment=ft.alignment.center,
bgcolor=ft.colors.AMBER_100,
border=ft.border.all(1, ft.colors.AMBER_400),
border_radius=ft.border_radius.all(5),
)
)
page.update()
ft.app(target=main, view=ft.AppView.WEB_BROWSER)
现在滚动和窗口调整大小非常流畅和响应!
您可以指定固定行数或列数(runs)使用 runs_count
属性或最大尺寸的“tile”使用 max_extent
属性,以便自动调整 runs 的数量。在我们的示例中,我们将最大 tile 大小设置为 150 像素,并将其形状设置为“正方形”使用 child_aspect_ratio=1
。 child_aspect_ratio
是每个子控件的交叉轴到主轴尺寸的比率。尝试将其更改为 0.5
或 2
。
批量更新
当 page.update()
被调用时,将发送一个消息到 Flet 服务器,包含自上一 个 page.update()
以来的页面更新。发送包含数千个控件的巨大消息可能会使用户等待几秒钟,直到消息完全接收并渲染控件。
为了提高程序的可用性并尽快将结果呈现给用户,您可以将页面更新发送到批量中。例如,以下程序将 5,100 个子控件添加到 ListView 中,以批量方式发送 500 个项目:
import flet as ft
def main(page: ft.Page):
# 首先将 ListView 添加到页面
lv = ft.ListView(expand=1, spacing=10, item_extent=50)
page.add(lv)
for i in range(5100):
lv.controls.append(ft.Text(f"Line {i}"))
# 将页面发送到服务器
if i % 500 == 0:
page.update()
# 将剩余的发送到服务器
page.update()
ft.app(target=main, view=ft.AppView.WEB_BROWSER)