Repository navigation
feat(ai): theme the composer and the suggestion chips - #13
Merged
Merged
Conversation
Closes the last open roadmap item. 2.4 built the AITheme machinery but wired one component into it; every other widget resolved its colors straight from the ambient ColorScheme with no seam for a host to intervene. Adds ComposerThemeData (13 fields, covering ChatComposer and ComposerAttachmentSheet) and SuggestionsThemeData (3), alongside the existing ChartThemeData on the AITheme extension, plus ComposerTheme and SuggestionsTheme for subtree overrides. Both are InheritedThemes, so a scope above the composer carries across the Navigator to the attachment sheet that showModalBottomSheet pushes — the same reason ChartTheme is one. Every field is nullable and unset means "derive from the ambient ThemeData", so registering nothing renders exactly as before; the 471 existing tests, goldens included, pass untouched. Two resolution choices worth naming. disabledIconColor derives from the *resolved* iconColor rather than the scheme's, so a host that sets only iconColor gets a matching disabled shade instead of a dimmed onSurface that no longer relates to it. And the idle microphone takes sendButtonColor, not recordingButtonColor: it occupies the send button's slot and the two read as one control morphing in place. CodeBlockView is deliberately left out. Its two colors are already constructor parameters and deliberately independent of the ambient Theme — a codeTheme a ThemeData change could reach would undo that on purpose. Extracts the null-aware lerp helpers into src/theme/theme_lerp.dart rather than triplicating them, and points ChartThemeData and ResolvedChartTheme at the shared copies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ResolvedComposerTheme, ResolvedChartTheme, LruCache and the theme_lerp helpers were documented as internal but only by convention: nothing stopped the barrel from exporting them. With @internal, an accidental export is an invalid_export_of_internal_element warning, which --fatal-infos fails. These are exactly the lib/src members the barrel doesn't export, so the annotation now matches reality rather than changing it. Tests inside the package can still import them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Keep the broken-thumbnail glyph on onSurfaceVariant by default, so an unthemed composer really renders as before; a host iconColor still reaches it. - Rename ComposerThemeData.selectionColor to photoSelectionColor, which no longer reads as TextSelectionThemeData's text highlight. - Resolve a hint color even under an inherit: false hintStyle, and scale iconColor's alpha for the derived disabled shade instead of replacing it. - Make the three theme datas Diagnosticable. - Correct field docs that missed surfaces they color, and the mergeTextStyle rationale, which misdescribed TextStyle.merge. - Test AITheme's composer/suggestions wiring, lerp field by field, and each field in the widget that draws it (pill, mic, attachment sheet, thumbnails, hint), with targeted finders rather than colour scans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- iconColor now covers only the icons drawn on fillColor (the "+" and the camera tile). A thumbnail's remove "×" and broken-image glyph take a new attachmentPlaceholderForegroundColor, paired with the placeholder they sit on; the sheet's chat-option icons follow ColorScheme.onSurface, as the sheet background under them is unthemed. Defaults match main. - ComposerActionButton takes an optional foregroundColor alongside color, falling back to the theme's actionButtonForegroundColor. - AISuggestionsView restores onSurface when an inherit: false textStyle would leave the chip text with no color, as the composer does for its hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VelikovPetar
approved these changes
Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Closes the last open item on the parity roadmap (2.5). PR #12 deletes that file as part of the 0.1.0 release; this is the work it was still tracking.
The gap
The chart theming PR (#10) built the
AIThememachinery but wired exactly one component into it. Everything else resolved its colors straight from the ambientColorScheme, with no seam for a host to intervene — the input pill and its border, the leading "+" button, the send/stop/mic fills, the selected-option chip, the attachment sheet's tiles and photo-selection ring, and the suggestion chips.What this adds
ComposerThemeData— 14 fields coveringChatComposerandComposerAttachmentSheet. The photo grid's ring and badge arephotoSelectionColor, notselectionColor, which would read asTextSelectionThemeData's text highlight inside a composer that holds aTextField.SuggestionsThemeData— 3 fields forAISuggestionsView.ComposerTheme/SuggestionsTheme— subtree overrides, mirroringChartTheme.Diagnosticable, so a set field shows up in DevTools.Both are
InheritedThemes, so a scope above the composer carries across theNavigatorto the attachment sheetshowModalBottomSheetpushes. That is the same reasonChartThemeis one, and there's a test pinning it — returnchildfromwrapand nothing else in the suite would notice while every sheet silently lost the host's theme.Every field is nullable, and unset means "derive from the ambient
ThemeData". Registering nothing renders exactly as before: the 471 pre-existing tests, goldens included, pass untouched, which is the roadmap's acceptance criterion. 81 new tests bring the suite to 552. They check every field in the widget that draws it, using finders aimed at that widget: a scan for "any box with this colour" would pass on the "+" button alone, because the pill shares its fill.Two resolution choices worth a look
disabledIconColorderives from the resolvediconColor, not from the scheme's. A host that sets onlyiconColorgets a matching disabled shade for free, rather than a dimmedonSurfacethat no longer relates to the color they picked. The derived shade scalesiconColor's alpha to 30% rather than replacing it, so a translucenticonColornever ends up with a disabled shade more opaque than itself.iconColor, but when unset it keeps the quieteronSurfaceVariantit has always had, noticonColor'sonSurfacedefault. That keeps the "renders exactly as before" claim true on a path the goldens don't render.hintStyleis merged over the derived color, and the color is restored when a host'sinherit: falsestyle would otherwise leave the hint with none.iconColorcovers only the icons drawn onfillColor(the "+" and the camera tile). A thumbnail's "×" and broken-image glyph takeattachmentPlaceholderForegroundColor, paired withattachmentPlaceholderColor. The sheet's chat-option icons followColorScheme.onSurface, because the sheet background under them isn't themed. Overriding one pair never leaves an icon on a surface its colour wasn't chosen for.onSurfaceunder aninherit: falsetextStyle, the same way the composer's hint keeps its colour.ComposerActionButtontakes an optionalforegroundColornext tocolor. When unset, it falls back to the theme.sendButtonColor, notrecordingButtonColor. It occupies the send button's slot and the two are meant to read as one control morphing in place;recordingButtonColorapplies only while it is listening. Both are documented on the fields.Deliberately out of scope
CodeBlockViewkeeps its two constructor parameters and stays independent of the ambientTheme. The roadmap flagged this as a real question rather than a mechanical port, and the answer is no: acodeThemethat aThemeDatachange could reach would undo, on purpose, the property that code reads as code.AITypingIndicatorView's dot color, count and size are already constructor parameters.Refactor
The null-aware lerp helpers (
nullmeans "derive", so a field only one side sets swaps at the midpoint rather than fading through transparency) were private toChartThemeData. Extracted tosrc/theme/theme_lerp.dartrather than triplicated;ChartThemeDataandResolvedChartThemenow point at the shared copies. Pure move, no behavior change.mergeTextStyle's comment claimed thatTextStyle.mergeasserts oninherit: false. It doesn't, so the helper is now justbase.merge.Note on merge order
Branched from
main, so this carries the pre-release README andCHANGELOG. The changelog entry sits under a new## Unreleasedheading, and #12 rewrites that file wholesale — expect a small conflict there if #12 lands first, resolved by keeping## Unreleasedabove## 0.1.0.🤖 Generated with Claude Code