Skip to content

feat(ui): factory slots for message item leading, header, footer#2710

Open
xsahil03x wants to merge 3 commits into
v10.0.0from
feat/message-item-leading-slot
Open

feat(ui): factory slots for message item leading, header, footer#2710
xsahil03x wants to merge 3 commits into
v10.0.0from
feat/message-item-leading-slot

Conversation

@xsahil03x
Copy link
Copy Markdown
Member

@xsahil03x xsahil03x commented Jun 2, 2026

Summary

  • Adds per-slot factory overrides for the message item's leading, header, and footer sub-components. Previously, the only override was messageItem, which forced integrators to replace the entire item even when they only wanted to swap the avatar, the pinned/reminder annotations, or the timestamp row.
  • Closes a parity gap with the other SDKs that already expose this:
    • iOS SwiftUI — makeUserAvatarView (ViewFactory.swift:120-123)
    • Android Compose — MessageAuthor / MessageTop / MessageBottom on ChatComponentFactory
    • React Native — MessageAuthor / MessageHeader / MessageFooter on the components context
  • Motivated by docs-content PR #1241 review — Martin's question about a leading/trailing customization path.

What's in this PR

Commit 1 — refactor(ui): extract StreamMessageLeading from message item

Extracts the message item's inline avatar block (stream_message_item.dart:433-443) into a new StreamMessageLeading widget under components/, mirroring the existing StreamMessageHeader and StreamMessageFooter. DefaultStreamMessageItem now composes leading, header, and footer symmetrically.

Commit 2 — feat(ui): factory slots for message item leading, header, footer

Refactors each of the three sub-components into the StreamMessageItem shape (public widget → Default… impl → …Props) and registers three new keys in streamChatComponentBuilders:

StreamComponentBuilder<StreamMessageLeadingProps>? messageLeading,
StreamComponentBuilder<StreamMessageHeaderProps>? messageHeader,
StreamComponentBuilder<StreamMessageFooterProps>? messageFooter,

The widgets are also exported from the package barrel so integrators can reference the props and default classes from their custom builders.

Usage

StreamChat(
  client: client,
  componentBuilders: StreamComponentBuilders(
    extensions: streamChatComponentBuilders(
      // Swap just the avatar.
      messageLeading: (context, props) =>
          MyPresenceAvatar(user: props.message.user),
      // Add a custom annotation while keeping the defaults.
      messageHeader: (context, props) =>
          DefaultStreamMessageHeader(props: props),
      // Replace the timestamp row entirely.
      messageFooter: (context, props) =>
          MyCustomFooter(message: props.message),
    ),
  ),
  child: MyApp(),
)

Each slot is independently overridable — none of them require touching the whole messageItem.

Test plan

  • dart analyze --fatal-infos packages/stream_chat_flutter — clean
  • dart format — clean (page width 120)
  • melos run test:flutter — golden tests for StreamMessageItem still match (no visual change expected; local run shows 115 pre-existing macOS golden mismatches unrelated to this change, none referencing the touched files)
  • Spot-check sample app: 1:1 channel (no leading avatar, no header), group channel own message (no leading avatar, footer with status/time), group channel other-user message (leading avatar at bottom of grouped run, header/footer as before)
  • Verify each slot in isolation by registering a custom builder in the sample app

🤖 Generated with Claude Code

Move the leading avatar rendering out of DefaultStreamMessageItem.build
and into its own NullableStatelessWidget under components/, mirroring the
structure already used by StreamMessageHeader and StreamMessageFooter.
Behavior is unchanged; the three slots are now composed symmetrically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 599863fc-2622-4b06-a102-a1261d32cf34

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/message-item-leading-slot

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

❌ Patch coverage is 63.26531% with 18 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (v10.0.0@d9b3105). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...ssage_widget/components/stream_message_header.dart 53.33% 7 Missing ⚠️
...ssage_widget/components/stream_message_footer.dart 63.63% 4 Missing ⚠️
...sage_widget/components/stream_message_leading.dart 76.47% 4 Missing ⚠️
...src/components/stream_chat_component_builders.dart 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             v10.0.0    #2710   +/-   ##
==========================================
  Coverage           ?   67.59%           
==========================================
  Files              ?      408           
  Lines              ?    24506           
  Branches           ?        0           
==========================================
  Hits               ?    16564           
  Misses             ?     7942           
  Partials           ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@xsahil03x xsahil03x changed the title refactor(ui): extract StreamMessageLeading from message item feat(ui): factory slots for message item leading, header, footer Jun 2, 2026
Register messageLeading, messageHeader, and messageFooter in
streamChatComponentBuilders so integrators can override each
sub-component independently instead of replacing the whole messageItem.
Each slot follows the same shape as StreamMessageItem: a public widget
that resolves chatComponentBuilder<...Props>(), a Props data class, and
a DefaultStream... implementation holding the rendering. The widgets are
now exported from the package barrel.

Closes a gap with iOS (makeUserAvatarView), Android Compose (MessageAuthor /
MessageTop / MessageBottom) and React Native (MessageAuthor / MessageHeader
/ MessageFooter).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@xsahil03x xsahil03x force-pushed the feat/message-item-leading-slot branch from 19550de to b2d686e Compare June 2, 2026 14:44
…cing

When the leading slot's nullableBuild returns null (e.g. message.user is
null) the framework substitutes _CollapsedWidget. A regular Row counts
this as a child and applies its spacing, leaving an extra gap before
the content column. StreamRow skips _RenderCollapsed children, matching
the StreamColumn already used inside StreamMessageContent for header
and footer.

Defers the visibility decision to the theme: if a theme resolves the
avatar to .visible while message.user is null, the slot renders an
empty widget without disturbing the row's horizontal rhythm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant