From aab830e766db88101452a033e5596d04088d58e9 Mon Sep 17 00:00:00 2001 From: Brazol Date: Wed, 18 Mar 2026 16:08:43 +0100 Subject: [PATCH 1/2] unread indicator redesign --- .../stream_chat_component_builders.dart | 3 + .../message_list_view/message_list_view.dart | 4 +- .../unread_indicator_button.dart | 113 +++++++++++++----- .../lib/stream_chat_flutter.dart | 1 + 4 files changed, 87 insertions(+), 34 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart b/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart index a692917a5..a3172105f 100644 --- a/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart +++ b/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart @@ -1,4 +1,5 @@ import 'package:flutter/widgets.dart'; +import 'package:stream_chat_flutter/src/message_list_view/unread_indicator_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Builds the list of component builders for the stream chat components. @@ -12,6 +13,7 @@ Iterable> streamChatComponentBuilders({ StreamComponentBuilder? messageComposerInputHeader, StreamComponentBuilder? messageComposerInputTrailing, StreamComponentBuilder? messageWidget, + StreamComponentBuilder? unreadIndicator, }) { final builders = [ if (channelListItem != null) StreamComponentBuilderExtension(builder: channelListItem), @@ -23,6 +25,7 @@ Iterable> streamChatComponentBuilders({ if (messageComposerInputHeader != null) StreamComponentBuilderExtension(builder: messageComposerInputHeader), if (messageComposerInputTrailing != null) StreamComponentBuilderExtension(builder: messageComposerInputTrailing), if (messageWidget != null) StreamComponentBuilderExtension(builder: messageWidget), + if (unreadIndicator != null) StreamComponentBuilderExtension(builder: unreadIndicator), ]; return builders; diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart index 51cc86cf6..640cc83f5 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart @@ -1115,8 +1115,8 @@ class _StreamMessageListViewState extends State { ); return Positioned( - bottom: 8, - right: 8, + bottom: 16, + right: 16, width: 40, height: 40, child: Stack( diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart b/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart index 5d8c0c8c3..b3e76170e 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/unread_indicator_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/components/stream_chat_component_builders.dart'; import 'package:stream_chat_flutter/src/misc/empty_widget.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/utils/extensions.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_core_flutter/stream_core_flutter.dart'; @@ -24,6 +24,32 @@ typedef UnreadIndicatorBuilder = OnUnreadIndicatorDismissTap onDismissTap, ); +/// Properties for configuring an [UnreadIndicatorButton]. +/// +/// This class holds all the configuration options for an unread indicator, +/// allowing them to be passed through the [StreamComponentFactory]. +/// +/// See also: +/// +/// * [UnreadIndicatorButton], which uses these properties. +class UnreadIndicatorProps { + /// Creates properties for an unread indicator. + const UnreadIndicatorProps({ + required this.unreadCount, + required this.onTap, + required this.onDismissTap, + }); + + /// The number of unread messages. + final int unreadCount; + + /// Callback triggered when the indicator is tapped. + final OnUnreadIndicatorTap onTap; + + /// Callback triggered when the dismiss button is tapped. + final OnUnreadIndicatorDismissTap onDismissTap; +} + /// {@template unreadIndicatorButton} /// A button that displays the number of unread messages in a channel. /// @@ -52,7 +78,8 @@ class UnreadIndicatorButton extends StatelessWidget { /// Optional builder for customizing the appearance of the unread indicator. /// - /// If not provided, a default indicator will be built. + /// If not provided, falls back to [StreamComponentFactory], then to the + /// default indicator. final UnreadIndicatorBuilder? unreadIndicatorBuilder; @override @@ -67,46 +94,68 @@ class UnreadIndicatorButton extends StatelessWidget { final unreadCount = currentUserRead.unreadMessages; if (unreadCount <= 0) return const Empty(); + final props = UnreadIndicatorProps( + unreadCount: unreadCount, + onTap: onTap, + onDismissTap: onDismissTap, + ); + if (unreadIndicatorBuilder case final builder?) { return builder(unreadCount, onTap, onDismissTap); } - final theme = StreamChatTheme.of(context); - final textTheme = theme.textTheme; - final colorTheme = theme.colorTheme; + final factoryBuilder = context.chatComponentBuilder(); + if (factoryBuilder != null) return factoryBuilder(context, props); + + final colorTheme = context.streamColorScheme; + final textTheme = context.streamTextTheme; return Material( - elevation: 4, + elevation: 3, clipBehavior: Clip.antiAlias, - color: colorTheme.textLowEmphasis, + color: colorTheme.backgroundElevation1, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18), + borderRadius: BorderRadiusDirectional.all(context.streamRadius.max), ), - child: InkWell( - onTap: () => onTap(currentUserRead.lastReadMessageId), - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 2, 8, 2), - child: Row( - children: [ - Text( - context.translations.unreadCountIndicatorLabel( - unreadCount: unreadCount, - ), - style: textTheme.body.copyWith(color: colorTheme.barsBg), - ), - const SizedBox(width: 12), - IconButton( - iconSize: 24, - icon: Icon(context.streamIcons.crossMedium), - padding: const EdgeInsets.all(4), - style: IconButton.styleFrom( - foregroundColor: colorTheme.barsBg, - minimumSize: const Size.square(24), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - onPressed: onDismissTap, + child: SizedBox( + height: 40, + child: InkWell( + onTap: () => onTap(currentUserRead.lastReadMessageId), + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 2, 8, 2), + child: IntrinsicHeight( + child: Row( + children: [ + Icon( + context.streamIcons.arrowUp, + size: 20, + ), + SizedBox(width: context.streamSpacing.xs), + Text( + context.translations.unreadCountIndicatorLabel( + unreadCount: unreadCount, + ), + style: textTheme.bodyEmphasis.copyWith(color: colorTheme.textSecondary), + ), + SizedBox(width: context.streamSpacing.md), + VerticalDivider( + color: colorTheme.borderDefault, + thickness: 1, + ), + IconButton( + iconSize: 20, + icon: Icon(context.streamIcons.crossMedium), + padding: const EdgeInsets.all(5), + style: IconButton.styleFrom( + foregroundColor: colorTheme.textSecondary, + minimumSize: const Size.square(20), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: onDismissTap, + ), + ], ), - ], + ), ), ), ), diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index b5b8696cf..f44bc6323 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -108,6 +108,7 @@ export 'src/message_input/stream_message_send_button.dart'; export 'src/message_input/stream_message_text_field.dart'; export 'src/message_list_view/message_details.dart'; export 'src/message_list_view/message_list_view.dart'; +export 'src/message_list_view/unread_indicator_button.dart'; export 'src/message_modal/message_action_confirmation_modal.dart'; export 'src/message_modal/message_actions_modal.dart'; export 'src/message_modal/message_modal.dart'; From 8a2afc11f61427331add8e008499358f6f828d45 Mon Sep 17 00:00:00 2001 From: Brazol Date: Thu, 19 Mar 2026 10:43:58 +0100 Subject: [PATCH 2/2] tweak --- .../lib/src/components/stream_chat_component_builders.dart | 1 - .../lib/src/message_list_view/message_list_view.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart b/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart index a3172105f..08c065e4e 100644 --- a/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart +++ b/packages/stream_chat_flutter/lib/src/components/stream_chat_component_builders.dart @@ -1,5 +1,4 @@ import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/message_list_view/unread_indicator_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Builds the list of component builders for the stream chat components. diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart index 640cc83f5..fdf1717e3 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart @@ -9,7 +9,6 @@ import 'package:stream_chat_flutter/src/message_list_view/floating_date_divider. import 'package:stream_chat_flutter/src/message_list_view/loading_indicator.dart'; import 'package:stream_chat_flutter/src/message_list_view/mlv_utils.dart'; import 'package:stream_chat_flutter/src/message_list_view/thread_separator.dart'; -import 'package:stream_chat_flutter/src/message_list_view/unread_indicator_button.dart'; import 'package:stream_chat_flutter/src/message_list_view/unread_messages_separator.dart'; import 'package:stream_chat_flutter/src/message_widget/ephemeral_message.dart'; import 'package:stream_chat_flutter/src/misc/empty_widget.dart';