Skip to content

feat(ui): channel page and floating UI - #2748

Open
renefloor wants to merge 75 commits into
masterfrom
feat/channel-page
Open

feat(ui): channel page and floating UI#2748
renefloor wants to merge 75 commits into
masterfrom
feat/channel-page

Conversation

@renefloor

@renefloor renefloor commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-502

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

This introduces the floating style on all sample app pages and moves the channel page and thread page to the sdk.
The StreamMessageListViewConfiguration is also added to the global chat config so it's easier to use in the channel and thread page.

Core PR: GetStream/stream-core-flutter#115

Screenshots / Videos

Regular Floating
image image
image image
image image
image image
image image

Summary by CodeRabbit

  • New Features

    • Added ready-to-use channel and thread pages with reply, edit, navigation, and composer support.
    • Added floating or docked composer layouts with customizable themes.
    • Added global and per-view message-list configuration, padding controls, and reaction callbacks.
    • Added improved floating app-bar and avatar presentation.
    • Added optional location sharing in the sample app.
  • Bug Fixes

    • Thread replies are hidden when replies are disabled.
    • Long deleted-message labels now wrap correctly.
    • Thread scroll-to-bottom controls reflect effective configuration.
    • Photo galleries respect bottom safe-area spacing.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

This PR adds reusable channel and thread pages, configurable message-list and composer layouts, floating app-bar avatar styling, and migrates the sample app to StreamScaffold-based navigation and inset-aware screens.

Changes

Library UI and public APIs

Layer / File(s) Summary
Message list defaults and reusable chat pages
packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart, packages/stream_chat_flutter/lib/src/message_list_view/*, packages/stream_chat_flutter/lib/src/channel/*, packages/stream_chat_flutter/lib/stream_chat_flutter.dart
Adds global message-list configuration fallback, top/bottom padding, and reusable channel/thread pages with composer, typing indicator, and thread wiring.
Composer location theme and layout
packages/stream_chat_flutter/lib/src/theme/*, packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart
Adds floating/docked composer theming and placement-specific rendering.
Floating app-bar and avatar styling
packages/stream_chat_flutter/lib/src/channel/*header.dart, packages/stream_chat_flutter/lib/src/misc/back_button.dart, packages/stream_chat_flutter/lib/src/components/avatar/*
Resolves app-bar behavior and propagates floating state to avatar shadows and button styling.
Behavior, loading, and dependency updates
packages/stream_chat_flutter/lib/src/message_action/*, packages/stream_chat_flutter/lib/src/scroll_view/*, packages/stream_chat_flutter/pubspec.yaml, melos.yaml, docs/docs_screenshots/*
Gates thread replies, adjusts loading and padding behavior, updates reply-enabled mocks, and repins stream_core_flutter.

Sample app scaffold migration

Layer / File(s) Summary
App style persistence and theme wiring
sample_app/lib/config/sample_app_config.dart, sample_app/lib/app.dart
Adds persisted regular/floating app style selection and derives themes and message-list defaults from configuration.
Navigation shell and chat entry points
sample_app/lib/pages/channel_list_page.dart, sample_app/lib/widgets/channel_list.dart, sample_app/lib/pages/{draft_list_page,thread_list_page}.dart, sample_app/lib/routes/app_routes.dart
Replaces bottom-navigation primitives and routes channel and thread destinations through the new library pages.
Screen scaffold and inset migration
sample_app/lib/config/sample_app_config_screen.dart, sample_app/lib/pages/*
Replaces screen scaffolds with StreamScaffold and applies scaffold insets to scrollable content and lists.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

Suggested reviewers: xsahil03x

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main changes: new channel page work and floating UI support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/channel-page

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart (1)

433-470: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing composerLocation in copyWith method.

The new composerLocation field is not included in the copyWith method, breaking the expected API contract. Callers cannot override this field when copying props.

🐛 Proposed fix to add missing parameter
   MessageComposerProps copyWith({
     void Function(Message)? onMessageSent,
     FutureOr<Message> Function(Message)? preMessageSending,
     StreamMessageComposerController? messageComposerController,
     FocusNode? focusNode,
     bool? disableAttachments,
     bool? canAlsoSendToChannelFromThread,
     bool? enableVoiceRecording,
     bool? sendVoiceRecordingAutomatically,
     AudioRecorderFeedback? voiceRecordingFeedback,
     UserMentionTileBuilder? userMentionsTileBuilder,
     ErrorListener? onError,
     int? attachmentLimit,
     List<AttachmentPickerType>? allowedAttachmentPickerTypes,
     Iterable<StreamAutocompleteTrigger>? customAutocompleteTriggers,
     bool? mentionAllAppUsers,
     bool? shouldKeepFocusAfterMessage,
     MessageValidator? validator,
     String? restorationId,
     bool? enableSafeArea,
     bool? enableMentionsOverlay,
     VoidCallback? onQuotedMessageCleared,
     OgPreviewFilter? ogPreviewFilter,
     MessageInputPlaceholderBuilder? placeholderBuilder,
     bool? useSystemAttachmentPicker,
     PollConfig? pollConfig,
     AttachmentPickerOptionsBuilder? attachmentPickerOptionsBuilder,
     OnAttachmentPickerResult? onAttachmentPickerResult,
     KeyEventPredicate? sendMessageKeyPredicate,
     KeyEventPredicate? clearQuotedMessageKeyPredicate,
     TextInputAction? textInputAction,
     TextInputType? keyboardType,
     TextCapitalization? textCapitalization,
     bool? autofocus,
     bool? autoCorrect,
+    ComposerLocation? composerLocation,
   }) {
     return MessageComposerProps(
       onMessageSent: onMessageSent ?? this.onMessageSent,
       preMessageSending: preMessageSending ?? this.preMessageSending,
       messageComposerController: messageComposerController ?? this.messageComposerController,
       focusNode: focusNode ?? this.focusNode,
       disableAttachments: disableAttachments ?? this.disableAttachments,
       canAlsoSendToChannelFromThread: canAlsoSendToChannelFromThread ?? this.canAlsoSendToChannelFromThread,
       enableVoiceRecording: enableVoiceRecording ?? this.enableVoiceRecording,
       sendVoiceRecordingAutomatically: sendVoiceRecordingAutomatically ?? this.sendVoiceRecordingAutomatically,
       voiceRecordingFeedback: voiceRecordingFeedback ?? this.voiceRecordingFeedback,
       userMentionsTileBuilder: userMentionsTileBuilder ?? this.userMentionsTileBuilder,
       onError: onError ?? this.onError,
       attachmentLimit: attachmentLimit ?? this.attachmentLimit,
       allowedAttachmentPickerTypes: allowedAttachmentPickerTypes ?? this.allowedAttachmentPickerTypes,
       customAutocompleteTriggers: customAutocompleteTriggers ?? this.customAutocompleteTriggers,
       mentionAllAppUsers: mentionAllAppUsers ?? this.mentionAllAppUsers,
       shouldKeepFocusAfterMessage: shouldKeepFocusAfterMessage ?? this.shouldKeepFocusAfterMessage,
       validator: validator ?? this.validator,
       restorationId: restorationId ?? this.restorationId,
       enableSafeArea: enableSafeArea ?? this.enableSafeArea,
       enableMentionsOverlay: enableMentionsOverlay ?? this.enableMentionsOverlay,
       onQuotedMessageCleared: onQuotedMessageCleared ?? this.onQuotedMessageCleared,
       ogPreviewFilter: ogPreviewFilter ?? this.ogPreviewFilter,
       placeholderBuilder: placeholderBuilder ?? this.placeholderBuilder,
       useSystemAttachmentPicker: useSystemAttachmentPicker ?? this.useSystemAttachmentPicker,
       pollConfig: pollConfig ?? this.pollConfig,
       attachmentPickerOptionsBuilder: attachmentPickerOptionsBuilder ?? this.attachmentPickerOptionsBuilder,
       onAttachmentPickerResult: onAttachmentPickerResult ?? this.onAttachmentPickerResult,
       sendMessageKeyPredicate: sendMessageKeyPredicate ?? this.sendMessageKeyPredicate,
       clearQuotedMessageKeyPredicate: clearQuotedMessageKeyPredicate ?? this.clearQuotedMessageKeyPredicate,
       textInputAction: textInputAction ?? this.textInputAction,
       keyboardType: keyboardType ?? this.keyboardType,
       textCapitalization: textCapitalization ?? this.textCapitalization,
       autofocus: autofocus ?? this.autofocus,
       autoCorrect: autoCorrect ?? this.autoCorrect,
+      composerLocation: composerLocation ?? this.composerLocation,
     );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`
around lines 433 - 470, The copyWith method in MessageComposerProps is missing
the new composerLocation field; update the copyWith signature to accept
composerLocation and include it in the returned MessageComposerProps (e.g.,
composerLocation: composerLocation ?? this.composerLocation) alongside the other
fields so callers can override composerLocation when copying props.
sample_app/lib/pages/new_group_chat_screen.dart (1)

10-11: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add public API doc comment.

NewGroupChatScreen is a public widget but lacks documentation. As per coding guidelines, all public APIs must have doc comments.

📝 Suggested doc comment
+/// A screen for selecting users to add to a new group chat.
+///
+/// Users can search for platform members and select multiple participants
+/// before proceeding to group details configuration.
 class NewGroupChatScreen extends StatefulWidget {
   const NewGroupChatScreen({super.key});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sample_app/lib/pages/new_group_chat_screen.dart` around lines 10 - 11, Add a
public Dart doc comment for the NewGroupChatScreen widget: above the class
declaration for NewGroupChatScreen, insert a /// comment that briefly describes
the widget's purpose (e.g., creates a UI for creating a new group chat),
documents its constructor parameters (if any, like key), and any important
behavior or usage notes so it complies with the public API documentation
guideline.

Source: Coding guidelines

🧹 Nitpick comments (3)
packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart (1)

979-984: 💤 Low value

Deprecated axisAlignment parameter usage.

The axisAlignment parameter on SizeTransition is deprecated. The comment acknowledges this, but consider migrating to the replacement API when available to avoid future deprecation warnings during builds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`
around lines 979 - 984, Replace the deprecated axisAlignment usage in the
SizeTransition inside StreamMessageComposer: remove the "axisAlignment: -1"
argument and instead pass an alignment value that preserves the original
behavior (axisAlignment -1 -> top alignment) using the new "alignment" parameter
(e.g., Alignment.topCenter or AlignmentDirectional.topStart) on the
SizeTransition that wraps _buildInlineAttachmentPicker; update the
SizeTransition invocation accordingly so the widget aligns the
collapsing/expanding animation to the top without using the deprecated
axisAlignment.
packages/stream_chat_flutter/lib/src/channel/channel_header.dart (1)

134-136: ⚡ Quick win

Inconsistent documentation pattern for appBarBehavior fields across StreamChannelHeader, StreamChannelListHeader, and StreamBackButton.

All three widgets document their appBarBehavior field with boolean phrasing ("Whether X is floating") when the field is typed as AppBarBehavior?, not a boolean. The documentation should describe the parameter's purpose (controlling visual behavior) and mention the fallback to theme defaults. Consider unifying the wording across channel_header.dart, channel_list_header.dart, and back_button.dart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stream_chat_flutter/lib/src/channel/channel_header.dart` around
lines 134 - 136, Update the doc comments for the appBarBehavior field in
StreamChannelHeader, StreamChannelListHeader, and StreamBackButton to stop using
boolean phrasing and instead describe that appBarBehavior (type AppBarBehavior?)
controls the header/back button visual/layout behavior (e.g., floating vs
pinned) and that it falls back to the theme's default when null; make the
wording consistent across the three files by using the same concise sentence
describing purpose and null/theme fallback and reference the AppBarBehavior type
in the comment.
packages/stream_chat_flutter/lib/src/channel/channel_page.dart (1)

32-44: ⚡ Quick win

Prefer late final non-nullable FocusNode to avoid force-unwraps.

The _focusNode field is declared nullable but always initialized in initState and force-unwrapped at every usage site (lines 43, 50, 57). This pattern is fragile and could panic if lifecycle order changes. Declaring it as late final FocusNode makes the initialization contract explicit and eliminates the null-check overhead.

♻️ Proposed refactor
-  FocusNode? _focusNode;
+  late final FocusNode _focusNode;
   final _messageComposerController = StreamMessageComposerController();
 
   `@override`
   void initState() {
+    super.initState();
     _focusNode = FocusNode();
-    super.initState();
   }
 
   `@override`
   void dispose() {
-    _focusNode!.dispose();
+    _focusNode.dispose();
     super.dispose();
   }
 
   void _reply(Message message) {
     _messageComposerController.quotedMessage = message;
     WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
-      _focusNode!.requestFocus();
+      _focusNode.requestFocus();
     });
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stream_chat_flutter/lib/src/channel/channel_page.dart` around lines
32 - 44, The _focusNode field is declared nullable but always initialized in
initState and force-unwrapped elsewhere; change its declaration to use
non-nullable late final (late final FocusNode _focusNode) so initialization is
explicit, keep _focusNode = FocusNode() inside initState, call
_focusNode.dispose() in dispose, and remove any force-unwraps (!) where
_focusNode is accessed (e.g., usages in the widget build and handlers).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dart`:
- Line 78: The permission-loading branch in stream_gallery_picker.dart returns
const SizedBox.expand() when !snapshot.hasData, but the project's Empty widget
builds const SizedBox.shrink(), so replace the expand with the shrink (or return
Empty()) in the permission/loading branch to avoid introducing an unintended
expanded blank area and layout shift; locate the conditional using
snapshot.hasData in the StreamGalleryPicker build method and update the returned
widget accordingly.

In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`:
- Around line 14-16: The constant _kPickerBodyHeight is unused dead code; either
remove the declaration or apply it as the fixed height for the inline attachment
picker UI—locate the picker implementation in stream_message_composer.dart (look
for the InlineAttachmentPicker or picker body widget inside
StreamMessageComposer/MessageComposer) and replace any hard-coded height values
or SizedBox/Container height with _kPickerBodyHeight, or delete the
_kPickerBodyHeight declaration if you choose not to standardize the height.

In `@sample_app/lib/routes/app_routes.dart`:
- Around line 48-72: The onChannelAvatarPressed handler can crash because
currentUserId may be null and channelMembers.firstWhere(...) will throw if no
match; update the logic in onChannelAvatarPressed to null-guard currentUserId
and safely find the other member (use collection's firstWhereOrNull or
firstWhere with orElse returning null) when computing otherUser from
channel.state?.members, and only navigate to Routes.CHAT_INFO_SCREEN when
otherUser is non-null; fallback to the GROUP_INFO_SCREEN path otherwise. Ensure
you import the collection helper if you use firstWhereOrNull.

---

Outside diff comments:
In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`:
- Around line 433-470: The copyWith method in MessageComposerProps is missing
the new composerLocation field; update the copyWith signature to accept
composerLocation and include it in the returned MessageComposerProps (e.g.,
composerLocation: composerLocation ?? this.composerLocation) alongside the other
fields so callers can override composerLocation when copying props.

In `@sample_app/lib/pages/new_group_chat_screen.dart`:
- Around line 10-11: Add a public Dart doc comment for the NewGroupChatScreen
widget: above the class declaration for NewGroupChatScreen, insert a /// comment
that briefly describes the widget's purpose (e.g., creates a UI for creating a
new group chat), documents its constructor parameters (if any, like key), and
any important behavior or usage notes so it complies with the public API
documentation guideline.

---

Nitpick comments:
In `@packages/stream_chat_flutter/lib/src/channel/channel_header.dart`:
- Around line 134-136: Update the doc comments for the appBarBehavior field in
StreamChannelHeader, StreamChannelListHeader, and StreamBackButton to stop using
boolean phrasing and instead describe that appBarBehavior (type AppBarBehavior?)
controls the header/back button visual/layout behavior (e.g., floating vs
pinned) and that it falls back to the theme's default when null; make the
wording consistent across the three files by using the same concise sentence
describing purpose and null/theme fallback and reference the AppBarBehavior type
in the comment.

In `@packages/stream_chat_flutter/lib/src/channel/channel_page.dart`:
- Around line 32-44: The _focusNode field is declared nullable but always
initialized in initState and force-unwrapped elsewhere; change its declaration
to use non-nullable late final (late final FocusNode _focusNode) so
initialization is explicit, keep _focusNode = FocusNode() inside initState, call
_focusNode.dispose() in dispose, and remove any force-unwraps (!) where
_focusNode is accessed (e.g., usages in the widget build and handlers).

In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`:
- Around line 979-984: Replace the deprecated axisAlignment usage in the
SizeTransition inside StreamMessageComposer: remove the "axisAlignment: -1"
argument and instead pass an alignment value that preserves the original
behavior (axisAlignment -1 -> top alignment) using the new "alignment" parameter
(e.g., Alignment.topCenter or AlignmentDirectional.topStart) on the
SizeTransition that wraps _buildInlineAttachmentPicker; update the
SizeTransition invocation accordingly so the widget aligns the
collapsing/expanding animation to the top without using the deprecated
axisAlignment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e2a3f42-fca0-422a-96f1-7cd12e8d83ee

📥 Commits

Reviewing files that changed from the base of the PR and between 805e2e0 and 74888d1.

📒 Files selected for processing (40)
  • docs/docs_screenshots/pubspec.yaml
  • melos.yaml
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/lib/src/channel/channel_header.dart
  • packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart
  • packages/stream_chat_flutter/lib/src/channel/channel_page.dart
  • packages/stream_chat_flutter/lib/src/channel/thread_page.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_channel_avatar.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_group.dart
  • packages/stream_chat_flutter/lib/src/message_action/message_actions_builder.dart
  • packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dart
  • packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart
  • packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart
  • packages/stream_chat_flutter/lib/src/misc/back_button.dart
  • packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_skeleton_loading.dart
  • packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery.dart
  • packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_skeleton_loading.dart
  • packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart
  • packages/stream_chat_flutter/lib/stream_chat_flutter.dart
  • packages/stream_chat_flutter/pubspec.yaml
  • sample_app/lib/app.dart
  • sample_app/lib/config/sample_app_config.dart
  • sample_app/lib/config/sample_app_config_screen.dart
  • sample_app/lib/pages/advanced_options_page.dart
  • sample_app/lib/pages/channel_file_display_screen.dart
  • sample_app/lib/pages/channel_list_page.dart
  • sample_app/lib/pages/channel_media_display_screen.dart
  • sample_app/lib/pages/channel_page.dart
  • sample_app/lib/pages/chat_info_screen.dart
  • sample_app/lib/pages/draft_list_page.dart
  • sample_app/lib/pages/group_chat_details_screen.dart
  • sample_app/lib/pages/group_info_screen.dart
  • sample_app/lib/pages/new_chat_screen.dart
  • sample_app/lib/pages/new_group_chat_screen.dart
  • sample_app/lib/pages/pinned_messages_screen.dart
  • sample_app/lib/pages/thread_list_page.dart
  • sample_app/lib/pages/thread_page.dart
  • sample_app/lib/routes/app_routes.dart
  • sample_app/lib/widgets/channel_list.dart
💤 Files with no reviewable changes (2)
  • sample_app/lib/pages/thread_page.dart
  • sample_app/lib/pages/channel_page.dart

Comment thread packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart Outdated
Comment thread sample_app/lib/routes/app_routes.dart

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the HSL -> HCT color change

@renefloor
renefloor force-pushed the feat/channel-page branch from a3eb6e1 to 775efe0 Compare July 30, 2026 08:29
renefloor and others added 15 commits July 30, 2026 14:03
# Conflicts:
#	docs/docs_screenshots/pubspec.yaml
#	melos.yaml
#	packages/stream_chat_flutter/pubspec.yaml
Moving the channel page into the SDK dropped the sample app's location
picker: the composer wiring lived in sample_app/lib/pages/channel_page.dart,
which was deleted, and StreamChannelPage owns its composer without exposing
any parameters for it. Received locations still rendered via the attachment
builder, but nothing could send one -- the "Location Sharing" config toggle
was half dead.

Register the picker as a `messageComposer` component builder instead of
passing it to a composer directly. StreamMessageComposer already resolves
that builder from the component factory, so it reaches the composers inside
StreamChannelPage and StreamThreadPage without widening their API.

Tolerates a missing StreamChannel ancestor, since the factory is global and
a composer need not sit inside a channel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both pages take four parameters, which reads as a closed surface. It isn't:
the component factory reaches inside them, because the components resolve it
themselves -- StreamMessageItem and StreamMessageComposer each look up their
own builder. Combined with the new global messageListViewConfiguration, that
covers the customization callers actually reach for.

Document that, with an example, and name the two things that genuinely have
no factory entry -- the list-level StreamMessageListViewBuilders slots and the
headers' title/subtitle/actions -- so they read as known boundaries rather
than oversights.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The entry claimed an `appBarBehavior` parameter was added to
StreamChannelHeader and StreamChannelListHeader. Only StreamBackButton got
one; the two headers read the behavior off their existing StreamAppBarStyle
to decide the avatar drop shadow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Threads previously gated the button on the parent channel's isUpToDate
stream rather than the thread's own scroll position, so it could show while
already at the newest reply. Behaviour change for callers who never set
showScrollToBottom, so it belongs in the changelog.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Making `config` nullable moved its fallback onto an inherited-widget lookup,
and `_config` was a getter, so `StreamChatConfiguration.of(context)` now ran
from wherever the getter was read -- including the `_messageNewListener`
stream callback. dependOnInheritedWidgetOfExactType asserts the element is
active, so a message arriving while the list is deactivated (GlobalKey
reparent, or racing the cancel in dispose) throws in debug. Reading
widget.config, as before, had no such constraint. It also re-ran the lookup
~20x per build.

Resolve into a field at the top of didChangeDependencies, and add
didUpdateWidget for the case where `config` changes without dependencies
changing.

The didUpdateWidget test swaps the config via setState below StreamChat
rather than a second pumpWidget: StreamChatConfigurationData has no `==`, so
rebuilding StreamChat hands StreamChatConfiguration a fresh instance and
notifies every dependent, which would re-resolve the config for the wrong
reason and hide a missing didUpdateWidget. Verified the test fails with the
line removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The `repliesEnabled` guard on ThreadReply landed with only a mock-helper
change (adding `replies: true`) so existing tests kept passing -- the new
branch itself was untested.

Parameterise the helper with `enableReplies` and assert the action drops out
while the send-reply capability is still granted, so the test pins the config
check rather than the capability check. Also assert QuotedReply survives,
since it does not depend on the replies config. Verified the test fails with
the guard removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The three-tier fallback (widget-local override, then StreamAppBarTheme, then
the ambient StreamAppStyle) plus an identical two-arm switch was written out
three times, and the derived local was named differently in each --
showAvatarShadow, hasAvatarShadow, isFloating.

Extract resolveAppBarBehavior and isFloatingAppBar into an internal util, and
name the local isFloating at all three sites. Behaviour-preserving; existing
header and back-button tests pass unchanged.

Kept in stream_chat_flutter rather than stream_core_flutter so this stays
self-contained -- noted in the doc comment that next to StreamAppBarBehavior
is the better long-term home.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three unrelated nits from the review:

- Move `_pillHeight` up with the other state fields in the composer; it was
  declared between two methods, after both of its use sites.

- Drop the ignore_for_file line in channel_list_page.dart entirely. The review
  flagged `avoid_redundant_argument_values` as too broad; checking it turned
  out to have no offenders at all, and `deprecated_member_use` went stale when
  BottomNavigationBar was replaced with StreamBottomNavBar.

- Add a `streamTopInset` / `streamBottomInset` BuildContext extension in the
  sample app, replacing 12 copies of
  `StreamScaffoldInsets.maybeOf(context)?.topPadding ?? 0.0` across 11 files.
  Kept in the sample: StreamScaffoldInsets lives in stream_core_flutter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts:
#	packages/stream_chat_flutter/CHANGELOG.md
#	sample_app/lib/pages/channel_page.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart (1)

837-840: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the composer-location resolution.

The three-step fallback (props.location → theme → app style) is duplicated verbatim in build and _buildMessageInput; the two can silently diverge. A private getter keeps them in sync.

♻️ Proposed refactor
ComposerLocation get _effectiveComposerLocation =>
    widget.props.location ??
    StreamMessageComposerTheme.of(context).location ??
    (StreamTheme.of(context).appStyle.isFloating ? ComposerLocation.floating : ComposerLocation.docked);
-    final effectiveComposerLocation =
-        widget.props.location ??
-        StreamMessageComposerTheme.of(context).location ??
-        (StreamTheme.of(context).appStyle.isFloating ? ComposerLocation.floating : ComposerLocation.docked);
+    final effectiveComposerLocation = _effectiveComposerLocation;

Also applies to: 1001-1005

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`
around lines 837 - 840, Extract the duplicated composer-location fallback into a
private _effectiveComposerLocation getter on the composer, resolving
widget.props.location, the theme location, and app-style default in that order.
Replace the inline resolution in both build and _buildMessageInput with this
getter so both paths share one implementation.
packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart (1)

174-175: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer copying the ambient MediaQueryData over replacing it.

MediaQueryData(padding: ...) drops every other field (notably size, which defaults to Size.zero). It happens to work because the composer only reads paddingOf, but any future sizeOf/textScaler read below this subtree will see degenerate values. Wrapping with MediaQuery.of(context).copyWith(padding: ...) (via a Builder) is more robust.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart`
around lines 174 - 175, Update the MediaQuery wrapper around the home widget to
copy the ambient MediaQueryData and override only its padding, using a Builder
if needed to obtain the correct context. Preserve all other inherited MediaQuery
fields instead of constructing a new MediaQueryData with defaults.
packages/stream_chat_flutter/test/src/channel/thread_page_test.dart (1)

147-226: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared mock harness.

_pumpThreadPage duplicates almost all of the client/channel/channelState stubbing (and the _messageListView/_composerController/_composerFocusNode helpers) from packages/stream_chat_flutter/test/src/channel/channel_page_test.dart and packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart. A shared helper (e.g. a mockChatSetup() in a test util) would keep these three suites from drifting apart as the mocked surface grows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stream_chat_flutter/test/src/channel/thread_page_test.dart` around
lines 147 - 226, The mock setup in _pumpThreadPage duplicates the client,
channel, channelState, and related helper stubbing used by _messageListView,
_composerController, and _composerFocusNode in the other test suites. Extract
the shared setup into a reusable test utility such as mockChatSetup(), update
all three suites to use it, and preserve each test’s suite-specific overrides
and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/stream_chat_flutter/lib/src/channel/channel_header.dart`:
- Around line 152-159: Update isFloating resolution in channel_header.dart at
lines 152-159 and channel_list_header.dart at lines 129-131 to use the
per-instance style behavior first, falling back to headerTheme.style?.behavior.
Preserve the existing avatar and app-bar behavior while honoring the local
header theme when no override is provided.

In `@sample_app/lib/widgets/location/location_aware_message_composer.dart`:
- Around line 30-35: Update the props construction in
LocationAwareMessageComposer to preserve the incoming
allowedAttachmentPickerTypes by appending LocationPickerType to that existing
collection rather than replacing it with all enum values. Also chain
props.onAttachmentPickerResult from onAttachmentPickerResult so non-location
results reach the caller’s handler while location results continue through
_onCustomAttachmentPickerResult.

---

Nitpick comments:
In
`@packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart`:
- Around line 837-840: Extract the duplicated composer-location fallback into a
private _effectiveComposerLocation getter on the composer, resolving
widget.props.location, the theme location, and app-style default in that order.
Replace the inline resolution in both build and _buildMessageInput with this
getter so both paths share one implementation.

In `@packages/stream_chat_flutter/test/src/channel/thread_page_test.dart`:
- Around line 147-226: The mock setup in _pumpThreadPage duplicates the client,
channel, channelState, and related helper stubbing used by _messageListView,
_composerController, and _composerFocusNode in the other test suites. Extract
the shared setup into a reusable test utility such as mockChatSetup(), update
all three suites to use it, and preserve each test’s suite-specific overrides
and behavior.

In
`@packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart`:
- Around line 174-175: Update the MediaQuery wrapper around the home widget to
copy the ambient MediaQueryData and override only its padding, using a Builder
if needed to obtain the correct context. Preserve all other inherited MediaQuery
fields instead of constructing a new MediaQueryData with defaults.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ab55b9ff-06fb-4ffe-92dd-22038fbf2be6

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb63d5 and 1a156e4.

⛔ Files ignored due to path filters (3)
  • docs/docs_screenshots/test/theming/goldens/macos/theming_red.png is excluded by !**/*.png
  • packages/stream_chat_flutter/test/src/message_widget/goldens/ci/stream_message_deleted_dark.png is excluded by !**/*.png
  • packages/stream_chat_flutter/test/src/message_widget/goldens/ci/stream_message_deleted_light.png is excluded by !**/*.png
📒 Files selected for processing (38)
  • docs/docs_screenshots/test/src/mocks.dart
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/lib/src/channel/channel_header.dart
  • packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart
  • packages/stream_chat_flutter/lib/src/channel/channel_page.dart
  • packages/stream_chat_flutter/lib/src/channel/thread_page.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_channel_avatar.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_group.dart
  • packages/stream_chat_flutter/lib/src/components/message_composer/message_composer_input.dart
  • packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart
  • packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart
  • packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_deleted.dart
  • packages/stream_chat_flutter/lib/src/misc/back_button.dart
  • packages/stream_chat_flutter/lib/src/utils/app_bar_behavior.dart
  • packages/stream_chat_flutter/test/src/channel/channel_page_test.dart
  • packages/stream_chat_flutter/test/src/channel/thread_page_test.dart
  • packages/stream_chat_flutter/test/src/message_action/message_actions_builder_test.dart
  • packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart
  • packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart
  • packages/stream_chat_flutter/test/src/message_widget/stream_message_deleted_test.dart
  • packages/stream_chat_flutter/test/src/theme/message_composer_theme_test.dart
  • sample_app/ios/Runner.xcodeproj/project.pbxproj
  • sample_app/lib/app.dart
  • sample_app/lib/config/sample_app_config_screen.dart
  • sample_app/lib/pages/advanced_options_page.dart
  • sample_app/lib/pages/channel_file_display_screen.dart
  • sample_app/lib/pages/channel_list_page.dart
  • sample_app/lib/pages/channel_media_display_screen.dart
  • sample_app/lib/pages/chat_info_screen.dart
  • sample_app/lib/pages/group_chat_details_screen.dart
  • sample_app/lib/pages/group_info_screen.dart
  • sample_app/lib/pages/new_chat_screen.dart
  • sample_app/lib/pages/new_group_chat_screen.dart
  • sample_app/lib/pages/pinned_messages_screen.dart
  • sample_app/lib/utils/scaffold_insets.dart
  • sample_app/lib/widgets/channel_list.dart
  • sample_app/lib/widgets/location/location_aware_message_composer.dart
💤 Files with no reviewable changes (1)
  • sample_app/lib/pages/channel_list_page.dart
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_group.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar.dart
  • packages/stream_chat_flutter/lib/src/components/avatar/stream_channel_avatar.dart
  • packages/stream_chat_flutter/CHANGELOG.md

Comment thread packages/stream_chat_flutter/lib/src/channel/channel_header.dart Outdated
Comment thread sample_app/lib/widgets/location/location_aware_message_composer.dart Outdated
# Conflicts:
#	packages/stream_chat_flutter/CHANGELOG.md
#	packages/stream_chat_flutter/lib/src/misc/back_button.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/stream_chat_flutter/lib/src/channel/thread_page.dart (1)

92-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace deprecated channelId/showUnreadCount with unreadIndicator.

StreamBackButton.channelId and StreamBackButton.showUnreadCount are marked @Deprecated in favor of unreadIndicator, and the annotation states they will be removed in a future version. StreamThreadHeader's own default leading already uses the non-deprecated unreadIndicator parameter directly. Use the same pattern here for consistency and to avoid relying on an API scheduled for removal.

♻️ Proposed fix to use `unreadIndicator` instead of deprecated params
       leading: switch (widget.onBackPressed) {
         final onBackPressed? => StreamBackButton(
           onPressed: onBackPressed,
-          channelId: channel?.cid,
-          showUnreadCount: true,
+          unreadIndicator: switch (channel?.cid) {
+            final cid? => StreamUnreadIndicator.channels(cid: cid),
+            null => const StreamUnreadIndicator(),
+          },
         ),
         _ => null,
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/stream_chat_flutter/lib/src/channel/thread_page.dart` around lines
92 - 104, Update the StreamBackButton construction in StreamThreadHeader’s
leading switch to replace the deprecated channelId and showUnreadCount arguments
with the non-deprecated unreadIndicator parameter, matching the header’s default
leading behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/stream_chat_flutter/lib/src/channel/thread_page.dart`:
- Around line 92-104: Update the StreamBackButton construction in
StreamThreadHeader’s leading switch to replace the deprecated channelId and
showUnreadCount arguments with the non-deprecated unreadIndicator parameter,
matching the header’s default leading behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f10bfec3-94ee-44ce-acd2-18c043bcff15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a156e4 and 4cf55e1.

📒 Files selected for processing (16)
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/lib/src/channel/channel_header.dart
  • packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart
  • packages/stream_chat_flutter/lib/src/channel/channel_page.dart
  • packages/stream_chat_flutter/lib/src/channel/thread_page.dart
  • packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart
  • packages/stream_chat_flutter/lib/src/misc/back_button.dart
  • packages/stream_chat_flutter/lib/src/misc/thread_header.dart
  • packages/stream_chat_flutter/lib/src/theme/message_composer_theme.dart
  • packages/stream_chat_flutter/lib/src/theme/message_composer_theme.g.theme.dart
  • packages/stream_chat_flutter/test/src/channel/channel_header_test.dart
  • packages/stream_chat_flutter/test/src/channel/channel_list_header_test.dart
  • packages/stream_chat_flutter/test/src/channel/thread_page_test.dart
  • packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart
  • packages/stream_chat_flutter/test/src/theme/message_composer_theme_test.dart
  • sample_app/lib/widgets/location/location_aware_message_composer.dart
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/stream_chat_flutter/lib/src/channel/channel_page.dart
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/test/src/message_input/message_composer_location_test.dart
  • packages/stream_chat_flutter/lib/src/misc/back_button.dart
  • sample_app/lib/widgets/location/location_aware_message_composer.dart
  • packages/stream_chat_flutter/lib/src/theme/message_composer_theme.dart
  • packages/stream_chat_flutter/lib/src/theme/message_composer_theme.g.theme.dart
  • packages/stream_chat_flutter/test/src/theme/message_composer_theme_test.dart
  • packages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dart

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