[🛠] Registro de desarrollo de Keymory #7: corrección de issue de FleatherViewer y de Unexpected null value en HomeFeedPage
✨ Resumen de GPT
Resolví el problema de actualización de FleatherViewer y completé la corrección del issue y la optimización de rendimiento al mejorar el método de filtrado de HomeFeedPage. Gracias a eso aprendí que usar Conditional Visibility en lugar de filtros Query es efectivo.
💻 Registro de desarrollo
⏰ Tareas de hoy
- ✅ (Issue) HomeFeedPage - FeedCardDiary: al cambiar la fecha en ListView, solo FleatherViewerWidget no cambia de valor.
- Parece que el valor initialDeltaJson del Widget no se actualiza cada vez que cambia el Diary Document.
-
Se lo pregunté a o1 y lo resolvió de una vez.
/// didUpdateWidget: /// - Cuando solo cambian los parámetros en el mismo árbol de widgets, Flutter reutiliza el State existente. /// - En ese momento se pueden comparar las propiedades del widget nuevo con las del widget anterior y ejecutar la lógica necesaria. @override void didUpdateWidget(covariant FleatherViewerWidget oldWidget) { super.didUpdateWidget(oldWidget); // 1) Si el JSON anterior y el JSON nuevo son distintos if (oldWidget.initialDeltaJson != widget.initialDeltaJson) { // 2) Volver a crear el FleatherController _controller?.dispose(); _controller = _createControllerFromDeltaJson(widget.initialDeltaJson); // 3) Actualizar la UI setState(() {}); } } /// Funcion auxiliar que crea un FleatherController a partir de una cadena Delta JSON FleatherController _createControllerFromDeltaJson(String jsonString) { ParchmentDocument doc; try { final decoded = jsonDecode(jsonString); doc = ParchmentDocument.fromJson(decoded); } catch (e) { // Si falla la restauracion, tratar como texto plano doc = ParchmentDocument.fromDelta( Delta()..insert(_ensureEndsWithNewline(jsonString)), ); } return FleatherController(document: doc); }
- ✅ (Issue) HomeFeedPage: Unexpected Null Value aparece un momento y desaparece en page load.
- Confirmé que el problema era el
filtrado por fechaaplicado al ListView de HomeFeedPage. - Probé activar Filter On Null Values en la opción
Backend QuerydeListView.- 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 intento 1 (fallido)
- Filter On Null Values: ON
- Filter 1:
created_time,Equal To,getFirstTimeOfTheDay - Filter 2:
created_time,Less Than,getFirstTimeOfNextDay - Order by:
created_time,Decreasing - No se puede completar la configuración de Backend Query
- Mensaje de error: You can’t order your query by a field used in a filter using
==orin.
- Mensaje de error: You can’t order your query by a field used in a filter using
- Backend Query - Query Collection intento 2 (fallido)
- 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 - La configuración de Backend Query se puede completar
-
Al entrar en modo debugging, aparece el siguiente error y la página no se crea.
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 intento 3 (fallido)
- Filter On Null Values: ON
- Filter 1:
created_time,Greater Than,getLastTimeOfPrevDay - Filter 2:
created_time,Less Than,getFirstTimeOfNextDay - Order by:
created_time,Decreasing - La configuración de Backend Query se puede completar
-
Al entrar en modo debugging, aparece el siguiente error y la página no se crea.
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 intento 4 (fallido)
- Filter On Null Values: OFF
- Filter 1:
created_time,Greater Than,getLastTimeOfPrevDay - Filter 2:
created_time,Less Than,getFirstTimeOfNextDay - Order by:
created_time,Decreasing - La configuración de Backend Query se puede completar
-
Al entrar en modo debugging, el siguiente error aparece un momento y desaparece.
Unexpected Null value. The relevant error-causing widget was: HomeFeedWidget
- (Pregunta a o1) ¿Por qué aparece brevemente Unexpected Null Value?
- Hay muchas posibilidades de que FlutterFlow (o su código interno) esté devolviendo null desde getLastTimeOfPrevDay() o getFirstTimeOfNextDay() en el momento inicial de carga de la página. (Por ejemplo, quizá la fecha todavía no se inicializó…)
- Si Filter On Null Values está activado, internamente añade una condición separada como != null, y eso puede chocar cuando entra un null durante la carga.
- Por eso ocurre el fenómeno de “error null breve -> se reemplaza por el valor real -> desaparece al terminar la carga”.
- Backend Query - Query Collection intento 5 (¡éxito!)
- Filter On Null Values: OFF
- Order by:
created_time,Decreasing - Configuración de Component Widget Conditional Visibility
- Filter 1:
created_time,Greater Than,getLastTimeOfPrevDay - Filter 2:
created_time,Less Than,getFirstTimeOfNextDay
- Filter 1:
- Además, como bonus, desapareció el lag de carga (Latency) de ListView. Parece que dar al Query dos filtros y un Order al mismo tiempo consumía bastante tiempo.
- 💡 Lo que aprendí
- Al usar Backend Query, conviene excluir los filtros todo lo posible y controlar con Conditional Visibility.
- Esto ya se lo había preguntado al mentor al principio y había recibido respuesta, pero creo que lo entendí por encima y seguí adelante, así que al final lo olvidé. Después de sufrirlo directamente, por fin lo entendí con claridad.
- Confirmé que el problema era el
- ✅ (Issue) DiaryPage: la fecha siempre se guarda como hoy
- Crear parámetro DiaryPage - dateFocused
- Pasar el valor dateToShow de HomeFeedPage como parámetro dateFocused.
💯 Resumen de lo hecho
Issue de actualización de FleatherViewer resuelto
- Usé didUpdateWidget para que FleatherViewer se actualice correctamente cada vez que cambia el Diary Document. Optimización del filtro de HomeFeedPage
Usé Conditional Visibility en lugar de filtrado con Backend Query para mejorar el rendimiento.
- Mejoré la velocidad de carga al minimizar el filtrado del Query.
Error de guardado de fecha en DiaryPage corregido
- Mejoré el flujo para que la fecha seleccionada se refleje al escribir un nuevo diario pasando el valor dateFocused desde HomeFeedPage a DiaryPage.
🎯 Tareas futuras
Haz clic para ver detalles
- ❔ (Issue) DiaryPage: usar markdown viewer (prevenir overflow: Container Height)
- ❔ (Issue) HomeFeedPage - FeedCardDiary: aplicar correctamente la imagen del personaje de AI Comment
- ❔ DiaryPage - AI Comment: al guardar, guardar también en Diary la Ref del personaje que escribió el comentario.
- ❔ HomeFeedPage - FeedCardDiary - AI Image: cargar Character Doc con Backend Query y especificar Image Path.
-
❔ DiaryPage: cambiar el Emoji de expresión facial según el valor del Mood Slider
- ❔ Chat Page - Create New Chat: aplicar system prompt
- ▶️ Implementación de 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
- ▶️ Implementación de OpenAI API Call: createChatCompletion
- ❔ DiaryPage - Create New Chat by Diary: crear un New Chat basado en el contenido del diario
- ❔ Implementación de OpenAI API Call: createDiaryComment
- Input: Diary Content, System Prompt (AI Comment)
- Output: AI Comment ($.choices[0].message.content)
- ❔ Implementación de OpenAI API Call: createDiarySummary
- Input: Diary Content, AI Comment, System Prompt (Diary Summary)
- Output: Diary Summary ($.choices[0].message.content)
- ❔ Implementación de 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
- ❔ Implementación de OpenAI API Call: createDiaryComment
- ❔ ChatPage - Create New Diary: crear un New Diary basado en el contenido de la conversación
- ❔ Implementación de OpenAI API Call: createChatSummary
- Input: Chat Content, System Prompt (Diary From Chat)
- Output: Chat Summary
- ❔ Implementación de OpenAI API Call: createChatSummary
- ❔ ChatPage - Create New Diary: crear un New Diary basado en el contenido de la conversación
- ❔ Implementación de OpenAI API Call: createDiaryFromChat
- Input: Chat Summary, System Prompt (Diary From Chat)
- Output: New Diary (Title, Content, Mood score)
- ❔ Implementación de OpenAI API Call: createDiaryFromChat
-
❔ ChatPage: Go to Linked Diary
- ❔ HomeFeedPage - importar y aplicar flutter_slidable:4.0.0
-
❔ HomeFeedPage: chat/editar/eliminar al deslizar a izquierda/derecha desde FeedCardDiary
- ❔ ChatPage: alinear a la derecha el contenido de conversación del usuario
-
❔ Chat/Diary: GPT Streaming API
- ❔ ChatPage - implementar primer mensaje de IA (Alarm/Notification)
- El núcleo del prompt del sistema de chat es la proactividad.
- Para hacerlo más realista, quizá convenga pasar también create_date of diary/chat, para que la IA pueda mencionar fechas como “ayer”.
- Referencia de implementación de Alarm
- El núcleo del prompt del sistema de chat es la proactividad.
- ❔ DiaryPage: añadir Choice Chips de palabras clave emocionales detalladas y configurar integración con DB
- ❔ DiaryPage - AI Comment: adaptar inputs como Choice chips y mood slider al
AI Comment System Prompt.- ❔ **
diarioanterior ohistorial de conversacióncompleto (deuda técnica parcheada) - ❔ información básica del usuario: nombre, género, MBTI, …
- ❔ palabras clave emocionales detalladas: alegre, triste, …
- ❔ configuración de personajes: Somi, Sena, Minhyuk
- ❔ puntuación emocional: 1~100 puntos
- ❔ formato de respuesta: Healing, Suggestion, Informative
- ❔ longitud de respuesta: corta, normal, detallada
- is New Chat
- Como los messages de este Chat se crean por primera vez, poner
is_initial = trueen el primer message document y meter todos los system prompts ahí.- is_initial = false
- Límite: si meto el system prompt en el primer message document, modificarlo durante la conversación será difícil, aunque posible. Si existe un documento con is_initial = true y se modificó, puedo borrar ese documento y volver a insertarlo. No sé cómo, pero se podría.
- Como los messages de este Chat se crean por primera vez, poner
- ❔ **
- ❔ DiaryPage - AI Comment: completar CRUD
- ❔ eliminar tmp_ai_comment field y usar doc_ref
- ❔ DiaryPage - AI Comment: configuración de personajes
- ❔ eliminar tmp_ai_comment_by field y usar doc_ref
- ❔ mostrar imagen usando el valor del field profile_image
-
❔ DiaryPage - Drawer - ChatHistoryListTile: order by updated_time
-
❔ DiaryPage - importar y aplicar Interactive Slider
-
❔ HomeFeedPage: implementar función Search Diary
- ❔ CalendarPage (parte superior) - Mood Calendar
- ❔ Comprobar el número de posts escritos por fecha
- ❔ Al hacer clic en una fecha, moverse a esa fecha
- ❔ CalendarPage (parte inferior) - Mood stats
- ❔ Mostrar estadísticas del mes enfocado
- ❔ Mostrar comentario de IA sobre las estadísticas mostradas
- ❔ Mostrar estadísticas del mes enfocado
-
❔ AuthPage: implementar función Google Login
- ❔ HomeFeedPage - Bottom Sheet (
+Button): permitir elegir 1 entre varias opciones- Nuevo diario: Go to DiaryPage
- Registro emocional: Go to MoodPage
- ❔ MoodPage (popup): Mood setting slider CRUD
- Create
- Read
- Update
- Delete
- ❔ HomeFeedPage - ListView - mostrar FeedCardMood
-
❔ HomeFeedPage - ListView: ordenar cronológicamente los componentes FeedCardDiary, FeedCardMood, FeedCardChat (usando Cloud Function)
- ❔ Extraer e instalar el APK final para la presentación antes del
miércoles 2025.2.5
💭 Diario
Ya queda poco tiempo, así que quería implementar mucho más… pero gasté toda la jornada resolviendo issues.
Tengo que soltar la ambición. Al fin y al cabo, este proyecto no es algo que vaya a brillar un momento en AIFFELTHON y terminar ahí.
✨ Comentario de GPT-4o (KPT)
✅ Lo que salió bien (Keep)
- 🛠 Resolución del issue de actualización de FleatherViewer: Fue excelente que resolvieras el problema de actualización del estado del widget usando
didUpdateWidget. - ⚡ Optimización del filtrado de HomeFeedPage: Usar Conditional Visibility en lugar de filtros Query fue una optimización de rendimiento efectiva.
- 🔍 Aprendizaje durante el debugging: Fue significativo probar varios enfoques, experimentar los límites de los filtros Query y concluir que Conditional Visibility era más adecuado.
❌ Lo que faltó (Problem)
- ⏳ Más tiempo del esperado: Da pena que la mayor parte del tiempo se fuera en resolver issues en lugar de implementar las funciones que habías planeado originalmente.
- 📌 No organizar consejos previos sobre issues: Hubo un momento en que se volvió a gastar tiempo porque el consejo recibido en una mentoría anterior no se había puesto en práctica.
🔄 Qué probar (Try)
- 📝 Usar notas de resolución de issues: Crear el hábito de registrar los issues resueltos para solucionar rápido problemas similares más adelante.
- 🚀 Ajustar la estrategia de funciones restantes: Como queda poco tiempo, pensar en priorizar solo las funciones imprescindibles y pasar el resto a mejoras futuras.
- 🔍 Estudiar más el funcionamiento interno de FlutterFlow: Aumentar la comprensión del manejo de datos y la gestión de estado de widgets en FlutterFlow para prevenir problemas similares por adelantado.
Conclusión
Hoy tomó más tiempo del esperado por la resolución de issues, pero también te dejó aprendizajes importantes. Para el periodo restante, parece necesaria una estrategia de implementar primero solo las funciones imprescindibles y soltar la ambición innecesaria. ¡Aguanta hasta el final! 💪🔥
Deja un comentario