Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dbec56b
refactor(sample, ui): redesign channel detail sheet
xsahil03x May 3, 2026
77c99fe
chore(ui): re-export StreamAppBar from stream_chat_flutter
xsahil03x May 3, 2026
dbfca91
feat(sample): redesign Contact Info screen
xsahil03x May 3, 2026
6589471
feat(sample): redesign Group Info screen
xsahil03x May 3, 2026
c8d7127
feat(sample): all-members sheet + contact detail sheet
xsahil03x May 3, 2026
950f5b8
feat(sample): edit-group sheet + avatar picker
xsahil03x May 3, 2026
179526d
fix(sample): edit-group sheet — full-height + keyboard-safe
xsahil03x May 3, 2026
ae37007
fix(sample): edit-group sheet — disable save on empty name
xsahil03x May 3, 2026
f2b2cbc
fix(sample): edit-group sheet — drop keyboard before opening picker
xsahil03x May 3, 2026
9d157ee
revert(sample): drop redundant unfocus before avatar picker
xsahil03x May 3, 2026
040606d
fix(sample): edit-group sheet — Upload uses StreamButton
xsahil03x May 3, 2026
67a91d4
feat(sample): edit-group sheet — show upload progress
xsahil03x May 3, 2026
4ffd051
fix(sample): edit-group sheet — uploaded preview matches channel avatar
xsahil03x May 3, 2026
2fb8e06
fix(sample): edit-group sheet — avatar is preview-only, no tap target
xsahil03x May 3, 2026
86b826b
fix(sample): edit-group sheet — shrink-wrap, not full-height
xsahil03x May 3, 2026
ff33c95
fix(sample): edit-group sheet — eager image saves + size matches
xsahil03x May 3, 2026
ecd7acd
fix(sample): edit-group sheet — defer image persistence to save
xsahil03x May 3, 2026
eb7f955
fix(sample): edit-group sheet — local preview + orphan cleanup
xsahil03x May 3, 2026
3d7d090
fix(sample): edit-group sheet — swap to URL once upload settles
xsahil03x May 3, 2026
83acd74
feat(sample): add-members sheet
xsahil03x May 3, 2026
3a8db58
fix(sample): add-members sheet uses sample-app's SearchTextField
xsahil03x May 3, 2026
dbdddc6
fix(sample): add-members list fills available height
xsahil03x May 3, 2026
afd43fd
fix(sample): add-members sheet — match SDK empty/loading patterns
xsahil03x May 3, 2026
38d29ce
feat(sample): redesign Pinned Messages screen
xsahil03x May 3, 2026
f03ed01
feat(sample): redesign Photos & Videos screen
xsahil03x May 3, 2026
c7e365c
feat(sample): redesign Files screen
xsahil03x May 3, 2026
16cc246
refactor(ui)!: rebuild chat headers on top of StreamAppBar
xsahil03x May 4, 2026
f2fd7fe
chore(sample): polish sample app pages
xsahil03x May 4, 2026
994fbdd
chore(ui): finish header-redesign callsite migration
xsahil03x May 4, 2026
d8d281a
chore(ui): refresh attachment actions modal icons
xsahil03x May 4, 2026
0f83696
chore(docs): regenerate goldens after design refresh
xsahil03x May 4, 2026
7d671f8
feat(ui, localizations): localise reaction-detail sheet header
xsahil03x May 4, 2026
b9acc9a
docs(ui, localizations): document reactionsCountText
xsahil03x May 4, 2026
5be439d
chore(ui): drop redundant stream_core_flutter imports
xsahil03x May 4, 2026
1e800a0
chore(deps): update stream_core_flutter dependency reference
xsahil03x May 6, 2026
a5b89d2
feat(llc): add Channel.isOneToOne and tighten isGroup/isDistinct
xsahil03x May 7, 2026
db9ae73
feat(ui)!: add pin icon to channel list and rename indicator position
xsahil03x May 7, 2026
3c01f93
chore(ui): align StreamThreadListTile spacing with design tokens
xsahil03x May 7, 2026
24f6ba5
feat(sample): add Mute / Block actions to ChannelDetailSheet
xsahil03x May 7, 2026
e79c8e7
refactor(ui)!: remove unused public bottom sheets
xsahil03x May 7, 2026
4074c9f
chore: Update Goldens
xsahil03x May 7, 2026
2f78794
chore(docs): regenerate channel-list goldens for pin attribute
xsahil03x May 7, 2026
63b5b19
chore: Update Goldens
xsahil03x May 7, 2026
a1ae247
ci: serialize pub-get to avoid git-dep cache race
xsahil03x May 7, 2026
b066647
test(ui): rebuild gallery header tests against new navigation model
xsahil03x May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs_screenshots/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ dependencies:
stream_core_flutter:
git:
url: https://github.com/GetStream/stream-core-flutter.git
ref: c012cfe01900fcc9cc87cafbcb2d46eada243343
ref: 639f99401891f171e9cc2264eea822ef3ede3f99
path: packages/stream_core_flutter

dev_dependencies:
Expand Down
18 changes: 11 additions & 7 deletions docs/docs_screenshots/test/channel/channel_header_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ Widget _buildChannelHeaderScaffold({
child: StreamChannel(
showLoading: false,
channel: channel,
child: Scaffold(
appBar: header ?? const StreamChannelHeader(showBackButton: false),
),
child: Scaffold(appBar: header),
),
),
);
Expand All @@ -35,7 +33,7 @@ void main() {
goldenTest(
'channel header default',
fileName: 'channel_header',
constraints: const BoxConstraints.tightFor(width: 375, height: 56),
constraints: const BoxConstraints.tightFor(width: 375, height: 72),
builder: () {
final client = MockClient();
final clientState = MockClientState();
Expand All @@ -50,14 +48,20 @@ void main() {
channelName: 'General',
);

return _buildChannelHeaderScaffold(client: client, channel: channel);
return _buildChannelHeaderScaffold(
client: client,
channel: channel,
header: const StreamChannelHeader(
automaticallyImplyLeading: false,
),
);
},
);

goldenTest(
'channel header with custom title',
fileName: 'channel_header_custom_title',
constraints: const BoxConstraints.tightFor(width: 375, height: 56),
constraints: const BoxConstraints.tightFor(width: 375, height: 72),
builder: () {
final client = MockClient();
final clientState = MockClientState();
Expand All @@ -76,8 +80,8 @@ void main() {
client: client,
channel: channel,
header: const StreamChannelHeader(
showBackButton: false,
title: Text('My Custom Title'),
automaticallyImplyLeading: false,
),
);
},
Expand Down
13 changes: 7 additions & 6 deletions docs/docs_screenshots/test/channel/channel_list_header_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ Widget _buildListHeaderScaffold({
client: client,
streamChatThemeData: docsStreamChatThemeData(),
connectivityStream: Stream.value([ConnectivityResult.mobile]),
child: Scaffold(
appBar: header ?? const StreamChannelListHeader(),
),
child: Scaffold(appBar: header),
),
);
}
Expand All @@ -31,21 +29,24 @@ void main() {
goldenTest(
'channel list header default',
fileName: 'channel_list_header',
constraints: const BoxConstraints.tightFor(width: 375, height: 56),
constraints: const BoxConstraints.tightFor(width: 375, height: 72),
builder: () {
final client = MockClient();
final clientState = MockClientState();
when(() => client.state).thenReturn(clientState);
when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id', name: 'Alice'));

return _buildListHeaderScaffold(client: client);
return _buildListHeaderScaffold(
client: client,
header: const StreamChannelListHeader(),
);
},
);

goldenTest(
'channel list header with custom subtitle',
fileName: 'channel_list_header_custom_subtitle',
constraints: const BoxConstraints.tightFor(width: 375, height: 56),
constraints: const BoxConstraints.tightFor(width: 375, height: 72),
builder: () {
final client = MockClient();
final clientState = MockClientState();
Expand Down
Binary file modified docs/docs_screenshots/test/channel/goldens/ci/channel_header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/docs_screenshots/test/channel/goldens/ci/swipe_channel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/docs_screenshots/test/polls/goldens/macos/poll_creator.png
2 changes: 2 additions & 0 deletions docs/docs_screenshots/test/src/mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ void setupMockChannel({
when(() => channel.isDistinct).thenReturn(false);
when(() => channel.isMuted).thenReturn(false);
when(() => channel.isMutedStream).thenAnswer((_) => Stream.value(false));
when(() => channel.isPinned).thenReturn(false);
when(() => channel.isPinnedStream).thenAnswer((_) => Stream.value(false));
when(() => channel.extraDataStream).thenAnswer((_) => Stream.value({'name': channelName}));
when(() => channel.extraData).thenReturn({'name': channelName});
when(() => channel.name).thenReturn(channelName);
Expand Down
5 changes: 4 additions & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ categories:

command:
bootstrap:
# Run `pub get` sequentially to avoid races on the shared git-dep cache.
runPubGetInParallel: false

# Dart and Flutter environment used in the project.
environment:
sdk: ^3.10.0
Expand Down Expand Up @@ -98,7 +101,7 @@ command:
stream_core_flutter:
git:
url: https://github.com/GetStream/stream-core-flutter.git
ref: c012cfe01900fcc9cc87cafbcb2d46eada243343
ref: 639f99401891f171e9cc2264eea822ef3ede3f99
path: packages/stream_core_flutter
synchronized: ^3.1.0+1
thumblr: ^0.0.4
Expand Down
198 changes: 175 additions & 23 deletions migrations/redesign/headers_and_icons.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,34 +113,178 @@ The following icons have been **removed with no equivalent** in the new set:

## Header Widgets

`StreamChannelHeader`, `StreamChannelListHeader`, and `StreamThreadHeader` all received the same set of default-value changes.
All four chat headers — `StreamChannelHeader`, `StreamChannelListHeader`,
`StreamThreadHeader`, and `StreamGalleryHeader` — have been rebuilt on top
of the new design system's `StreamAppBar`. They now share a single slot
model (`leading` / `title` / `subtitle` / `trailing`) and a single theme
type (`StreamAppBarThemeData`), replacing the legacy
`AppBar`-style API.

### What changed across all headers

* **New layout primitive.** The headers render a [`StreamAppBar`] with a
fixed 72-px height (`kStreamHeaderHeight`) instead of Material's
`kToolbarHeight` (56 px). Pass the header directly to `Scaffold.appBar`
as before — it implements `PreferredSizeWidget`.
* **Slot model.** All four headers now expose `leading`, `title`,
`subtitle`, and `trailing` as plain `Widget?` slots. Anything you used
to compose with `actions: [...]` should move into `trailing:` (single
widget) or be wrapped into a `Row` you build yourself.
* **Auto-implied leading.** Headers that previously had `showBackButton`
/ `onBackPressed` now use `automaticallyImplyLeading` (default `true`)
to insert a default back button. To override, pass `leading:` directly;
to suppress, pass `automaticallyImplyLeading: false`.
* **Theme.** `StreamChannelHeaderThemeData`, `StreamChannelListHeaderThemeData`,
and `StreamGalleryHeaderThemeData` are deleted. The corresponding
accessors on `StreamChatThemeData` (`channelHeaderTheme`,
`channelListHeaderTheme`, `threadHeaderTheme`, `galleryHeaderTheme`)
now return [`StreamAppBarThemeData`].
* **Per-instance overrides.** A new `style: StreamAppBarStyle?` parameter
lets callers override colours / padding / typography for one instance —
it merges over the ambient `StreamAppBarTheme`.
* **Removed parameters.** `centerTitle`, `elevation`, `bottomOpacity`,
`bottom`, and `backgroundColor` are gone — the new bar always centres
the title, draws a hairline `borderSubtle` divider instead of an
elevation shadow, and reads its background from the theme / `style`.

### `StreamChannelHeader`

| Old parameter | New equivalent |
|---------------|----------------|
| `showBackButton: false` | `automaticallyImplyLeading: false` |
| `onBackPressed: cb` | `leading: StreamBackButton(onPressed: cb)` |
| `onTitleTap: cb` | `title: GestureDetector(onTap: cb, child: ...)` |
| `onImageTap: cb` | `onChannelAvatarPressed: (channel) => cb()` (or replace `trailing:`) |
| `showTypingIndicator: false` | `subtitle: Text(channel.name)` (or any custom widget) |
| `actions: [a, b]` | `trailing: Row(children: [a, b])` |
| `centerTitle`, `elevation`, `bottom`, `bottomOpacity`, `backgroundColor` | Use `style: StreamAppBarStyle(backgroundColor: ...)` for the background; the rest are gone |

### Breaking Changes
**Before:**

| Parameter | Old default | New default | Notes |
|-----------|-------------|-------------|-------|
| `centerTitle` | `bool?` (`null` → platform-adaptive) | `bool` (`true`) | Was `null` by default, which let Flutter centre on iOS and left-align on Android. Now always `true` — explicitly pass `centerTitle: false` to restore left-aligned titles. |
| `elevation` | `1` | `0` | Removes the drop shadow by default. Pass `elevation: 1` to restore the old appearance. |
| `scrolledUnderElevation` | — | `0` (new param) | Controls the elevation when content is scrolled under the header. |
```dart
StreamChannelHeader(
showBackButton: true,
onBackPressed: () => GoRouter.of(context).pop(),
onImageTap: () => openChannelInfo(channel),
showTypingIndicator: true,
elevation: 1,
)
```

### Migration
**After:**

```dart
StreamChannelHeader(
leading: StreamBackButton(onPressed: () => GoRouter.of(context).pop()),
onChannelAvatarPressed: (channel) => openChannelInfo(channel),
)
```

The default leading is now [`StreamBackButton`] with a channel-aware
unread badge; the default trailing is the channel avatar wrapped in a
48×48 tap target wired to `onChannelAvatarPressed`.

### `StreamChannelListHeader`

| Old parameter | New equivalent |
|---------------|----------------|
| `titleBuilder: (context, user) => ...` | `title: ...` (a `Widget`) |
| `onUserAvatarTap: cb` | `onUserAvatarPressed: cb` (renamed) |
| `onNewChatButtonTap: cb` | `trailing: StreamButton.icon(icon: Icon(context.streamIcons.plus), onPressed: cb)` |
| `preNavigationCallback`, `leading`, `actions`, `centerTitle`, `elevation`, `backgroundColor` | Removed — see notes below |

The leading slot is no longer caller-overridable: the SDK always renders
the signed-in user's avatar. When `onUserAvatarPressed` is null the
avatar mirrors Material `AppBar`'s auto-implied leading and opens the
enclosing `Scaffold`'s drawer if one exists, so the previous
`onUserAvatarTap: (_) => Scaffold.of(context).openDrawer()` callsites
can drop the callback entirely.

The trailing slot is empty by default — the SDK no longer ships a
"new chat" button. Pass your own widget if you want one.

**Before:**

```dart
// Before: title was platform-adaptive, header had a shadow
StreamChannelHeader()
StreamChannelListHeader()
StreamThreadHeader(parent: parentMessage)

// After: title always centred, no shadow
// If you relied on left-aligned titles on Android, pass centerTitle: false:
StreamChannelHeader(centerTitle: false)
StreamChannelListHeader(centerTitle: false)
StreamThreadHeader(parent: parentMessage, centerTitle: false)

// If you relied on the elevation shadow, restore it:
StreamChannelHeader(elevation: 1)
StreamChannelListHeader(
titleBuilder: (context, user) => Text(user?.name ?? 'Stream Chat'),
onUserAvatarTap: (_) => Scaffold.of(context).openDrawer(),
onNewChatButtonTap: () => GoRouter.of(context).pushNamed('new-chat'),
elevation: 1,
)
```

**After:**

```dart
StreamChannelListHeader(
title: Text('Chats', style: context.streamTextTheme.headingSm),
trailing: StreamButton.icon(
icon: Icon(context.streamIcons.plus),
onPressed: () => GoRouter.of(context).pushNamed('new-chat'),
),
)
```

### `StreamThreadHeader`

| Old parameter | New equivalent |
|---------------|----------------|
| `showBackButton: false` | `automaticallyImplyLeading: false` |
| `onBackPressed: cb` | `leading: StreamBackButton(onPressed: cb)` |
| `onTitleTap: cb` | `title: GestureDetector(onTap: cb, child: ...)` |
| `showTypingIndicator: false` | `subtitle: Text(...)` (or any custom widget) |
| `actions: [a, b]` | `trailing: Row(children: [a, b])` |
| `centerTitle`, `elevation`, `backgroundColor` | Use `style:`; the rest are gone |

The default subtitle is a [`StreamTypingIndicator`] that falls back to
the thread's reply count when nobody is typing.

### `StreamGalleryHeader`

| Old parameter | New equivalent |
|---------------|----------------|
| `showBackButton: false` | `automaticallyImplyLeading: false` |
| `onBackPressed: cb` | `leading: StreamBackButton(onPressed: cb)` |
| `onTitleTap: cb` | `title: GestureDetector(onTap: cb, child: ...)` |
| `onImageTap: cb` | `trailing: GestureDetector(onTap: cb, child: ...)` |
| `elevation`, `backgroundColor` | Use `style:`; the rest are gone |

`onShowMessage`, `onReplyMessage`, and `attachmentActionsModalBuilder`
are unchanged. The default trailing is still an icon button that opens
the attachment actions modal.

### Theming

The theme accessors on `StreamChatThemeData` keep their names but their
type changes to [`StreamAppBarThemeData`]:

```dart
// Before
StreamChannelHeaderThemeData(
color: theme.colorTheme.barsBg,
titleStyle: theme.textTheme.headlineBold,
)

// After
StreamAppBarThemeData(
style: StreamAppBarStyle(
backgroundColor: colorScheme.backgroundElevation1,
titleTextStyle: textTheme.headingSm,
),
)
```

Use `StreamAppBarTheme(data: ..., child: ...)` to override the theme for
a subtree, or pass `style:` directly on a single header instance.

### Header height

`kStreamHeaderHeight` (72 px) is now the canonical header height —
exposed from `package:stream_chat_flutter/stream_chat_flutter.dart`. If
you read `kToolbarHeight` (56 px) to size custom chrome that sits next
to a Stream header, switch to `kStreamHeaderHeight` to stay aligned.

---

## StreamChat.componentBuilders
Expand Down Expand Up @@ -225,7 +369,15 @@ StreamChat(
## Migration Checklist

- [ ] Replace all `StreamSvgIcon(icon: StreamSvgIcons.*)` with `Icon(context.streamIcons.*)` using the mapping table above
- [ ] If you relied on platform-adaptive `centerTitle` behaviour on `StreamChannelHeader` / `StreamChannelListHeader` / `StreamThreadHeader`, pass `centerTitle: false` explicitly for Android-style left-aligned titles
- [ ] If you relied on the `elevation: 1` shadow on headers, pass `elevation: 1` explicitly
- [ ] Replace `showBackButton: false` with `automaticallyImplyLeading: false` on every header that used it
- [ ] Move `onBackPressed: cb` to `leading: StreamBackButton(onPressed: cb)`
- [ ] Move `onTitleTap` / `onImageTap` callbacks into `GestureDetector` wrappers around the new `title:` / `trailing:` slots (or use `onChannelAvatarPressed` on `StreamChannelHeader`)
- [ ] Rename `StreamChannelListHeader.onUserAvatarTap` to `onUserAvatarPressed`; drop manual `Scaffold.of(context).openDrawer()` callbacks if you only want the default drawer behaviour
- [ ] Replace `StreamChannelListHeader.onNewChatButtonTap` with a `trailing: StreamButton.icon(...)` widget — the SDK no longer ships the default button
- [ ] Replace `titleBuilder: (context, user) => ...` with `title: ...` (a `Widget`)
- [ ] Replace `actions: [a, b]` with `trailing: Row(children: [a, b])` — only one trailing slot is exposed
- [ ] Drop `centerTitle`, `elevation`, `bottomOpacity`, `bottom`, and `backgroundColor` from header callsites; use `style: StreamAppBarStyle(backgroundColor: ...)` if you need to override the background
- [ ] Update theme overrides: `StreamChannelHeaderThemeData` / `StreamChannelListHeaderThemeData` / `StreamGalleryHeaderThemeData` are deleted — switch to `StreamAppBarThemeData`
- [ ] If you sized custom chrome to `kToolbarHeight` next to a Stream header, switch to `kStreamHeaderHeight` (72 px)
- [ ] Optionally move `StreamComponentFactory` wrapping into the `componentBuilders` parameter on `StreamChat`
- [ ] Use the new `attachmentBuilders`, `reactionType`, and `reactionPosition` fields on `StreamChatConfigurationData` if you need custom attachment rendering or global reaction style control
4 changes: 4 additions & 0 deletions migrations/redesign/localizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ String get loadingReactionsError => 'Error loading reactions';
@override
String get tapToRemoveReactionLabel => 'Tap to remove';

@override
String reactionsCountText(int count) =>
count == 1 ? '1 Reaction' : '$count Reactions';

// Confirmation dialogs
@override
String get confirmLabel => 'CONFIRM';
Expand Down
6 changes: 6 additions & 0 deletions packages/stream_chat/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@

- Added `StreamChatClient.recoverStateOnReconnect` (defaults to `true`); when `false`, the client no longer auto-re-queries active channels on connection recovery — useful for consumers driving their own refresh from the `connectionRecovered` event.
- Added `Message.updateWith(Message? other)` — merges a server-side update onto the local message while preserving locally-known `poll`, `sharedLocation`, `ownReactions`, and nested `quotedMessage` enrichment when the server omits them.
- Added `Channel.isOneToOne` — true when the channel is `isDistinct` and has exactly two members. For the looser count-only check, inline `channel.memberCount == 2`.

⚠️ Deprecated

- Deprecated `Message.syncWith` in favor of `Message.updateWith`. Note the arguments are flipped: `local.updateWith(remote)` replaces `remote.syncWith(local)`.

🔄 Changed

- Tightened `Channel.isGroup` from `memberCount != 2` to `memberCount > 2 || !isDistinct`. Two-member non-distinct channels now correctly report as groups, and 1-member distinct channels no longer do. Migrate via `!channel.isOneToOne` or `channel.memberCount != 2`.
- Tightened `Channel.isDistinct` to require the `!members-` prefix (with trailing dash), matching the backend's `DistinctChannelPrefix` constant. Real server-generated ids always include the dash; only malformed/test ids that previously matched the looser `!members` check are affected.

🐞 Fixed

- Fixed reactions, polls, and quoted-message enrichment briefly flickering after the app returned from the background. The reconnect path now refreshes channels and advances `lastSyncAt` to the current time instead of replaying every event since `lastSyncAt` through `handleEvent`. `client.sync()` remains available for consumers that need event-level replay.
Expand Down
Loading
Loading