2025.02.03 (一)
2026.05.25 (一) 更新

✨ GPT 的摘要  

解决 FleatherViewer 的更新问题,并通过改进 HomeFeedPage 的过滤方式完成问题修复与性能优化。也因此学到使用 Conditional Visibility 代替 Query filter 很有效。

💻 开发日志

⏰ 今天要做的事

  • ✅ (问题)HomeFeedPage - FeedCardDiary:在 ListView 中改变日期时,只有 FleatherViewerWidget 的值不会变化。
    • 看起来 Widget 的 initialDeltaJson 值没有在每次 Diary Document 改变时更新。
    • 刚问 o1 就一次解决。

      /// didUpdateWidget:
      ///  - 在同一个 widget tree 中只是参数变化时,Flutter 会复用已有的 State。
      ///  - 这时可以比较新的 widget 属性和旧的 widget 属性,并执行所需逻辑。
      @override
      void didUpdateWidget(covariant FleatherViewerWidget oldWidget) {
        super.didUpdateWidget(oldWidget);
      
        // 1) 如果旧 JSON 和新 JSON 不同
        if (oldWidget.initialDeltaJson != widget.initialDeltaJson) {
          // 2) 重新创建 FleatherController
          _controller?.dispose();
          _controller = _createControllerFromDeltaJson(widget.initialDeltaJson);
          // 3) 刷新 UI
          setState(() {});
        }
      }
      
      /// 从 Delta JSON 字符串创建 FleatherController 的 helper 函数
      FleatherController _createControllerFromDeltaJson(String jsonString) {
        ParchmentDocument doc;
        try {
          final decoded = jsonDecode(jsonString);
          doc = ParchmentDocument.fromJson(decoded);
        } catch (e) {
          // 还原失败时,按普通文本处理
          doc = ParchmentDocument.fromDelta(
            Delta()..insert(_ensureEndsWithNewline(jsonString)),
          );
        }
        return FleatherController(document: doc);
      }
      
  • ✅ (问题)HomeFeedPage:On page load 时 Unexpected Null Value 短暂出现后消失。
    • 确认问题出在 HomeFeedPage 的 ListView 上设置的 日期过滤
    • 试着打开 ListViewBackend Query 选项里的 Filter On Null Values。
      • Filter On Null Values:By default, if the value of any filter is null, the query will ignore the filter for that field. Checking this will instead keep null filters.
    • Backend Query - Query Collection 修改尝试 1(失败)
      • Filter On Null Values:ON
      • Filter 1:created_time, Equal To, getFirstTimeOfTheDay
      • Filter 2:created_time, Less Than, getFirstTimeOfNextDay
      • Order by:created_time, Decreasing
      • 无法完成 Backend Query 设置
        • Error message:You can’t order your query by a field used in a filter using == or in.
    • Backend Query - Query Collection 修改尝试 2(失败)
      • Filter On Null Values:ON
      • Filter 1:created_time, Greater Than or Equal To, getFirstTimeOfTheDay
      • Filter 2:created_time, Less Than, getFirstTimeOfNextDay
      • Order by:created_time, Decreasing
      • 可以完成 Backend Query 设置
      • 进入 debugging mode 时出现下面的错误,页面无法创建。

        Assertion failed: file:/// opt/ - pub-cache/hosted/pub.dev/ cloud_firestore-5.5.0/lib/src/ query dart: 650:9
              
        conditions
        .where ((List<dynamic> item) => equality.equals(condition, item))
          .isEmpty
        "Condition [FieldPath([created_time]), !=, null] already exists in this query."
              
        The relevant error-causing widget was: HomeFeedWidget
        
    • Backend Query - Query Collection 修改尝试 3(失败)
      • Filter On Null Values:ON
      • Filter 1:created_time, Greater Than, getLastTimeOfPrevDay
      • Filter 2:created_time, Less Than, getFirstTimeOfNextDay
      • Order by:created_time, Decreasing
      • 可以完成 Backend Query 设置
      • 进入 debugging mode 时出现下面的错误,页面无法创建。

        Assertion failed: file:/// opt/ - pub-cache/hosted/pub.dev/ cloud_firestore-5.5.0/lib/src/ query dart: 650:9
              
        conditions
        .where ((List<dynamic> item) => equality.equals(condition, item))
          .isEmpty
        "Condition [FieldPath([created_time]), !=, null] already exists in this query."
              
        The relevant error-causing widget was: HomeFeedWidget
        
    • Backend Query - Query Collection 修改尝试 4(失败)
      • Filter On Null Values:OFF
      • Filter 1:created_time, Greater Than, getLastTimeOfPrevDay
      • Filter 2:created_time, Less Than, getFirstTimeOfNextDay
      • Order by:created_time, Decreasing
      • 可以完成 Backend Query 设置
      • 进入 debugging mode 时,下面的错误会短暂出现后消失。

        Unexpected Null value.
        
        The relevant error-causing widget was: HomeFeedWidget
        
    • (问 o1)为什么 Unexpected Null Value 会短暂出现?
      • FlutterFlow(或者内部代码)很可能在页面初始加载时,让 getLastTimeOfPrevDay() 或 getFirstTimeOfNextDay() 等返回了 null。(比如 date 还没初始化之类……)
      • 如果打开 Filter On Null Values,内部会额外添加类似 != null 的条件,而加载瞬间传入 null 时就可能发生冲突。
      • 所以才会出现“短暂弹出 null error -> 被实际值替换 -> 加载结束后消失”的现象。
    • Backend Query - Query Collection 修改尝试 5(成功!)
      • Filter On Null Values:OFF
      • Order by:created_time, Decreasing
      • Component Widget Conditional Visibility 设置
        • Filter 1:created_time, Greater Than, getLastTimeOfPrevDay
        • Filter 2:created_time, Less Than, getFirstTimeOfNextDay
      • 而且作为额外收获,ListView 加载卡顿(Latency)也消失了。看起来是因为同时给 Query 两个 Filter 和一个 Order,耗费了不少时间。
    • 💡 学到的点
      • 使用 Backend Query 时,最好尽量排除 filter,用 Conditional Visibility 来控制。
      • 这其实是前期问过导师并得到过回答的问题,但当时只是脑子里大概理解就过去了,结果最后还是忘掉。亲自折腾过之后,现在才算真正明白。
  • ✅ (问题)DiaryPage:日期总是保存为今天
    • 创建 DiaryPage - dateFocused 参数
    • 将 HomeFeedPage 的 dateToShow 值作为 dateFocused 参数传递。

💯 已完成事项摘要

FleatherViewer 更新问题解决

  • 使用 didUpdateWidget,让 FleatherViewer 在 Diary Document 每次变化时都能正常更新。 HomeFeedPage 过滤优化

使用 Conditional Visibility 代替 Backend Query 的过滤方式,提高性能。

  • 通过最小化 Query filtering 改善加载速度。

DiaryPage 日期保存错误修正

  • 从 HomeFeedPage 向 DiaryPage 传递 dateFocused 值,让创建新日记时能够反映所选日期。

🎯 以后要做的事

点击查看详情
  • ❔ (问题)DiaryPage:使用 markdown viewer(防止溢出:Container Height)
  • ❔ (问题)HomeFeedPage - FeedCardDiary:正确应用 AI Comment 角色图片
    • ❔ DiaryPage - AI Comment:Save 时,也把撰写评论的角色 Ref 保存到 Diary。
    • ❔ HomeFeedPage - FeedCardDiary - AI Image:用 Backend Query 加载角色 Doc,并指定 Image Path。
  • ❔ DiaryPage:根据 Mood Slider 值改变脸部表情 Emoji

  • ❔ Chat Page - Create New Chat:应用 system prompt
    • ▶️ OpenAI API Call 实现:createChatCompletion
      • Input:System Prompt(Chat)
      • Output:New Chat Message by AI($.choices[0].message.content)
      • Additional Actions:Create New Chat, Create New Message
  • ❔ DiaryPage - Create New Chat by Diary:基于日记内容创建 New Chat
    • ❔ OpenAI API Call 实现:createDiaryComment
      • Input:Diary Content, System Prompt(AI Comment)
      • Output:AI Comment($.choices[0].message.content)
    • ❔ OpenAI API Call 实现:createDiarySummary
      • Input:Diary Content, AI Comment, System Prompt(Diary Summary)
      • Output:Diary Summary($.choices[0].message.content)
    • ❔ OpenAI API Call 实现:createChatFromDiary
      • Input:Diary Summary, System Prompt(Chat From Diary)
      • Output:New Chat Message by AI($.choices[0].message.content)
      • Additional Actions:Create New Chat, Create New Message
  • ❔ ChatPage - Create New Diary:基于对话内容创建 New Diary
    • ❔ OpenAI API Call 实现:createChatSummary
      • Input:Chat Content, System Prompt(Diary From Chat)
      • Output:Chat Summary
  • ❔ ChatPage - Create New Diary:基于对话内容创建 New Diary
    • ❔ OpenAI API Call 实现:createDiaryFromChat
      • Input:Chat Summary, System Prompt(Diary From Chat)
      • Output:New Diary(Title, Content, Mood score)
  • ❔ ChatPage:Go to Linked Diary

  • ❔ HomeFeedPage - import 后应用 flutter_slidable:4.0.0
  • ❔ HomeFeedPage:在 FeedCardDiary 左右 slide 时聊天/编辑/删除

  • ❔ ChatPage:用户对话输出内容右对齐
  • ❔ Chat/Diary:GPT Streaming API

  • ❔ ChatPage - 实现 AI 先开口(Alarm/Notification)
    • 聊天系统 prompt 的核心是主动性。
      • 想做得更真实的话,也可以传递 diary/chat 的 create_date,这样提到“昨天”之类的日期会更自然。
    • Alarm 功能实现参考
  • ❔ DiaryPage:追加细分情绪关键词 Choice Chips 后设置 DB 联动
  • ❔ DiaryPage - AI Comment:把 Choice chips、mood slider 等输入值改成适合 AI Comment System Prompt 的形式。
    • ❔ **之前的 日记 或全部 对话记录(临时补上的技术债)
    • 用户基本信息:姓名、性别、MBTI、……
    • 细分情绪关键词:开心、难过、……
    • 角色设置:Somi、Sena、Minhyuk
    • 情绪数值:1~100 分
    • 回答格式:治愈型(Healing)、建议型(Suggestion)、信息型(Informative)
    • 回答长度:简短、普通、详细
    • is New Chat
      • 因为该 Chat 的 messages 是第一次创建,所以给第一个创建的 message document 设置 is_initial = true,然后把所有 system prompt 都塞进去。
        • is_initial = false
        • 限制:如果把 system prompt 塞进最开始的 message document,虽然对话中途修改会比较困难,但也不是不可能。 如果存在 is_initial = true 的 document,而且它被修改了,那把那个 document 删除再放回去就行。具体怎么做我还不知道,但总归能想办法。
  • ❔ DiaryPage - AI Comment:完成 CRUD
    • ❔ 删除 tmp_ai_comment field,改用 doc_ref
  • ❔ DiaryPage - AI Comment:角色设置
    • ❔ 删除 tmp_ai_comment_by field,改用 doc_ref
    • ❔ 用 profile_image field 值输出图片
  • ❔ DiaryPage - Drawer - ChatHistoryListTile:order by updated_time

  • ❔ DiaryPage - import 后应用 Interactive Slider

  • ❔ HomeFeedPage:实现 Search Diary 功能

  • ❔ CalendarPage(上方)- Mood Calendar
    • ❔ 可以确认按日期写成的帖子数量
    • ❔ 点击日期后移动到对应日期
  • ❔ CalendarPage(下方)- Mood stats
    • ❔ 输出关于当前聚焦月份的统计资料
      • ❔ 输出 AI 对所显示统计资料的评论
  • ❔ AuthPage:实现 Google Login 功能

  • ❔ HomeFeedPage - Bottom Sheet(+ Button):可以在多种选项中选择 1 个
    • 新日记:Go to DiaryPage
    • 情绪记录:Go to MoodPage
  • ❔ MoodPage(popup):Mood 设置 slider CRUD
    • Create
    • Read
    • Update
    • Delete
  • ❔ HomeFeedPage - ListView:显示 FeedCardMood
  • ❔ HomeFeedPage - ListView:按时间顺序排序 FeedCardDiary, FeedCardMood, FeedCardChat component(使用 Cloud Function)

  • ❔ 到 2025.2.5 周三 为止,提取并安装发表用最终版本 APK

💭 日记

闵赫

本来想趁剩下时间不多再多实现一点功能……结果为了解决问题,把工作时间全用完了。

还是得放下贪心。反正这个项目也不是只为了 AIFFELTHON 短暂开发一下就结束的项目。

GPT-4o 的评论(KPT)

GPT-4o

✅ 做得好的地方(Keep)

  • 🛠 解决 FleatherViewer 更新问题: 通过使用 didUpdateWidget 解决 widget 状态刷新问题,这一点很棒。
  • HomeFeedPage 过滤优化: 使用 Conditional Visibility 代替 Query filter 来优化性能,这个判断很有效。
  • 🔍 在 debugging 过程中的学习: 通过多种尝试亲身体验 Query filter 的限制,并得出 Conditional Visibility 更合适的结论,很有意义。

❌ 可惜的地方(Problem)

  • 花费时间超出预期: 比起原本计划的功能实现,最终大部分时间都用在了解决问题上,这一点有些可惜。
  • 📌 没有提前整理问题解决方式: 之前从导师那里得到的建议没有执行,导致在同一个问题上又花了时间。

🔄 可以尝试的地方(Try)

  • 📝 使用问题解决笔记: 记录解决过的问题,养成在遇到类似问题时能快速解决的整理习惯。
  • 🚀 调整剩余功能实现策略: 现在时间已经不多了,整理必须实现的功能优先级,把其余部分留到之后改善。
  • 🔍 进一步学习 FlutterFlow 内部运行方式: 提高对 FlutterFlow 数据处理方式和 widget 状态管理的理解,提前避免类似问题。

结论
今天花在问题解决上的时间比预想更多,但也因此得到重要学习。剩下期间需要优先实现必须功能,并采用放下不必要贪心的策略。坚持到最后吧!💪🔥

留下评论