You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Flet待办清单应用中使用page.client_storage时遇循环引用等错误

Flet待办清单任务保存问题修复

问题表现

  • 添加第二个任务时触发报错:
    Exception in thread Thread-24 (add_clicked):
    ValueError: Circular reference detected
    
  • 应用重启后报错:
    'dict' object has no attribute '_build_add_commands'
    
  • 仅能成功添加1个任务,添加第二个任务时立即触发错误。

问题根源

直接将Flet的UI控件(如ft.Row、ft.Checkbox)存入page.client_storage会导致两个问题:

  1. UI控件内部存在循环引用属性,序列化存储时触发循环引用错误;
  2. 从存储中读取的是控件的字典序列化结果,并非真正的Flet控件对象,无法直接添加到页面。

修复方案

改为存储任务的纯数据(任务文本、复选框状态),而非UI控件。重启应用时,根据存储的数据重新生成对应的UI控件。

修改后的完整代码

import flet as ft


def main(page: ft.Page):
    page.window_width = 400
    page.window_height = 600
    page.bgcolor = "#141414"
    page.window_center()

    rows = list()
    # 用于存储任务数据(而非UI控件)
    rows_data = list()

    # 顶部导航栏
    page.appbar = ft.AppBar(
        bgcolor="#303030",
        leading=ft.Icon(ft.icons.CHECKLIST),
        title=ft.Text("Tareas", font_family="Roboto")
    )

    def create_task_row(task_text, is_checked=False):
        """根据任务数据创建UI行"""
        def clear(e):
            # 从数据列表和UI列表中移除对应项
            for idx, data in enumerate(rows_data):
                if data["text"] == task_text:
                    rows_data.pop(idx)
                    break
            rows.remove(row)
            # 更新存储
            page.client_storage.set("tasks_data", rows_data)
            page.update()

        checkbox = ft.Checkbox(value=is_checked)
        task = ft.Text(value=task_text, width=258)
        remove = ft.IconButton(
            icon="remove_circle",
            icon_size="32",
            icon_color="#D05050",
            on_click=clear
        )

        row = ft.Row(controls=[checkbox, task, remove])
        rows.append(row)
        return row

    def add_clicked(e):
        if new_task.value.strip() != "":
            # 创建任务数据并存储
            task_data = {"text": new_task.value.strip(), "checked": False}
            rows_data.append(task_data)
            page.client_storage.set("tasks_data", rows_data)
            
            # 创建UI行并添加到页面
            row = create_task_row(new_task.value.strip())
            task_container.controls.append(row)
            
            # 清空输入框并更新页面
            new_task.value = ""
            new_task.update()
            page.update()

    def clearall(e):
        rows.clear()
        rows_data.clear()
        task_container.controls.clear()
        page.client_storage.remove("tasks_data")
        page.update()

    # 任务输入框
    new_task = ft.TextField(hint_text="Agregar tarea", width=300)

    # 添加任务行
    page.add(ft.Row([
        new_task,
        ft.FloatingActionButton(
            icon="add",
            on_click=add_clicked,
            bgcolor="#4B90BE",
            scale=0.95
        )
    ]))

    # 清空所有任务按钮
    page.add(ft.FloatingActionButton(
        icon=ft.icons.CLEANING_SERVICES_ROUNDED,
        on_click=clearall,
        bgcolor="#4B90BE",
        scale=0.95
    ))

    # 用于容纳任务行的容器
    task_container = ft.Column()
    page.add(task_container)

    # 从存储加载保存的任务数据并重建UI
    saved_tasks_data = page.client_storage.get("tasks_data") or []
    rows_data.extend(saved_tasks_data)
    for task_data in saved_tasks_data:
        row = create_task_row(task_data["text"], task_data["checked"])
        task_container.controls.append(row)
    
    page.update()


ft.app(target=main)

关键修改点

  1. 新增rows_data列表存储任务的纯数据(字典格式,包含text和checked字段);
  2. 抽离create_task_row函数,统一处理任务UI行的创建和删除逻辑;
  3. 存储和读取时操作的是任务数据而非UI控件;
  4. 重启应用时,根据存储的数据重新生成UI控件并添加到页面;
  5. 删除任务时同步更新数据列表和存储,确保数据一致性。

内容的提问来源于stack exchange,提问作者Artis Wize

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 01:12:07