Skip to content
60 changes: 60 additions & 0 deletions migrations/v10-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ This guide covers all breaking changes in **Stream Chat Flutter SDK v10.0.0**. W
- [MessageState](#messagestate)
- [File Upload](#file-upload)
- [AttachmentFileUploader](#attachmentfileuploader)
- [Unread Threads Banner](#unread-threads-banner)
- [Appendix: Beta Release Timeline](#appendix-beta-release-timeline)
- [Migration Checklist](#migration-checklist)

Expand Down Expand Up @@ -60,6 +61,7 @@ Each breaking change section includes an **"Introduced in"** tag so you can quic
| [**Message UI**](#message-ui) | New `onAttachmentTap` signature with fallback support, generic `StreamMessageAction` |
| [**Message State**](#message-state--deletion) | `MessageDeleteScope` replaces `bool hard`, delete-for-me support |
| [**File Upload**](#file-upload) | Four new abstract methods on `AttachmentFileUploader` |
| [**Unread Threads Banner**](#unread-threads-banner) | Wrapper pattern with `child`, `enabled`, `onRefresh`; removed `onTap`, `minHeight` |

---

Expand Down Expand Up @@ -991,6 +993,58 @@ class CustomAttachmentFileUploader implements AttachmentFileUploader {

---

## Unread Threads Banner

#### Key Changes:

- `StreamUnreadThreadsBanner` is now a **wrapper widget** instead of a standalone banner placed in a `Column`.
- New `child` parameter — wrap your `StreamThreadListView` instead of placing the banner as a sibling.
- `onTap` (`VoidCallback?`) replaced by `onRefresh` (`Future<void> Function()?`) — shows a loading spinner while the future is pending.
- `minHeight` parameter **removed**.
- `margin` default changed from `EdgeInsets.symmetric(horizontal: 8, vertical: 6)` to `EdgeInsets.zero`.
- `padding` default changed from `EdgeInsets.symmetric(horizontal: 16)` to `EdgeInsets.all(spacing.sm)`.

#### Migration Steps:

**Before:**
```dart
Column(
children: [
ValueListenableBuilder(
valueListenable: controller.unseenThreadIds,
builder: (_, unreadThreads, __) => StreamUnreadThreadsBanner(
unreadThreads: unreadThreads,
onTap: () => controller
.refresh(resetValue: false)
.then((_) => controller.clearUnseenThreadIds()),
),
),
Expanded(
child: StreamThreadListView(controller: controller),
),
],
);
```

**After:**
```dart
ValueListenableBuilder<Set<String>>(
valueListenable: controller.unseenThreadIds,
builder: (context, unseenThreadIds, child) => StreamUnreadThreadsBanner(
enabled: unseenThreadIds.isNotEmpty,
unreadThreads: unseenThreadIds,
onRefresh: () async {
await controller.refresh(resetValue: false);
controller.clearUnseenThreadIds();
},
child: child!,
),
child: StreamThreadListView(controller: controller),
);
```

---

## Appendix: Beta Release Timeline

This appendix provides a chronological reference of breaking changes by beta version for users upgrading from specific pre-release versions.
Expand Down Expand Up @@ -1036,6 +1090,12 @@ This appendix provides a chronological reference of breaking changes by beta ver

## Migration Checklist

### Unread Threads Banner
- [ ] Replace `Column` + `Expanded` layout with `StreamUnreadThreadsBanner(child: StreamThreadListView(...))`
- [ ] Replace `onTap` with `onRefresh` (returns `Future<void>`)
- [ ] Add `enabled: true` to show the banner (defaults to hidden)
- [ ] Remove `minHeight` parameter if used

### For v10.0.0-beta.12:
- [ ] Replace `ArgumentError('The size of the attachment is...')` with `AttachmentTooLargeError` (provides `fileSize` and `maxSize` properties)
- [ ] Replace `ArgumentError('The maximum number of attachments is...')` with `AttachmentLimitReachedError` (provides `maxCount` property)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,9 @@ abstract class Translations {
/// The label for "$count new threads"
String newThreadsLabel({required int count});

/// The label for "Loading..."
String get loadingLabel;

/// The label for "Slide to cancel"
String get slideToCancelLabel;

Expand Down Expand Up @@ -1326,6 +1329,9 @@ Attachment limit exceeded: it's not possible to add more than $limit attachments
return '$count new threads';
}

@override
String get loadingLabel => 'Loading...';

@override
String get slideToCancelLabel => 'Slide to cancel';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/misc/timestamp.dart';
import 'package:stream_chat_flutter/src/utils/date_formatter.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_core_flutter/stream_core_flutter.dart';

/// {@template streamThreadListTile}
/// A widget that displays a thread in a list.
Expand Down Expand Up @@ -105,75 +106,77 @@ class _DefaultStreamThreadListTile extends StatelessWidget {
channel?.formatName(currentUser: currentUser) ?? avatarUser?.name ?? context.translations.noTitleText;
final participantUsers = thread.threadParticipants.map((it) => it.user).nonNulls.toList(growable: false);

return Material(
color: effectiveBackgroundColor,
child: InkWell(
return StreamListTileTheme(
data: StreamListTileThemeData(
contentPadding: effectivePadding,
backgroundColor: WidgetStatePropertyAll(effectiveBackgroundColor),
),
child: StreamListTileContainer(
enabled: true,
selected: false,
onTap: props.onTap,
onLongPress: props.onLongPress,
child: Padding(
padding: effectivePadding,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (avatarUser case final user?)
Padding(
padding: const EdgeInsetsDirectional.only(end: 12),
child: StreamUserAvatar(
user: user,
size: StreamAvatarSize.xl,
),
)
else
const Padding(
padding: EdgeInsetsDirectional.only(end: 12),
child: SizedBox.square(dimension: 40),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (avatarUser case final user?)
Padding(
padding: const EdgeInsetsDirectional.only(end: 12),
child: StreamUserAvatar(
user: user,
size: StreamAvatarSize.xl,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ThreadTitle(
channelName: channelName,
style: effectiveChannelNameStyle,
),
)
else
const Padding(
padding: EdgeInsetsDirectional.only(end: 12),
child: SizedBox.square(dimension: 40),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ThreadTitle(
channelName: channelName,
style: effectiveChannelNameStyle,
),
),
if (unreadMessageCount case final count? when count > 0) ...[
const SizedBox(width: 8),
ThreadUnreadCount(
unreadCount: count,
style: effectiveUnreadCountStyle,
backgroundColor: effectiveUnreadCountBackgroundColor,
),
if (unreadMessageCount case final count? when count > 0) ...[
const SizedBox(width: 8),
ThreadUnreadCount(
unreadCount: count,
style: effectiveUnreadCountStyle,
backgroundColor: effectiveUnreadCountBackgroundColor,
),
],
],
),
const SizedBox(height: 2),
ThreadRootMessagePreview(
parentMessage: parentMessage,
channel: channel,
language: language,
style: effectiveReplyToMessageStyle,
emptyStyle: effectiveLatestReplyMessageStyle,
),
const SizedBox(height: 8),
ThreadFooter(
participantUsers: participantUsers,
replyCount: thread.replyCount,
latestActivityAt: latestActivityAt,
replyCountStyle: effectiveReplyCountStyle,
timestampStyle: effectiveTimestampStyle,
timestampFormatter: effectiveTimestampFormatter,
),
],
),
],
),
const SizedBox(height: 2),
ThreadRootMessagePreview(
parentMessage: parentMessage,
channel: channel,
language: language,
style: effectiveReplyToMessageStyle,
emptyStyle: effectiveLatestReplyMessageStyle,
),
const SizedBox(height: 8),
ThreadFooter(
participantUsers: participantUsers,
replyCount: thread.replyCount,
latestActivityAt: latestActivityAt,
replyCountStyle: effectiveReplyCountStyle,
timestampStyle: effectiveTimestampStyle,
timestampFormatter: effectiveTimestampFormatter,
),
],
),
],
),
),
],
),
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ typedef StreamThreadListViewIndexedWidgetBuilder = StreamScrollViewIndexedWidget
///
/// Uses a [StreamThreadListController] to load threads in paginated form.
///
/// Wrap with [StreamUnreadThreadsBanner] to show a banner above the list when
/// new unseen threads are available.
///
/// Each row is rendered using [StreamThreadListTile], which can be customized
/// app-wide through [StreamComponentFactory].
///
Expand All @@ -40,6 +43,7 @@ typedef StreamThreadListViewIndexedWidgetBuilder = StreamScrollViewIndexedWidget
/// ```
///
/// See also:
/// * [StreamUnreadThreadsBanner], which wraps this view to show new threads.
/// * [StreamMessageWidget], which renders each thread's parent message.
/// * [StreamThreadListController]
/// {@endtemplate}
Expand Down
Loading
Loading