
Flet 框架为开发者提供了一个名为 page.client_storage 的机制,用于在客户端(通常是浏览器或桌面应用的本地存储)持久化数据。这类似于 Web 开发中的 localStorage,它允许应用在会话结束后依然保留少量数据,以便在下次启动时恢复。
page.client_storage 是一个简单的键值对存储,其基本操作包括:
在尝试使用 page.client_storage 存储 Flet UI 控件(如 ft.Row、ft.Text、ft.Checkbox 等)时,开发者经常会遇到 ValueError: Circular reference detected 或 'dict' object has no attribute '_build_add_commands' 等错误。
这是因为 Flet 控件是复杂的 Python 对象,它们内部包含对其他控件、父控件、事件处理器等的引用。这些复杂的内部结构导致它们无法直接被 JSON 序列化(page.client_storage 在底层通常依赖于 JSON 序列化来存储数据)。当你尝试存储一个包含循环引用的对象时,序列化器会陷入无限循环,从而抛出 Circular reference detected 错误。即使没有循环引用,Flet 控件对象本身也包含许多非数据属性和方法,这些都不是 client_storage 期望存储的简单数据。
核心原则:page.client_storage 应该存储数据,而不是 UI 控件。 你应该存储用户输入的数据(例如,待办事项的文本内容),然后在应用启动时,根据这些存储的数据动态地重建 UI 控件。
我们将基于一个待办事项应用来演示如何正确使用 page.client_storage。
我们的 To-Do List 应用将包含:
数据存储策略:
添加任务与存储:
加载已存储任务:
清除所有任务:
import flet as ft
def main(page: ft.Page):
# 页面基本设置
page.window_center()
page.window_width = 400
page.window_height = 600
page.title = "Flet 持久化待办事项"
page.bgcolor = "#141414"
# 1. 定义清除所有任务的函数
def all_clear(e):
# 清除 client_storage 中的所有数据
page.client_storage.clear()
# 清除 UI 中的所有任务控件
task_column.controls.clear()
# 更新页面
page.update()
# 设置浮动操作按钮用于清除所有任务
page.floating_action_button = ft.FloatingActionButton(
icon=ft.icons.CLEANING_SERVICES_ROUNDED,
on_click=all_clear,
bgcolor="#4B90BE",
scale=0.95
)
# 2. 定义添加任务的函数
def add_task(e):
# 确保输入框不为空
if tf.value:
# 增加添加按钮的data属性作为任务的唯一ID计数器
# add_button.data 初始值为 0,每次点击增加
add_button.data += 1
# 获取任务文本
task_text = tf.value
# 创建一个 ft.Text 控件来显示任务
# 注意:这里我们创建了控件,但不会直接存储它
task_control = ft.Text(task_text, color=ft.colors.WHITE)
# 将任务控件添加到显示任务的列中
task_column.controls.append(task_control)
# 将任务文本存储到 client_storage
# 使用 f"task_{add_button.data}" 作为唯一的键
page.client_storage.set(f"task_{add_button.data}", task_text)
# 清空输入框
tf.value = ""
# 更新页面以显示新任务和清空的输入框
page.update()
# 打印当前所有存储的键,用于调试
print("当前存储的键:", page.client_storage.get_keys(''))
# 如果输入框为空,则不执行任何操作
else:
tf.focus() # 重新聚焦输入框
page.update()
# 任务输入框
tf = ft.TextField(
label='输入新任务',
expand=True, # 允许文本框扩展以填充可用空间
color=ft.colors.WHITE,
hint_text_style=ft.TextStyle(color=ft.colors.GREY_500)
)
# 添加任务按钮
# data=0 用于作为任务ID的计数器
add_button = ft.IconButton(
icon=ft.icons.ADD,
on_click=add_task,
data=0,
icon_color="#4B90BE",
tooltip="添加任务"
)
# 用于显示所有任务的列
task_column = ft.Column(
controls=[],
spacing=10 # 任务之间的间距
)
# 将输入框和添加按钮添加到页面顶部
page.add(
ft.Row(
controls=[
tf,
add_button
],
alignment=ft.MainAxisAlignment.CENTER
)
)
# 将任务显示列添加到页面
page.add(
ft.Container(
content=task_column,
expand=True, # 允许任务列表扩展
padding=ft.padding.all(10),
alignment=ft.alignment.top_left
)
)
# 3. 应用启动时加载已存储的任务
# 检查 client_storage 中是否存在以 'task_' 开头的键
stored_keys = page.client_storage.get_keys('task_')
if stored_keys:
# 对键进行排序,以确保任务按添加顺序显示
# 提取数字部分进行排序,例如 'task_10' -> 10
sorted_keys = sorted(stored_keys, key=lambda k: int(k.split('_')[1]))
for key in sorted_keys:
# 获取存储的任务文本
task_text = page.client_storage.get(key)
if task_text: # 确保获取到的值不为空
# 根据任务文本创建 ft.Text 控件
task_control = ft.Text(task_text, color=ft.colors.WHITE)
# 将控件添加到任务显示列中
task_column.controls.append(task_control)
# 更新 add_button.data,确保新的任务ID不会与现有ID冲突
# 找到最大的ID,并将其设置为 add_button.data 的起始值
current_max_id = int(key.split('_')[1])
if current_max_id > add_button.data:
add_button.data = current_max_id
# 加载完成后,将 add_button.data 增加1,为下一个新任务做准备
add_button.data += 1
# 更新页面以显示加载的任务
page.update()
# 运行 Flet 应用
ft.app(target=main)唯一键管理: 在上述示例中,我们使用 add_button.data 作为计数器来生成唯一的任务键(例如 task_1, task_2)。这是一种简单有效的方法。在应用启动时,需要遍历所有已存储的键,找出最大的数字,并更新 add_button.data,以确保新添加的任务不会覆盖现有任务。
单个任务的删除: 示例中只实现了“清除所有任务”。如果需要删除单个任务,则在创建每个任务的 UI 控件时,需要将其对应的 client_storage 键关联起来(例如,通过 control.data 属性)。当删除按钮被点击时,可以根据这个键从 client_storage 中移除对应的数据,并从 UI 中移除该控件。
更复杂的数据结构: 如果需要存储更复杂的数据(例如,任务的文本、完成状态、截止日期等),可以考虑将每个任务存储为一个字典,然后将所有任务的字典列表进行 JSON 序列化,作为一个单一的值存储到 client_storage 中。
import json
# 存储
tasks_data = [{"text": "Buy milk", "completed": False}, {"text": "Learn Flet", "completed": True}]
page.client_storage.set("all_tasks", json.dumps(tasks_data))
# 读取
stored_json = page.client_storage.get("all_tasks")
if stored_json:
loaded_tasks = json.loads(stored_json)
# 根据 loaded_tasks 重新构建 UI这种方式更易于管理和更新,因为它将所有相关数据存储在一个条目下。
page.client_storage 是 Flet 应用实现数据持久化的强大工具,但正确使用它至关重要。核心在于理解它是一个键值对存储,只能存储可序列化的数据,而非复杂的 Flet UI 控件对象。通过将数据与 UI 逻辑分离,并在应用启动时动态重建 UI,我们可以有效地利用 client_storage 来提升用户体验,确保应用数据的持久性。
以上就是Flet 应用中利用 page.client_storage 实现数据持久化教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号