feat(ui): channel page and floating UI - #2748
Conversation
This reverts commit 8f3f336. # Conflicts: # packages/stream_chat_flutter/lib/src/channel/channel_page.dart
broken due to merge
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds reusable channel and thread pages, configurable message-list and composer layouts, floating app-bar avatar styling, and migrates the sample app to ChangesLibrary UI and public APIs
Sample app scaffold migration
Estimated code review effort: 4 (Complex) | ~75 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 winMissing
composerLocationincopyWithmethod.The new
composerLocationfield is not included in thecopyWithmethod, 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 winAdd public API doc comment.
NewGroupChatScreenis 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 valueDeprecated
axisAlignmentparameter usage.The
axisAlignmentparameter onSizeTransitionis 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 winInconsistent documentation pattern for
appBarBehaviorfields acrossStreamChannelHeader,StreamChannelListHeader, andStreamBackButton.All three widgets document their
appBarBehaviorfield with boolean phrasing ("Whether X is floating") when the field is typed asAppBarBehavior?, 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 acrosschannel_header.dart,channel_list_header.dart, andback_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 winPrefer
late finalnon-nullableFocusNodeto avoid force-unwraps.The
_focusNodefield is declared nullable but always initialized ininitStateand force-unwrapped at every usage site (lines 43, 50, 57). This pattern is fragile and could panic if lifecycle order changes. Declaring it aslate final FocusNodemakes 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
📒 Files selected for processing (40)
docs/docs_screenshots/pubspec.yamlmelos.yamlpackages/stream_chat_flutter/CHANGELOG.mdpackages/stream_chat_flutter/lib/src/channel/channel_header.dartpackages/stream_chat_flutter/lib/src/channel/channel_list_header.dartpackages/stream_chat_flutter/lib/src/channel/channel_page.dartpackages/stream_chat_flutter/lib/src/channel/thread_page.dartpackages/stream_chat_flutter/lib/src/components/avatar/stream_channel_avatar.dartpackages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar.dartpackages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_group.dartpackages/stream_chat_flutter/lib/src/message_action/message_actions_builder.dartpackages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dartpackages/stream_chat_flutter/lib/src/message_input/stream_message_composer.dartpackages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dartpackages/stream_chat_flutter/lib/src/misc/back_button.dartpackages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_skeleton_loading.dartpackages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery.dartpackages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_skeleton_loading.dartpackages/stream_chat_flutter/lib/src/stream_chat_configuration.dartpackages/stream_chat_flutter/lib/stream_chat_flutter.dartpackages/stream_chat_flutter/pubspec.yamlsample_app/lib/app.dartsample_app/lib/config/sample_app_config.dartsample_app/lib/config/sample_app_config_screen.dartsample_app/lib/pages/advanced_options_page.dartsample_app/lib/pages/channel_file_display_screen.dartsample_app/lib/pages/channel_list_page.dartsample_app/lib/pages/channel_media_display_screen.dartsample_app/lib/pages/channel_page.dartsample_app/lib/pages/chat_info_screen.dartsample_app/lib/pages/draft_list_page.dartsample_app/lib/pages/group_chat_details_screen.dartsample_app/lib/pages/group_info_screen.dartsample_app/lib/pages/new_chat_screen.dartsample_app/lib/pages/new_group_chat_screen.dartsample_app/lib/pages/pinned_messages_screen.dartsample_app/lib/pages/thread_list_page.dartsample_app/lib/pages/thread_page.dartsample_app/lib/routes/app_routes.dartsample_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
- Drop the redundant "since no default back button is built" clause (the two conditions already imply it) — prefer brevity. - Link [Navigator.maybePop] instead of backtick-quoting it, so dart doc resolves the reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The content* edges are always populated by PositionedList (equal to the item* edges when there's no inset), never null-when-unpadded as the doc claimed — the null case is only for externally-built ItemPositions that omit them. Fix the doc to say so and add SPL coverage for the previously-untested fields: - under a leading inset, content* is content-relative (item 0 flush with the content top reads 0) while item* stays viewport-relative (0.25); - with no inset, content* equals item*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dependency had been switched to a local absolute path for cross-repo iteration, which breaks pub get on CI. Restore the git dependency (core PR #146, ref 8425a0f8) in pubspec.yaml, melos.yaml, and docs_screenshots. Local dev keeps using the path override in the gitignored pubspec_overrides.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Added in a WIP checkpoint, these semantics tests assert a merged-summary behavior with no corresponding implementation on this branch, so they fail (getSemantics finds no matching node). The a11y work can land in its own PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace StreamAppStyle / StreamMessageComposerBehavior with the unified StreamSurfaceStyle across the composer, headers, theme, and sample app, and drive the composer's collapsing safe area with StreamSafeArea.driven. Align dartdoc with the style guide (private members use //, US spelling, contract-not-implementation, documented null fallbacks) and remove dead code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Commit d07bcf5 intentionally reverted the channel replies-config gating so ThreadReply is governed solely by the send-reply capability, but this test was left asserting the old behavior and failed CI. Update it to assert the shipped behavior: the config flag alone does not hide the action when the capability is granted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The channel replies-config gating this covered was intentionally reverted (d07bcf5) and is not on master, so remove the test rather than keep it asserting a behavior the branch no longer implements. Thread reply remains covered by the send-reply capability cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Enable `SystemUiMode.edgeToEdge` in `main()` to allow content to flow under system bars. - Add `_EdgeToEdgeSystemBars` wrapper widget using `AnnotatedRegion` to configure `SystemUiOverlayStyle`. - Set `statusBarColor` and `systemNavigationBarColor` to transparent. - Disable `systemNavigationBarContrastEnforced` to allow full edge-to-edge content bleeding. - Ensure system bar icon brightness automatically updates based on the current `ThemeMode`.
- Implement `WidgetsBindingObserver` in `DefaultStreamMessageComposerState` to trigger rebuilds on metric changes (keyboard opening/closing). - Update the `StreamSafeArea` bottom padding to dynamically switch between `spacing.safeAreaBottom()` when the keyboard is hidden and `spacing.md` when it is visible.
The composer floored its bottom gap at `StreamSpacing.safeAreaBottom()` (32 on iOS/macOS, 40 elsewhere). A floor stacks on a system bar the window never extends behind: on a device without edge-to-edge the inset reports 0, so the composer kept the full 40 above a navigation bar that already separated it from the screen edge. It now adds a margin on top of the inset instead — none on Apple platforms, which rest directly on it, and `spacing.md` elsewhere, where the gesture region reaches past the drawing inset. With no inset to rest on (no edge-to-edge, a desktop or web window, or the keyboard holding the edge) the margin stands in for it, so the input never sits flush on the edge. Gating on the inset also removes the need to track the keyboard: the IME collapses `MediaQuery.padding.bottom`, so the margin applies on its own. The `WidgetsBindingObserver` and `didChangeMetrics` rebuild that existed only to swap the floor while typing go with it, along with the undependable `View.of(context).viewInsets` read. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverts the `SystemUiMode.edgeToEdge` call and the `AnnotatedRegion` wrapper that made the sample app render behind transparent system bars. Below Android 15 the request is a no-op (`SystemUiMode.edgeToEdge` needs API 29+ and the OS only enforces it from API 35), so the app was edge-to-edge on some devices and not others while the composer's bottom inset was being tuned. Android 15+ still opts in by default via the target SDK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Points at core 208f360, which replaces the floating pill's bottom-inset floor with a margin and drops `StreamSpacing.safeAreaBottom()`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Points at core 4e43166, which lets a docked nav-bar tile's ripple reach into the bottom system inset. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Points at core 4e43166, which lets a docked nav-bar tile's ripple reach into the bottom system inset. Restores the git dependency in the process: the previous commit had captured a local absolute path used for on-device iteration, which breaks `pub get` on CI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Renames the `override` parameter to `surfaceStyle` and collapses the body to a single `??` chain, matching the shape the core bars' resolvers use. Trims the doc to the precedence contract. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The headers wrapped their bar in a `StreamAppBarTheme` to deliver `channelHeaderTheme` / `channelListHeaderTheme` / `threadHeaderTheme`. Because that wrapper sits inside the header, it is always nearer to the bar than a caller's own `StreamAppBarTheme`, so the caller's override could never reach it. Layering the per-header theme under `style` instead lets the bar's existing resolution pick the ancestor up on its own, so the precedence the docs already promise holds without an extra `InheritedTheme` in the tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The composer read `MediaQuery.paddingOf` for its bottom margin, and `padding.bottom` is already `max(0, viewPadding - viewInsets)` — it goes to zero when the keyboard shows, and `paddingOf` registers the dependency that rebuilds on the change. The observer's blanket `setState` was doing work `MediaQuery` already does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The expression states what it does, the precedence contract lives on `style`'s own doc, and the first clause described the bar's internal resolution from the wrong file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`_resolveSurfaceStyle` is a one-line delegation and the precedence it described is already on `StreamMessageComposer.resolveSurfaceStyle`'s doc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two entries described things this PR does not do: - `StreamPhotoGallery`'s padding is untouched on this branch and still has no safe-area default. The real change is `StreamMediaGallery` dropping its outer padding and its `MediaQuery.removePadding` wrapper, which had no entry. - Nothing was added to a re-export allowlist — the barrel re-exports `stream_core_flutter` through a `hide` list — and the entry named `StreamScaffoldInsets`, which no longer exists in core. The rest are trimmed to one sentence to match the released sections, and the behavior changes buried inside `Added` bullets moved to `Changed`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Submit a pull request
Linear: FLU-502
CLA
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
StreamMessageListViewConfigurationis 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#146
Screenshots / Videos
Summary by CodeRabbit
New Features
Bug Fixes