fix: Rendering and Life-cycle Errors in Chat Message Streaming, Issue… #141
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
… link: #140
This addresses a critical bug that caused two distinct errors in the chat interface, primarily when rendering streaming markdown content with complex elements like code blocks.
Problem: When receiving a streaming response from the LLM, especially one containing markdown with code blocks, the application would frequently crash with a !debugNeedsLayout assertion error. This indicated that a widget was being painted or processed while its layout was still being calculated, a common issue with frequent and inefficient state updates on complex widgets.
Root Cause: The Markit widget, which renders markdown, was a StatelessWidget that rebuilt the entire MarkdownBlock on every single data chunk received from the stream. This caused significant performance bottlenecks and race conditions in Flutter's rendering pipeline.
Solution Implemented:
A two-part solution was implemented to resolve both issues and improve overall performance and stability.
Unique Keys: Added unique ValueKeys to MessageBubble widgets within ChatMessageContent. This ensures Flutter's reconciliation algorithm can correctly identify and update widgets during rebuilds, preventing unnecessary tree mutations.
Lifecycle-Aware Caching in Markit Widget:
The _MarkitState was refactored to remove all context-dependent logic from initState.
The markdown rendering logic now resides entirely within the build method.
The rendered MarkdownBlock widget is cached in a state variable (_cachedMarkdown).
This cache is only invalidated and rebuilt if:
The input data string changes.
The Theme.of(context).brightness changes (to support dark/light mode switching).
This approach ensures that expensive markdown parsing and rendering only occur when absolutely necessary, respecting the widget lifecycle and eliminating both the layout and dependency errors.
These changes have made the chat interface more robust, performant, and free of rendering-related crashes, especially during real-time message streaming.