From de9b0e377aa7a402d6afc9ce280d7412eb828b39 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 16 Apr 2026 16:49:47 +0200 Subject: [PATCH 1/5] Cleanup message theme and usages --- .../example/lib/tutorial_part_6.dart | 13 - .../src/bottom_sheets/edit_message_sheet.dart | 129 ------- .../src/message_input/attachment_button.dart | 59 --- .../message_input/quoted_message_widget.dart | 347 ------------------ .../message_input/stream_message_input.dart | 9 +- .../stream_message_send_button.dart | 74 ---- .../src/message_widget/message_widget.dart | 3 - .../lib/src/misc/markdown_message.dart | 14 +- .../reactions/desktop_reactions_builder.dart | 231 ------------ .../lib/src/reactions/user_reactions.dart | 160 -------- .../lib/src/theme/message_input_theme.dart | 295 --------------- .../lib/src/theme/message_theme.dart | 345 ----------------- .../lib/src/theme/stream_chat_theme.dart | 118 ------ .../lib/src/theme/themes.dart | 2 - .../lib/src/utils/typedefs.dart | 13 - .../lib/stream_chat_flutter.dart | 4 - .../edit_message_sheet_test.dart | 84 ----- .../message_input/attachment_button_test.dart | 88 ----- .../stream_message_send_button_test.dart | 195 ---------- .../message_actions_modal_test.dart | 8 +- .../src/theme/message_input_theme_test.dart | 126 ------- .../test/src/theme/message_theme_test.dart | 92 ----- .../pages/channel_file_display_screen.dart | 6 +- .../pages/channel_media_display_screen.dart | 6 +- sample_app/lib/pages/chat_info_screen.dart | 11 +- sample_app/lib/pages/group_info_screen.dart | 14 +- sample_app/lib/routes/app_routes.dart | 5 +- sample_app/lib/widgets/channel_list.dart | 5 +- 28 files changed, 21 insertions(+), 2435 deletions(-) delete mode 100644 packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart delete mode 100644 packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart delete mode 100644 packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart delete mode 100644 packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart delete mode 100644 packages/stream_chat_flutter/lib/src/reactions/desktop_reactions_builder.dart delete mode 100644 packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart delete mode 100644 packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart delete mode 100644 packages/stream_chat_flutter/lib/src/theme/message_theme.dart delete mode 100644 packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart delete mode 100644 packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart delete mode 100644 packages/stream_chat_flutter/test/src/message_input/stream_message_send_button_test.dart delete mode 100644 packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart delete mode 100644 packages/stream_chat_flutter/test/src/theme/message_theme_test.dart diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart index 7ef017dacf..f0ef523b30 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart @@ -74,19 +74,6 @@ class MyApp extends StatelessWidget { fit: BoxFit.cover, ), ), - ownMessageTheme: const StreamMessageThemeData( - urlAttachmentTitleMaxLine: 1, - ), - otherMessageTheme: StreamMessageThemeData( - messageBackgroundColor: colorTheme.textHighEmphasis, - messageTextStyle: TextStyle( - color: colorTheme.barsBg, - ), - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(8), - ), - urlAttachmentTitleMaxLine: 1, - ), ).merge(defaultTheme); return MaterialApp( diff --git a/packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart b/packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart deleted file mode 100644 index c42baa1d6e..0000000000 --- a/packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template showEditMessageSheet} -/// Displays an interactive modal bottom sheet to edit a message. -/// {@endtemplate} -Future showEditMessageSheet({ - required BuildContext context, - required Message message, - required Channel channel, - EditMessageInputBuilder? editMessageInputBuilder, -}) { - final messageInputTheme = StreamMessageInputTheme.of(context); - - return showModalBottomSheet( - context: context, - elevation: 2, - isScrollControlled: true, - clipBehavior: Clip.antiAlias, - backgroundColor: messageInputTheme.inputBackgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) => EditMessageSheet( - channel: channel, - message: message, - editMessageInputBuilder: editMessageInputBuilder, - ), - ); -} - -/// {@template editMessageSheet} -/// Allows a user to edit the selected message. -/// {@endtemplate} -class EditMessageSheet extends StatefulWidget { - /// {@macro editMessageSheet} - const EditMessageSheet({ - super.key, - required this.message, - required this.channel, - this.editMessageInputBuilder, - }); - - /// {@macro editMessageInputBuilder} - final EditMessageInputBuilder? editMessageInputBuilder; - - /// The message to edit. - final Message message; - - /// The [StreamChannel] above this widget. - final Channel channel; - - @override - State createState() => _EditMessageSheetState(); -} - -class _EditMessageSheetState extends State { - late final controller = StreamMessageInputController( - message: widget.message, - ); - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return KeyboardShortcutRunner( - onEscapeKeypress: () => Navigator.of(context).pop(), - child: Padding( - padding: MediaQuery.of(context).viewInsets, - child: StreamChannel( - channel: widget.channel, - child: Flex( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: Icon( - context.streamIcons.edit, - color: streamChatThemeData.colorTheme.disabled, - ), - ), - Text( - context.translations.editMessageLabel, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - IconButton( - visualDensity: VisualDensity.compact, - icon: Icon( - context.streamIcons.xmark, - color: streamChatThemeData.colorTheme.textLowEmphasis, - ), - onPressed: Navigator.of(context).pop, - ), - ], - ), - ), - if (widget.editMessageInputBuilder != null) - widget.editMessageInputBuilder!(context, widget.message) - else - StreamMessageInput( - messageInputController: controller, - preMessageSending: (m) { - FocusScope.of(context).unfocus(); - Navigator.of(context).pop(); - return m; - }, - ), - ], - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart deleted file mode 100644 index 10b825731f..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template attachmentButton} -/// A button for adding attachments to a chat on mobile. -/// {@endtemplate} -class AttachmentButton extends StatelessWidget { - /// {@macro attachmentButton} - const AttachmentButton({ - super.key, - required this.onPressed, - this.color, - this.icon, - this.size = kDefaultMessageInputIconSize, - }); - - /// The color of the button. - /// Should be set if no [icon] is provided. - final Color? color; - - /// The callback to perform when the button is tapped or clicked. - final VoidCallback onPressed; - - /// The icon to display inside the button. - /// if not provided, a default icon will be used - /// and [color] property should be set. - final Widget? icon; - - /// The size of the button and splash radius. - final double size; - - /// Returns a copy of this object with the given fields updated. - AttachmentButton copyWith({ - Key? key, - Color? color, - VoidCallback? onPressed, - Widget? icon, - double? size, - }) { - return AttachmentButton( - key: key ?? this.key, - color: color ?? this.color, - onPressed: onPressed ?? this.onPressed, - icon: icon ?? this.icon, - size: size ?? this.size, - ); - } - - @override - Widget build(BuildContext context) { - return StreamMessageInputIconButton( - color: color, - iconSize: size, - onPressed: onPressed, - icon: icon ?? Icon(context.streamIcons.attachment), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart deleted file mode 100644 index bed4365e5f..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart +++ /dev/null @@ -1,347 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/clear_input_item_button.dart'; -import 'package:stream_chat_flutter/src/misc/empty_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -typedef _Builders = Map; - -/// {@template streamQuotedMessage} -/// Widget for the quoted message. -/// {@endtemplate} -class StreamQuotedMessageWidget extends StatelessWidget { - /// {@macro streamQuotedMessage} - const StreamQuotedMessageWidget({ - super.key, - required this.message, - required this.messageTheme, - this.reverse = false, - this.showBorder = false, - this.textLimit = 170, - this.textBuilder, - this.attachmentThumbnailBuilders, - this.padding = const EdgeInsets.all(8), - this.onQuotedMessageClear, - }); - - /// The message - final Message message; - - /// The message theme - final StreamMessageThemeData messageTheme; - - /// If true the widget will be mirrored - final bool reverse; - - /// If true the message will show a grey border - final bool showBorder; - - /// limit of the text message shown - final int textLimit; - - /// Map that defines a thumbnail builder for an attachment type - final _Builders? attachmentThumbnailBuilders; - - /// Padding around the widget - final EdgeInsetsGeometry padding; - - /// Callback for clearing quoted messages. - final VoidCallback? onQuotedMessageClear; - - /// {@macro textBuilder} - final Widget Function(BuildContext, Message)? textBuilder; - - @override - Widget build(BuildContext context) { - final children = [ - Flexible( - child: _QuotedMessage( - message: message, - textLimit: textLimit, - messageTheme: messageTheme, - showBorder: showBorder, - reverse: reverse, - textBuilder: textBuilder, - onQuotedMessageClear: onQuotedMessageClear, - attachmentThumbnailBuilders: attachmentThumbnailBuilders, - ), - ), - const SizedBox(width: 8), - if (message.user != null) - StreamUserAvatar( - size: .sm, - user: message.user!, - showOnlineIndicator: false, - ), - ]; - return Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: reverse ? children.reversed.toList() : children, - ), - ); - } -} - -class _QuotedMessage extends StatelessWidget { - const _QuotedMessage({ - required this.message, - required this.textLimit, - required this.messageTheme, - required this.showBorder, - required this.reverse, - this.textBuilder, - this.onQuotedMessageClear, - this.attachmentThumbnailBuilders, - }); - - final Message message; - final int textLimit; - final VoidCallback? onQuotedMessageClear; - final StreamMessageThemeData messageTheme; - final bool showBorder; - final bool reverse; - final Widget Function(BuildContext, Message)? textBuilder; - - final _Builders? attachmentThumbnailBuilders; - - bool get _hasAttachments => message.attachments.isNotEmpty; - - bool get _containsText => message.text?.isNotEmpty == true; - - bool get _containsLinkAttachment => message.attachments.any((it) => it.type == AttachmentType.urlPreview); - - bool get _isGiphy => message.attachments.any((element) => element.type == AttachmentType.giphy); - - bool get _isDeleted => message.isDeleted || message.deletedAt != null; - - bool get _isPoll => message.poll != null; - - @override - Widget build(BuildContext context) { - final isOnlyEmoji = message.text!.isOnlyEmoji; - var msg = _hasAttachments && !_containsText - ? message.copyWith(text: message.attachments.last.title ?? '') - : message; - if (msg.text!.length > textLimit) { - msg = msg.copyWith(text: '${msg.text!.substring(0, textLimit - 3)}...'); - } - - List children; - if (_isDeleted) { - // Show deleted message text - children = [ - Text( - context.translations.messageDeletedLabel, - style: messageTheme.messageTextStyle?.copyWith( - fontStyle: FontStyle.italic, - color: messageTheme.createdAtStyle?.color, - ), - ), - ]; - } else if (_isPoll) { - // Show poll message - children = [ - Flexible( - child: Text( - '📊 ${message.poll?.name}', - style: messageTheme.messageTextStyle?.copyWith(fontSize: 12), - ), - ), - ]; - } else if (message.sharedLocation case final location?) { - // Show shared location message - children = [ - Flexible( - child: Text( - context.translations.locationLabel(isLive: location.isLive), - style: messageTheme.messageTextStyle?.copyWith(fontSize: 12), - ), - ), - ]; - } else { - // Show quoted message - children = [ - if (_hasAttachments) - _ParseAttachments( - message: message, - messageTheme: messageTheme, - attachmentThumbnailBuilders: attachmentThumbnailBuilders, - ), - if (msg.text!.isNotEmpty && !_isGiphy) - Flexible( - child: - textBuilder?.call(context, msg) ?? - StreamMarkdownMessage( - data: msg.replaceMentions().text ?? '', - messageTheme: isOnlyEmoji && _containsText - ? messageTheme.copyWith( - messageTextStyle: messageTheme.messageTextStyle?.copyWith( - fontSize: 32, - ), - ) - : messageTheme.copyWith( - messageTextStyle: messageTheme.messageTextStyle?.copyWith( - fontSize: 12, - ), - ), - ), - ), - ]; - } - - // Add clear button if needed. - if (isDesktopDeviceOrWeb && onQuotedMessageClear != null) { - children.insert( - 0, - ClearInputItemButton(onTap: onQuotedMessageClear), - ); - } - - return Container( - decoration: BoxDecoration( - color: _getBackgroundColor(context), - border: showBorder - ? Border.all( - color: StreamChatTheme.of(context).colorTheme.disabled, - ) - : null, - borderRadius: BorderRadius.only( - topRight: const Radius.circular(12), - topLeft: const Radius.circular(12), - bottomRight: reverse ? const Radius.circular(12) : Radius.zero, - bottomLeft: reverse ? Radius.zero : const Radius.circular(12), - ), - ), - padding: const EdgeInsets.all(8), - child: Row( - spacing: 8, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: reverse ? MainAxisAlignment.end : MainAxisAlignment.start, - children: reverse ? children.reversed.toList() : children, - ), - ); - } - - Color? _getBackgroundColor(BuildContext context) { - if (_containsLinkAttachment && !_isDeleted) { - return messageTheme.urlAttachmentBackgroundColor; - } - return messageTheme.messageBackgroundColor; - } -} - -class _ParseAttachments extends StatelessWidget { - const _ParseAttachments({ - required this.message, - required this.messageTheme, - this.attachmentThumbnailBuilders, - }); - - final Message message; - final StreamMessageThemeData messageTheme; - final _Builders? attachmentThumbnailBuilders; - - @override - Widget build(BuildContext context) { - final attachment = message.attachments.first; - - var attachmentBuilders = attachmentThumbnailBuilders; - attachmentBuilders ??= _createDefaultAttachmentBuilders(); - - // Build the attachment widget using the builder for the attachment type. - final attachmentWidget = attachmentBuilders[attachment.type]?.call( - context, - attachment, - ); - - // Return empty container if no attachment widget is returned. - if (attachmentWidget == null) return const Empty(); - - final colorTheme = StreamChatTheme.of(context).colorTheme; - - var clipBehavior = Clip.none; - ShapeDecoration? decoration; - if (attachment.type != AttachmentType.file && attachment.type != AttachmentType.voiceRecording) { - clipBehavior = Clip.hardEdge; - decoration = ShapeDecoration( - shape: RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(8), - ), - ); - } - - return Container( - key: Key(attachment.id), - clipBehavior: clipBehavior, - decoration: decoration, - constraints: const BoxConstraints.tightFor(width: 36, height: 36), - child: AbsorbPointer(child: attachmentWidget), - ); - } - - _Builders _createDefaultAttachmentBuilders() { - Widget _createMediaThumbnail(BuildContext context, Attachment media) { - return StreamImageAttachmentThumbnail( - image: media, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - } - - Widget _createUrlThumbnail(BuildContext context, Attachment media) { - return StreamImageAttachmentThumbnail( - image: media, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - } - - Widget _createFileThumbnail(BuildContext context, Attachment file) { - Widget thumbnail = StreamFileAttachmentThumbnail( - file: file, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - - final mediaType = file.title?.mediaType; - final isImage = mediaType?.type == AttachmentType.image; - final isVideo = mediaType?.type == AttachmentType.video; - if (isImage || isVideo) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - thumbnail = Container( - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(8), - ), - ), - child: thumbnail, - ); - } - - return thumbnail; - } - - return { - AttachmentType.image: _createMediaThumbnail, - AttachmentType.giphy: _createMediaThumbnail, - AttachmentType.video: _createMediaThumbnail, - AttachmentType.urlPreview: _createUrlThumbnail, - AttachmentType.file: _createFileThumbnail, - AttachmentType.voiceRecording: _createFileThumbnail, - }; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 99700ad989..3603560785 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -364,7 +364,6 @@ class StreamMessageInputState extends State late final CurvedAnimation _pickerAnimation; late StreamChatThemeData _streamChatTheme; - late StreamMessageInputThemeData _messageInputTheme; bool get _isEditing => !_effectiveController.message.state.isInitial; @@ -495,7 +494,6 @@ class StreamMessageInputState extends State @override void didChangeDependencies() { _streamChatTheme = StreamChatTheme.of(context); - _messageInputTheme = StreamMessageInputTheme.of(context); super.didChangeDependencies(); } @@ -587,7 +585,7 @@ class StreamMessageInputState extends State }; final spacing = context.streamSpacing; - final safeAreaEnabled = widget.enableSafeArea ?? _messageInputTheme.enableSafeArea ?? true; + final safeAreaEnabled = widget.enableSafeArea ?? true; final viewPadding = MediaQuery.paddingOf(context); return Material( @@ -745,13 +743,12 @@ class StreamMessageInputState extends State final allowedTypes = _getAllowedAttachmentPickerTypes(); - final messageInputTheme = StreamMessageInputTheme.of(context); final isWebOrDesktop = switch (CurrentPlatform.type) { PlatformType.android || PlatformType.ios => false, _ => true, }; final useSystemPicker = - widget.useSystemAttachmentPicker || (messageInputTheme.useSystemAttachmentPicker ?? false) || isWebOrDesktop; + widget.useSystemAttachmentPicker || isWebOrDesktop; final child = useSystemPicker ? systemAttachmentPickerBuilder( @@ -794,7 +791,7 @@ class StreamMessageInputState extends State padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15), child: Text( context.translations.sendMessagePermissionError, - style: _messageInputTheme.inputTextStyle, + style: context.streamTextInputTheme.style?.textStyle, ), ); } diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart deleted file mode 100644 index 1461251bb7..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a sending button. -class StreamMessageSendButton extends StatelessWidget { - /// Returns a [StreamMessageSendButton] with the given [timeOut], [isIdle], - /// [isCommandEnabled], [isEditEnabled], [idleSendButton], [activeSendButton], - /// [onSendMessage]. - const StreamMessageSendButton({ - super.key, - this.timeOut = 0, - this.isIdle = true, - this.idleSendIcon, - this.activeSendIcon, - required this.onSendMessage, - }); - - /// Time out related to slow mode. - final int timeOut; - - /// If true the button will be disabled. - final bool isIdle; - - /// The icon to display when the button is idle. - final Widget? idleSendIcon; - - /// The icon to display when the button is active. - final Widget? activeSendIcon; - - /// The callback to call when the button is pressed. - final VoidCallback onSendMessage; - - @override - Widget build(BuildContext context) { - final theme = StreamMessageInputTheme.of(context); - - final button = _buildButton(context); - return AnimatedSwitcher( - duration: theme.sendAnimationDuration!, - child: button, - ); - } - - Widget _buildButton(BuildContext context) { - if (timeOut > 0) { - return StreamCountdownButton( - key: const Key('countdown_button'), - count: timeOut, - ); - } - - final idleIcon = switch (idleSendIcon) { - final idleIcon? => idleIcon, - _ => Icon(context.streamIcons.send), - }; - - final activeIcon = switch (activeSendIcon) { - final activeIcon? => activeIcon, - _ => Icon(context.streamIcons.arrowUp), - }; - - final theme = StreamMessageInputTheme.of(context); - final icon = isIdle ? idleIcon : activeIcon; - final onPressed = isIdle ? null : onSendMessage; - return StreamMessageInputIconButton( - key: const Key('send_button'), - icon: icon, - color: theme.sendButtonColor, - disabledColor: theme.sendButtonIdleColor, - onPressed: onPressed, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart index 3662c9f382..ee6626c64d 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart @@ -325,9 +325,6 @@ class StreamMessageWidgetProps { final void Function(BuildContext context, Message message)? onBouncedErrorMessageActions; /// Called when the edit-message action is selected. - /// - /// When provided, this callback replaces the default behaviour of showing - /// the edit-message bottom sheet via [showEditMessageSheet]. final void Function(Message message)? onEditMessageTap; /// Custom attachment builders for rendering message attachments. diff --git a/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart b/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart index 5977692118..5b908cd866 100644 --- a/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart +++ b/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:stream_chat_flutter/src/ai_assistant/streaming_message_view.dart'; -import 'package:stream_chat_flutter/src/theme/message_theme.dart'; import 'package:stream_chat_flutter/src/utils/device_segmentation.dart'; +import 'package:stream_core_flutter/stream_core_flutter.dart' as core; /// {@template streamMarkdownMessage} /// A widget that displays a markdown message. This widget uses the markdown @@ -36,7 +36,7 @@ class StreamMarkdownMessage extends StatelessWidget { final MarkdownTapLinkCallback? onTapLink; /// The theme to apply to the message text. - final StreamMessageThemeData? messageTheme; + final core.StreamMessageStyle? messageTheme; /// Optional style sheet to customize the markdown output. /// @@ -91,17 +91,13 @@ class StreamMarkdownMessage extends StatelessWidget { MarkdownStyleSheet.fromTheme( themeData.copyWith( textTheme: themeData.textTheme.apply( - bodyColor: messageTheme?.messageTextStyle?.color, - decoration: messageTheme?.messageTextStyle?.decoration, - decorationColor: messageTheme?.messageTextStyle?.decorationColor, - decorationStyle: messageTheme?.messageTextStyle?.decorationStyle, - fontFamily: messageTheme?.messageTextStyle?.fontFamily, + bodyColor: messageTheme?.textColor, ), ), ) .copyWith( - a: messageTheme?.messageLinksStyle, - p: messageTheme?.messageTextStyle, + a: TextStyle(color: messageTheme?.textLinkColor), + p: TextStyle(color: messageTheme?.textColor), ) .merge(styleSheet), ); diff --git a/packages/stream_chat_flutter/lib/src/reactions/desktop_reactions_builder.dart b/packages/stream_chat_flutter/lib/src/reactions/desktop_reactions_builder.dart deleted file mode 100644 index 7a00738cb0..0000000000 --- a/packages/stream_chat_flutter/lib/src/reactions/desktop_reactions_builder.dart +++ /dev/null @@ -1,231 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template desktopReactionsBuilder} -/// Builds a list of reactions to a message on desktop & web. -/// -/// Not intended for use outside of [MessageWidgetContent]. -/// {@endtemplate} -class DesktopReactionsBuilder extends StatefulWidget { - /// {@macro desktopReactionsBuilder} - const DesktopReactionsBuilder({ - super.key, - required this.message, - required this.messageTheme, - this.onHover, - this.borderSide, - required this.reverse, - }); - - /// The message to show reactions for. - final Message message; - - /// The theme to use for the reactions. - /// - /// [StreamMessageThemeData] is used because the design spec for desktop - /// reactions matches the design spec for messages. - final StreamMessageThemeData messageTheme; - - /// Callback to run when the mouse enters or exits the reactions. - final OnReactionsHover? onHover; - - /// {@macro borderSide} - final BorderSide? borderSide; - - /// {@macro reverse} - final bool reverse; - - @override - State createState() => _DesktopReactionsBuilderState(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add( - DiagnosticsProperty('message', message), - ); - properties.add( - DiagnosticsProperty( - 'messageTheme', - messageTheme, - ), - ); - properties.add( - DiagnosticsProperty('borderSide', borderSide), - ); - properties.add(DiagnosticsProperty('reverse', reverse)); - } -} - -class _DesktopReactionsBuilderState extends State { - bool _showReactionsPopup = false; - - @override - Widget build(BuildContext context) { - final streamChat = StreamChat.of(context); - final currentUser = streamChat.currentUser!; - final config = StreamChatConfiguration.of(context); - final resolver = config.reactionIconResolver; - final streamChatTheme = StreamChatTheme.of(context); - - final reactionsMap = {}; - widget.message.latestReactions?.forEach((element) { - if (!reactionsMap.containsKey(element.type) || element.user!.id == currentUser.id) { - reactionsMap[element.type] = element; - } - }); - - final reactionsList = reactionsMap.values.toList()..sort((a, b) => a.user!.id == currentUser.id ? 1 : -1); - - return PortalTarget( - visible: _showReactionsPopup, - portalCandidateLabels: const [kPortalMessageListViewLabel], - anchor: Aligned( - target: widget.reverse ? Alignment.topRight : Alignment.topLeft, - follower: widget.reverse ? Alignment.bottomRight : Alignment.bottomLeft, - shiftToWithinBound: const AxisFlag(y: true), - ), - portalFollower: MouseRegion( - onEnter: (_) => _onReactionsHover(true), - onExit: (_) => _onReactionsHover(false), - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 336, - maxHeight: 342, - ), - child: StreamUserReactions(message: widget.message), - ), - ), - child: MouseRegion( - cursor: SystemMouseCursors.click, - onEnter: (_) => _onReactionsHover(true), - onExit: (_) => _onReactionsHover(false), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: 2, - horizontal: widget.reverse ? 0 : 4, - ), - child: Wrap( - spacing: 4, - runSpacing: 4, - children: [ - ...reactionsList.map((reaction) { - return _BottomReaction( - currentUser: currentUser, - reaction: reaction, - message: widget.message, - borderSide: widget.borderSide, - messageTheme: widget.messageTheme, - resolver: resolver, - streamChatTheme: streamChatTheme, - ); - }).toList(), - ], - ), - ), - ), - ); - } - - void _onReactionsHover(bool isHovering) { - if (widget.onHover != null) { - return widget.onHover!(isHovering); - } - - setState(() => _showReactionsPopup = isHovering); - } -} - -class _BottomReaction extends StatelessWidget { - const _BottomReaction({ - required this.currentUser, - required this.reaction, - required this.message, - required this.borderSide, - required this.messageTheme, - required this.resolver, - required this.streamChatTheme, - }); - - final User currentUser; - final Reaction reaction; - final Message message; - final BorderSide? borderSide; - final StreamMessageThemeData? messageTheme; - final ReactionIconResolver resolver; - final StreamChatThemeData streamChatTheme; - - @override - Widget build(BuildContext context) { - final userId = currentUser.id; - - final backgroundColor = messageTheme?.reactionsBackgroundColor; - - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (reaction.userId == userId) { - StreamChannel.of(context).channel.deleteReaction( - message, - reaction, - ); - } else { - StreamChannel.of(context).channel.sendReaction( - message, - Reaction( - type: reaction.type, - emojiCode: resolver.emojiCode(reaction.type), - ), - enforceUnique: StreamChatConfiguration.of(context).enforceUniqueReactions, - ); - } - }, - child: Card( - margin: EdgeInsets.zero, - // Setting elevation as null when background color is transparent. - // This is done to avoid shadow when background color is transparent. - elevation: backgroundColor == Colors.transparent ? 0 : null, - shape: RoundedRectangleBorder( - side: - borderSide ?? - BorderSide( - color: messageTheme?.reactionsBorderColor ?? Colors.transparent, - ), - borderRadius: BorderRadius.circular(10), - ), - color: backgroundColor, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - StreamEmoji( - size: StreamEmojiSize.sm, - emoji: resolver.resolve(reaction.type), - ), - const SizedBox(width: 4), - Text( - '${reaction.score}', - style: const TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('reaction', reaction)); - properties.add(DiagnosticsProperty('message', message)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart b/packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart deleted file mode 100644 index f5165d57e6..0000000000 --- a/packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/components/avatar/stream_user_avatar.dart'; -import 'package:stream_chat_flutter/src/misc/empty_widget.dart'; -import 'package:stream_chat_flutter/src/stream_chat_configuration.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'; - -/// {@template streamUserReactions} -/// A widget that displays the reactions of a user to a message. -/// -/// This widget is typically used in a modal or a dedicated section -/// to show all reactions made by users on a specific message. -/// {@endtemplate} -class StreamUserReactions extends StatelessWidget { - /// {@macro streamUserReactions} - const StreamUserReactions({ - super.key, - required this.message, - this.onUserAvatarTap, - }); - - /// Message to display reactions of. - final Message message; - - /// {@macro onUserAvatarTap} - final ValueSetter? onUserAvatarTap; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final textTheme = theme.textTheme; - final colorTheme = theme.colorTheme; - - return Material( - color: colorTheme.barsBg, - clipBehavior: Clip.antiAlias, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - context.translations.messageReactionsLabel, - style: textTheme.headlineBold, - ), - const SizedBox(height: 16), - Flexible( - child: SingleChildScrollView( - child: Wrap( - spacing: 16, - runSpacing: 16, - alignment: WrapAlignment.center, - runAlignment: WrapAlignment.center, - children: [ - ...?message.latestReactions?.map((reaction) { - return _UserReactionItem( - key: Key('${reaction.userId}-${reaction.type}'), - reaction: reaction, - onTap: onUserAvatarTap, - ); - }), - ], - ), - ), - ), - ], - ), - ), - ); - } -} - -class _UserReactionItem extends StatelessWidget { - const _UserReactionItem({ - super.key, - required this.reaction, - this.onTap, - }); - - final Reaction reaction; - - /// {@macro onUserAvatarTap} - final ValueSetter? onTap; - - @override - Widget build(BuildContext context) { - final reactionUser = reaction.user; - if (reactionUser == null) return const Empty(); - - final currentUser = StreamChatCore.of(context).currentUser; - final isCurrentUserReaction = reactionUser.id == currentUser?.id; - - final theme = StreamChatTheme.of(context); - final messageTheme = theme.getMessageTheme(reverse: isCurrentUserReaction); - - final resolver = StreamChatConfiguration.of(context).reactionIconResolver; - - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Stack( - clipBehavior: Clip.none, - children: [ - GestureDetector( - onTap: switch (onTap) { - final onTap? => () => onTap(reactionUser), - _ => null, - }, - child: StreamUserAvatar( - size: .xl, - user: reactionUser, - showOnlineIndicator: false, - ), - ), - PositionedDirectional( - bottom: 8, - end: isCurrentUserReaction ? null : 0, - start: isCurrentUserReaction ? 0 : null, - child: Container( - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: messageTheme.reactionsMaskColor, - borderRadius: const BorderRadius.all(Radius.circular(26)), - ), - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: messageTheme.reactionsBackgroundColor, - border: Border.all( - color: messageTheme.reactionsBorderColor ?? Colors.transparent, - ), - borderRadius: const BorderRadius.all(Radius.circular(24)), - ), - child: StreamEmoji( - size: StreamEmojiSize.sm, - emoji: resolver.resolve(reaction.type), - ), - ), - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - reactionUser.name.split(' ')[0], - style: theme.textTheme.footnoteBold, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart deleted file mode 100644 index 728ebd2cfb..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart +++ /dev/null @@ -1,295 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageInputTheme} -/// Overrides the default style of [MessageInput] descendants. -/// -/// See also: -/// -/// * [StreamMessageInputThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamMessageInputTheme extends InheritedTheme { - /// Creates a [StreamMessageInputTheme]. - /// - /// The [data] parameter must not be null. - const StreamMessageInputTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamMessageInputThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamMessageInputTheme] widget, then - /// [StreamChatThemeData.messageInputTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// final theme = MessageInputTheme.of(context); - /// ``` - static StreamMessageInputThemeData of(BuildContext context) { - final messageInputTheme = context.dependOnInheritedWidgetOfExactType(); - return messageInputTheme?.data ?? StreamChatTheme.of(context).messageInputTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => StreamMessageInputTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamMessageInputTheme oldWidget) => data != oldWidget.data; -} - -/// {@template messageInputThemeData} -/// A style that overrides the default appearance of [MessageInput] widgets -/// when used with [StreamMessageInputTheme] -/// or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.messageInputTheme]. -/// {@endtemplate} -class StreamMessageInputThemeData with Diagnosticable { - /// Creates a [StreamMessageInputThemeData]. - const StreamMessageInputThemeData({ - this.sendAnimationDuration, - this.actionButtonColor, - this.sendButtonColor, - this.actionButtonIdleColor, - this.sendButtonIdleColor, - this.inputBackgroundColor, - this.inputTextStyle, - this.inputDecoration, - this.activeBorderGradient, - this.idleBorderGradient, - this.borderRadius, - this.expandButtonColor, - this.linkHighlightColor, - this.enableSafeArea, - this.elevation, - this.shadow, - this.useSystemAttachmentPicker, - }); - - /// Duration of the [MessageInput] send button animation - final Duration? sendAnimationDuration; - - /// Background color of [MessageInput] send button - final Color? sendButtonColor; - - /// Color of a link - final Color? linkHighlightColor; - - /// Background color of [MessageInput] action buttons - final Color? actionButtonColor; - - /// Background color of [MessageInput] send button - final Color? sendButtonIdleColor; - - /// Background color of [MessageInput] action buttons - final Color? actionButtonIdleColor; - - /// Background color of [MessageInput] expand button - final Color? expandButtonColor; - - /// Background color of [MessageInput] - final Color? inputBackgroundColor; - - /// TextStyle of [MessageInput] - final TextStyle? inputTextStyle; - - /// InputDecoration of [MessageInput] - final InputDecoration? inputDecoration; - - /// Border gradient when the [MessageInput] is not focused - final Gradient? idleBorderGradient; - - /// Border gradient when the [MessageInput] is focused - final Gradient? activeBorderGradient; - - /// Border radius of [MessageInput] - final BorderRadius? borderRadius; - - /// Wrap [MessageInput] with a [SafeArea widget] - final bool? enableSafeArea; - - /// Elevation of the [MessageInput] - final double? elevation; - - /// Shadow for the [MessageInput] widget - final BoxShadow? shadow; - - /// If True, allows you to use the system’s default media picker instead of - /// the custom media picker provided by the library. This can be beneficial - /// for several reasons: - /// - /// 1. Consistency: Provides a consistent user experience by using the - /// familiar system media picker. - /// 2. Permissions: Reduces the need for additional permissions, as the system - /// media picker handles permissions internally. - /// 3. Simplicity: Simplifies the implementation by leveraging the built-in - /// functionality of the system media picker. - final bool? useSystemAttachmentPicker; - - /// Returns a new [StreamMessageInputThemeData] - /// replacing some of its properties - StreamMessageInputThemeData copyWith({ - Duration? sendAnimationDuration, - Color? inputBackgroundColor, - Color? actionButtonColor, - Color? sendButtonColor, - Color? actionButtonIdleColor, - Color? linkHighlightColor, - Color? sendButtonIdleColor, - Color? expandButtonColor, - TextStyle? inputTextStyle, - InputDecoration? inputDecoration, - Gradient? activeBorderGradient, - Gradient? idleBorderGradient, - BorderRadius? borderRadius, - bool? enableSafeArea, - double? elevation, - BoxShadow? shadow, - bool? useSystemAttachmentPicker, - }) { - return StreamMessageInputThemeData( - sendAnimationDuration: sendAnimationDuration ?? this.sendAnimationDuration, - inputBackgroundColor: inputBackgroundColor ?? this.inputBackgroundColor, - actionButtonColor: actionButtonColor ?? this.actionButtonColor, - sendButtonColor: sendButtonColor ?? this.sendButtonColor, - actionButtonIdleColor: actionButtonIdleColor ?? this.actionButtonIdleColor, - linkHighlightColor: linkHighlightColor ?? this.linkHighlightColor, - expandButtonColor: expandButtonColor ?? this.expandButtonColor, - inputTextStyle: inputTextStyle ?? this.inputTextStyle, - sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor, - inputDecoration: inputDecoration ?? this.inputDecoration, - activeBorderGradient: activeBorderGradient ?? this.activeBorderGradient, - idleBorderGradient: idleBorderGradient ?? this.idleBorderGradient, - borderRadius: borderRadius ?? this.borderRadius, - enableSafeArea: enableSafeArea ?? this.enableSafeArea, - elevation: elevation ?? this.elevation, - shadow: shadow ?? this.shadow, - useSystemAttachmentPicker: useSystemAttachmentPicker ?? this.useSystemAttachmentPicker, - ); - } - - /// Linearly interpolate from one [StreamMessageInputThemeData] to another. - StreamMessageInputThemeData lerp( - StreamMessageInputThemeData a, - StreamMessageInputThemeData b, - double t, - ) { - return StreamMessageInputThemeData( - actionButtonColor: Color.lerp(a.actionButtonColor, b.actionButtonColor, t), - actionButtonIdleColor: Color.lerp(a.actionButtonIdleColor, b.actionButtonIdleColor, t), - activeBorderGradient: Gradient.lerp(a.activeBorderGradient, b.activeBorderGradient, t), - borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t), - expandButtonColor: Color.lerp(a.expandButtonColor, b.expandButtonColor, t), - linkHighlightColor: Color.lerp(a.linkHighlightColor, b.linkHighlightColor, t), - idleBorderGradient: Gradient.lerp(a.idleBorderGradient, b.idleBorderGradient, t), - inputBackgroundColor: Color.lerp(a.inputBackgroundColor, b.inputBackgroundColor, t), - inputTextStyle: TextStyle.lerp(a.inputTextStyle, b.inputTextStyle, t), - sendButtonColor: Color.lerp(a.sendButtonColor, b.sendButtonColor, t), - sendButtonIdleColor: Color.lerp(a.sendButtonIdleColor, b.sendButtonIdleColor, t), - sendAnimationDuration: a.sendAnimationDuration, - inputDecoration: a.inputDecoration, - enableSafeArea: a.enableSafeArea, - elevation: lerpDouble(a.elevation, b.elevation, t), - shadow: BoxShadow.lerp(a.shadow, b.shadow, t), - useSystemAttachmentPicker: b.useSystemAttachmentPicker, - ); - } - - /// Merges [this] [StreamMessageInputThemeData] with the [other] - StreamMessageInputThemeData merge(StreamMessageInputThemeData? other) { - if (other == null) return this; - return copyWith( - sendAnimationDuration: other.sendAnimationDuration, - inputBackgroundColor: other.inputBackgroundColor, - actionButtonColor: other.actionButtonColor, - actionButtonIdleColor: other.actionButtonIdleColor, - sendButtonColor: other.sendButtonColor, - sendButtonIdleColor: other.sendButtonIdleColor, - inputTextStyle: inputTextStyle?.merge(other.inputTextStyle) ?? other.inputTextStyle, - inputDecoration: inputDecoration?.merge(other.inputDecoration) ?? other.inputDecoration, - activeBorderGradient: other.activeBorderGradient, - idleBorderGradient: other.idleBorderGradient, - borderRadius: other.borderRadius, - expandButtonColor: other.expandButtonColor, - linkHighlightColor: other.linkHighlightColor, - enableSafeArea: other.enableSafeArea, - elevation: other.elevation, - shadow: other.shadow, - useSystemAttachmentPicker: other.useSystemAttachmentPicker, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamMessageInputThemeData && - runtimeType == other.runtimeType && - sendAnimationDuration == other.sendAnimationDuration && - sendButtonColor == other.sendButtonColor && - actionButtonColor == other.actionButtonColor && - sendButtonIdleColor == other.sendButtonIdleColor && - actionButtonIdleColor == other.actionButtonIdleColor && - expandButtonColor == other.expandButtonColor && - inputBackgroundColor == other.inputBackgroundColor && - inputTextStyle == other.inputTextStyle && - inputDecoration == other.inputDecoration && - idleBorderGradient == other.idleBorderGradient && - activeBorderGradient == other.activeBorderGradient && - borderRadius == other.borderRadius && - linkHighlightColor == other.linkHighlightColor && - enableSafeArea == other.enableSafeArea && - elevation == other.elevation && - shadow == other.shadow && - useSystemAttachmentPicker == other.useSystemAttachmentPicker; - - @override - int get hashCode => - sendAnimationDuration.hashCode ^ - sendButtonColor.hashCode ^ - actionButtonColor.hashCode ^ - sendButtonIdleColor.hashCode ^ - actionButtonIdleColor.hashCode ^ - expandButtonColor.hashCode ^ - inputBackgroundColor.hashCode ^ - inputTextStyle.hashCode ^ - inputDecoration.hashCode ^ - idleBorderGradient.hashCode ^ - activeBorderGradient.hashCode ^ - borderRadius.hashCode ^ - linkHighlightColor.hashCode ^ - elevation.hashCode ^ - shadow.hashCode ^ - enableSafeArea.hashCode ^ - useSystemAttachmentPicker.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('sendAnimationDuration', sendAnimationDuration)) - ..add(ColorProperty('inputBackgroundColor', inputBackgroundColor)) - ..add(ColorProperty('actionButtonColor', actionButtonColor)) - ..add(ColorProperty('actionButtonIdleColor', actionButtonIdleColor)) - ..add(ColorProperty('sendButtonColor', sendButtonColor)) - ..add(ColorProperty('sendButtonIdleColor', sendButtonIdleColor)) - ..add(DiagnosticsProperty('inputTextStyle', inputTextStyle)) - ..add(DiagnosticsProperty('inputDecoration', inputDecoration)) - ..add(DiagnosticsProperty('activeBorderGradient', activeBorderGradient)) - ..add(DiagnosticsProperty('idleBorderGradient', idleBorderGradient)) - ..add(DiagnosticsProperty('borderRadius', borderRadius)) - ..add(ColorProperty('expandButtonColor', expandButtonColor)) - ..add(ColorProperty('linkHighlightColor', linkHighlightColor)) - ..add(DiagnosticsProperty('elevation', elevation)) - ..add(DiagnosticsProperty('shadow', shadow)) - ..add(DiagnosticsProperty('enableSafeArea', enableSafeArea)) - ..add(DiagnosticsProperty('useSystemAttachmentPicker', useSystemAttachmentPicker)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart deleted file mode 100644 index 1ad66765e0..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart +++ /dev/null @@ -1,345 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; -import 'package:stream_chat_flutter/src/utils/date_formatter.dart'; - -/// {@template message_theme_data} -/// Class for getting message theme -/// {@endtemplate} -// ignore: prefer-match-file-name -class StreamMessageThemeData with Diagnosticable { - /// Creates a [StreamMessageThemeData]. - const StreamMessageThemeData({ - this.repliesStyle, - this.messageTextStyle, - this.messageAuthorStyle, - this.messageLinksStyle, - this.messageDeletedStyle, - this.messageBackgroundColor, - this.messageBackgroundGradient, - this.messageBorderColor, - this.reactionsBackgroundColor, - this.reactionsBorderColor, - this.reactionsMaskColor, - this.avatarTheme, - this.createdAtStyle, - this.createdAtFormatter, - this.urlAttachmentBackgroundColor, - this.urlAttachmentHostStyle, - this.urlAttachmentTitleStyle, - this.urlAttachmentTextStyle, - this.urlAttachmentTitleMaxLine, - this.urlAttachmentTextMaxLine, - }); - - /// Text style for message text - final TextStyle? messageTextStyle; - - /// Text style for message author - final TextStyle? messageAuthorStyle; - - /// Text style for message links - final TextStyle? messageLinksStyle; - - /// Text style for created at text - final TextStyle? createdAtStyle; - - /// Formatter for the created at timestamp. - /// - /// If null, uses the default date formatting. - /// - /// Example: - /// ```dart - /// StreamMessageThemeData( - /// createdAtStyle: TextStyle(...), - /// createdAtFormatter: (context, date) { - /// return Jiffy.parseFromDateTime(date).jm; // "2:30 PM" - /// }, - /// ) - /// ``` - final DateFormatter? createdAtFormatter; - - /// Text style for the text on a deleted message - /// If not set [messageTextStyle] is used with [FontStyle.italic] and - /// [createdAtStyle.color]. - final TextStyle? messageDeletedStyle; - - /// Text style for replies - final TextStyle? repliesStyle; - - /// Color for messageBackgroundColor - final Color? messageBackgroundColor; - - /// Gradient for message background. - /// - /// Note: If this is set, it will override [messageBackgroundColor]. - final Gradient? messageBackgroundGradient; - - /// Color for message border color - final Color? messageBorderColor; - - /// Color for reactions - final Color? reactionsBackgroundColor; - - /// Colors reaction border - final Color? reactionsBorderColor; - - /// Color for reaction mask - final Color? reactionsMaskColor; - - /// Theme of the avatar - final StreamAvatarThemeData? avatarTheme; - - /// Background color for messages with url attachments. - final Color? urlAttachmentBackgroundColor; - - /// Color for url attachment host. - final TextStyle? urlAttachmentHostStyle; - - /// Color for url attachment title. - final TextStyle? urlAttachmentTitleStyle; - - /// Color for url attachment text. - final TextStyle? urlAttachmentTextStyle; - - /// Max number of lines in Url link title. - final int? urlAttachmentTitleMaxLine; - - /// Max number of lines in Url link text. - final int? urlAttachmentTextMaxLine; - - /// Copy with a theme - StreamMessageThemeData copyWith({ - TextStyle? messageTextStyle, - TextStyle? messageAuthorStyle, - TextStyle? messageLinksStyle, - TextStyle? messageDeletedStyle, - TextStyle? createdAtStyle, - DateFormatter? createdAtFormatter, - TextStyle? repliesStyle, - Color? messageBackgroundColor, - Gradient? messageBackgroundGradient, - Color? messageBorderColor, - StreamAvatarThemeData? avatarTheme, - Color? reactionsBackgroundColor, - Color? reactionsBorderColor, - Color? reactionsMaskColor, - Color? urlAttachmentBackgroundColor, - TextStyle? urlAttachmentHostStyle, - TextStyle? urlAttachmentTitleStyle, - TextStyle? urlAttachmentTextStyle, - int? urlAttachmentTitleMaxLine, - int? urlAttachmentTextMaxLine, - }) { - return StreamMessageThemeData( - messageTextStyle: messageTextStyle ?? this.messageTextStyle, - messageAuthorStyle: messageAuthorStyle ?? this.messageAuthorStyle, - messageLinksStyle: messageLinksStyle ?? this.messageLinksStyle, - createdAtStyle: createdAtStyle ?? this.createdAtStyle, - createdAtFormatter: createdAtFormatter ?? this.createdAtFormatter, - messageDeletedStyle: messageDeletedStyle ?? this.messageDeletedStyle, - messageBackgroundColor: messageBackgroundColor ?? this.messageBackgroundColor, - messageBackgroundGradient: messageBackgroundGradient ?? this.messageBackgroundGradient, - messageBorderColor: messageBorderColor ?? this.messageBorderColor, - avatarTheme: avatarTheme ?? this.avatarTheme, - repliesStyle: repliesStyle ?? this.repliesStyle, - reactionsBackgroundColor: reactionsBackgroundColor ?? this.reactionsBackgroundColor, - reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, - reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor, - urlAttachmentBackgroundColor: urlAttachmentBackgroundColor ?? this.urlAttachmentBackgroundColor, - urlAttachmentHostStyle: urlAttachmentHostStyle ?? this.urlAttachmentHostStyle, - urlAttachmentTitleStyle: urlAttachmentTitleStyle ?? this.urlAttachmentTitleStyle, - urlAttachmentTextStyle: urlAttachmentTextStyle ?? this.urlAttachmentTextStyle, - urlAttachmentTitleMaxLine: urlAttachmentTitleMaxLine ?? this.urlAttachmentTitleMaxLine, - urlAttachmentTextMaxLine: urlAttachmentTextMaxLine ?? this.urlAttachmentTextMaxLine, - ); - } - - /// Linearly interpolate from one [StreamMessageThemeData] to another. - StreamMessageThemeData lerp( - StreamMessageThemeData a, - StreamMessageThemeData b, - double t, - ) { - return StreamMessageThemeData( - avatarTheme: const StreamAvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t), - messageAuthorStyle: TextStyle.lerp(a.messageAuthorStyle, b.messageAuthorStyle, t), - createdAtStyle: TextStyle.lerp(a.createdAtStyle, b.createdAtStyle, t), - createdAtFormatter: t < 0.5 ? a.createdAtFormatter : b.createdAtFormatter, - messageDeletedStyle: TextStyle.lerp(a.messageDeletedStyle, b.messageDeletedStyle, t), - messageBackgroundColor: Color.lerp(a.messageBackgroundColor, b.messageBackgroundColor, t), - messageBackgroundGradient: t < 0.5 ? a.messageBackgroundGradient : b.messageBackgroundGradient, - messageBorderColor: Color.lerp(a.messageBorderColor, b.messageBorderColor, t), - messageLinksStyle: TextStyle.lerp(a.messageLinksStyle, b.messageLinksStyle, t), - messageTextStyle: TextStyle.lerp(a.messageTextStyle, b.messageTextStyle, t), - reactionsBackgroundColor: Color.lerp( - a.reactionsBackgroundColor, - b.reactionsBackgroundColor, - t, - ), - reactionsBorderColor: Color.lerp(a.messageBorderColor, b.reactionsBorderColor, t), - reactionsMaskColor: Color.lerp(a.reactionsMaskColor, b.reactionsMaskColor, t), - repliesStyle: TextStyle.lerp(a.repliesStyle, b.repliesStyle, t), - urlAttachmentBackgroundColor: Color.lerp( - a.urlAttachmentBackgroundColor, - b.urlAttachmentBackgroundColor, - t, - ), - urlAttachmentHostStyle: TextStyle.lerp(a.urlAttachmentHostStyle, b.urlAttachmentHostStyle, t), - urlAttachmentTextStyle: TextStyle.lerp( - a.urlAttachmentTextStyle, - b.urlAttachmentTextStyle, - t, - ), - urlAttachmentTitleStyle: TextStyle.lerp( - a.urlAttachmentTitleStyle, - b.urlAttachmentTitleStyle, - t, - ), - urlAttachmentTitleMaxLine: lerpDouble( - a.urlAttachmentTitleMaxLine, - b.urlAttachmentTitleMaxLine, - t, - )?.round(), - urlAttachmentTextMaxLine: lerpDouble( - a.urlAttachmentTextMaxLine, - b.urlAttachmentTextMaxLine, - t, - )?.round(), - ); - } - - /// Merge with a theme - StreamMessageThemeData merge(StreamMessageThemeData? other) { - if (other == null) return this; - return copyWith( - messageTextStyle: messageTextStyle?.merge(other.messageTextStyle) ?? other.messageTextStyle, - messageAuthorStyle: messageAuthorStyle?.merge(other.messageAuthorStyle) ?? other.messageAuthorStyle, - messageLinksStyle: messageLinksStyle?.merge(other.messageLinksStyle) ?? other.messageLinksStyle, - createdAtStyle: createdAtStyle?.merge(other.createdAtStyle) ?? other.createdAtStyle, - createdAtFormatter: other.createdAtFormatter ?? createdAtFormatter, - messageDeletedStyle: messageDeletedStyle?.merge(other.messageDeletedStyle) ?? other.messageDeletedStyle, - repliesStyle: repliesStyle?.merge(other.repliesStyle) ?? other.repliesStyle, - messageBackgroundColor: other.messageBackgroundColor, - messageBackgroundGradient: other.messageBackgroundGradient, - messageBorderColor: other.messageBorderColor, - avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, - reactionsBackgroundColor: other.reactionsBackgroundColor, - reactionsBorderColor: other.reactionsBorderColor, - reactionsMaskColor: other.reactionsMaskColor, - urlAttachmentBackgroundColor: other.urlAttachmentBackgroundColor, - urlAttachmentHostStyle: other.urlAttachmentHostStyle, - urlAttachmentTitleStyle: other.urlAttachmentTitleStyle, - urlAttachmentTextStyle: other.urlAttachmentTextStyle, - urlAttachmentTitleMaxLine: other.urlAttachmentTitleMaxLine, - urlAttachmentTextMaxLine: other.urlAttachmentTextMaxLine, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamMessageThemeData && - runtimeType == other.runtimeType && - messageTextStyle == other.messageTextStyle && - messageAuthorStyle == other.messageAuthorStyle && - messageLinksStyle == other.messageLinksStyle && - createdAtStyle == other.createdAtStyle && - createdAtFormatter == other.createdAtFormatter && - messageDeletedStyle == other.messageDeletedStyle && - repliesStyle == other.repliesStyle && - messageBackgroundColor == other.messageBackgroundColor && - messageBackgroundGradient == other.messageBackgroundGradient && - messageBorderColor == other.messageBorderColor && - reactionsBackgroundColor == other.reactionsBackgroundColor && - reactionsBorderColor == other.reactionsBorderColor && - reactionsMaskColor == other.reactionsMaskColor && - avatarTheme == other.avatarTheme && - urlAttachmentBackgroundColor == other.urlAttachmentBackgroundColor && - urlAttachmentHostStyle == other.urlAttachmentHostStyle && - urlAttachmentTitleStyle == other.urlAttachmentTitleStyle && - urlAttachmentTextStyle == other.urlAttachmentTextStyle && - urlAttachmentTitleMaxLine == other.urlAttachmentTitleMaxLine && - urlAttachmentTextMaxLine == other.urlAttachmentTextMaxLine; - - @override - int get hashCode => - messageTextStyle.hashCode ^ - messageAuthorStyle.hashCode ^ - messageLinksStyle.hashCode ^ - createdAtStyle.hashCode ^ - createdAtFormatter.hashCode ^ - messageDeletedStyle.hashCode ^ - repliesStyle.hashCode ^ - messageBackgroundColor.hashCode ^ - messageBackgroundGradient.hashCode ^ - messageBorderColor.hashCode ^ - reactionsBackgroundColor.hashCode ^ - reactionsBorderColor.hashCode ^ - reactionsMaskColor.hashCode ^ - avatarTheme.hashCode ^ - urlAttachmentBackgroundColor.hashCode ^ - urlAttachmentHostStyle.hashCode ^ - urlAttachmentTitleStyle.hashCode ^ - urlAttachmentTextStyle.hashCode ^ - urlAttachmentTitleMaxLine.hashCode ^ - urlAttachmentTextMaxLine.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('messageTextStyle', messageTextStyle)) - ..add(DiagnosticsProperty('messageAuthorStyle', messageAuthorStyle)) - ..add(DiagnosticsProperty('messageLinksStyle', messageLinksStyle)) - ..add(DiagnosticsProperty('createdAtStyle', createdAtStyle)) - ..add(DiagnosticsProperty('createdAtFormatter', createdAtFormatter)) - ..add(DiagnosticsProperty('messageDeletedStyle', messageDeletedStyle)) - ..add(DiagnosticsProperty('repliesStyle', repliesStyle)) - ..add(ColorProperty('messageBackgroundColor', messageBackgroundColor)) - ..add(DiagnosticsProperty('messageBackgroundGradient', messageBackgroundGradient)) - ..add(ColorProperty('messageBorderColor', messageBorderColor)) - ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) - ..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor)) - ..add(ColorProperty('reactionsBorderColor', reactionsBorderColor)) - ..add(ColorProperty('reactionsMaskColor', reactionsMaskColor)) - ..add( - ColorProperty( - 'urlAttachmentBackgroundColor', - urlAttachmentBackgroundColor, - ), - ) - ..add( - DiagnosticsProperty( - 'urlAttachmentHostStyle', - urlAttachmentHostStyle, - ), - ) - ..add( - DiagnosticsProperty( - 'urlAttachmentTitleStyle', - urlAttachmentTitleStyle, - ), - ) - ..add( - DiagnosticsProperty( - 'urlAttachmentTextStyle', - urlAttachmentTextStyle, - ), - ) - ..add( - DiagnosticsProperty( - 'urlAttachmentTitleMaxLine', - urlAttachmentTitleMaxLine, - ), - ) - ..add( - DiagnosticsProperty( - 'urlAttachmentTextMaxLine', - urlAttachmentTextMaxLine, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart index 45dea27c00..8ae19cb526 100644 --- a/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart @@ -45,9 +45,6 @@ class StreamChatThemeData { StreamChannelListHeaderThemeData? channelListHeaderTheme, StreamChannelPreviewThemeData? channelPreviewTheme, StreamChannelHeaderThemeData? channelHeaderTheme, - StreamMessageThemeData? otherMessageTheme, - StreamMessageThemeData? ownMessageTheme, - StreamMessageInputThemeData? messageInputTheme, Widget Function(BuildContext, User)? defaultUserImage, PlaceholderUserImage? placeholderUserImage, IconThemeData? primaryIconTheme, @@ -78,9 +75,6 @@ class StreamChatThemeData { channelListHeaderTheme: channelListHeaderTheme, channelPreviewTheme: channelPreviewTheme, channelHeaderTheme: channelHeaderTheme, - otherMessageTheme: otherMessageTheme, - ownMessageTheme: ownMessageTheme, - messageInputTheme: messageInputTheme, defaultUserImage: defaultUserImage, placeholderUserImage: placeholderUserImage, primaryIconTheme: primaryIconTheme, @@ -115,9 +109,6 @@ class StreamChatThemeData { required this.channelListHeaderTheme, required this.channelPreviewTheme, required this.channelHeaderTheme, - required this.otherMessageTheme, - required this.ownMessageTheme, - required this.messageInputTheme, required this.primaryIconTheme, required this.galleryHeaderTheme, required this.galleryFooterTheme, @@ -151,7 +142,6 @@ class StreamChatThemeData { StreamColorTheme colorTheme, StreamTextTheme textTheme, ) { - final accentColor = colorTheme.accentPrimary; final iconTheme = IconThemeData(color: colorTheme.textLowEmphasis); final channelHeaderTheme = StreamChannelHeaderThemeData( avatarTheme: StreamAvatarThemeData( @@ -200,87 +190,6 @@ class StreamChatThemeData { titleStyle: textTheme.headlineBold, ), channelHeaderTheme: channelHeaderTheme, - ownMessageTheme: StreamMessageThemeData( - messageAuthorStyle: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - messageTextStyle: textTheme.body, - messageDeletedStyle: textTheme.body.copyWith( - color: colorTheme.textLowEmphasis, - fontStyle: FontStyle.italic, - ), - createdAtStyle: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - repliesStyle: textTheme.footnoteBold.copyWith(color: accentColor), - messageBackgroundColor: colorTheme.inputBg, - reactionsBackgroundColor: colorTheme.barsBg, - reactionsBorderColor: colorTheme.borders, - reactionsMaskColor: colorTheme.appBg, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - messageLinksStyle: TextStyle(color: accentColor), - urlAttachmentBackgroundColor: colorTheme.linkBg, - urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor), - urlAttachmentTitleStyle: textTheme.footnoteBold, - urlAttachmentTextStyle: textTheme.footnote, - urlAttachmentTitleMaxLine: 1, - urlAttachmentTextMaxLine: 3, - ), - otherMessageTheme: StreamMessageThemeData( - reactionsBackgroundColor: colorTheme.borders, - reactionsBorderColor: colorTheme.borders, - reactionsMaskColor: colorTheme.appBg, - messageTextStyle: textTheme.body, - messageDeletedStyle: textTheme.body.copyWith( - color: colorTheme.textLowEmphasis, - fontStyle: FontStyle.italic, - ), - createdAtStyle: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - messageAuthorStyle: textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - repliesStyle: textTheme.footnoteBold.copyWith(color: accentColor), - messageLinksStyle: TextStyle(color: accentColor), - messageBackgroundColor: colorTheme.barsBg, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - urlAttachmentBackgroundColor: colorTheme.linkBg, - urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor), - urlAttachmentTitleStyle: textTheme.footnoteBold, - urlAttachmentTextStyle: textTheme.footnote, - urlAttachmentTitleMaxLine: 1, - urlAttachmentTextMaxLine: 3, - ), - messageInputTheme: StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - actionButtonColor: colorTheme.accentPrimary, - actionButtonIdleColor: colorTheme.textLowEmphasis, - expandButtonColor: colorTheme.accentPrimary, - sendButtonColor: colorTheme.accentPrimary, - sendButtonIdleColor: colorTheme.disabled, - inputBackgroundColor: colorTheme.barsBg, - inputTextStyle: textTheme.body, - linkHighlightColor: colorTheme.accentPrimary, - idleBorderGradient: LinearGradient( - colors: [ - colorTheme.borders, - colorTheme.borders, - ], - ), - activeBorderGradient: LinearGradient( - colors: [ - colorTheme.borders, - colorTheme.borders, - ], - ), - useSystemAttachmentPicker: false, - ), galleryHeaderTheme: StreamGalleryHeaderThemeData( closeButtonColor: colorTheme.textHighEmphasis, backgroundColor: channelHeaderTheme.color, @@ -446,15 +355,6 @@ class StreamChatThemeData { /// [StreamChatTheme]. final StreamGalleryFooterThemeData galleryFooterTheme; - /// Theme of the current user messages - final StreamMessageThemeData ownMessageTheme; - - /// Theme of other users messages - final StreamMessageThemeData otherMessageTheme; - - /// Theme dedicated to the [StreamMessageInput] widget - final StreamMessageInputThemeData messageInputTheme; - /// Primary icon theme final IconThemeData primaryIconTheme; @@ -491,15 +391,6 @@ class StreamChatThemeData { /// Theme configuration for the [StreamDraftListTile] widget. final StreamDraftListTileThemeData draftListTileTheme; - /// Returns the theme for the message based on the [reverse] parameter. - /// - /// If [reverse] is true, it returns the [otherMessageTheme], otherwise it - /// returns the [ownMessageTheme]. - StreamMessageThemeData getMessageTheme({bool reverse = false}) { - if (reverse) return ownMessageTheme; - return otherMessageTheme; - } - /// Creates a copy of [StreamChatThemeData] with specified attributes /// overridden. StreamChatThemeData copyWith({ @@ -507,9 +398,6 @@ class StreamChatThemeData { StreamColorTheme? colorTheme, StreamChannelPreviewThemeData? channelPreviewTheme, StreamChannelHeaderThemeData? channelHeaderTheme, - StreamMessageThemeData? ownMessageTheme, - StreamMessageThemeData? otherMessageTheme, - StreamMessageInputThemeData? messageInputTheme, Widget Function(BuildContext, User)? defaultUserImage, PlaceholderUserImage? placeholderUserImage, IconThemeData? primaryIconTheme, @@ -534,9 +422,6 @@ class StreamChatThemeData { primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme), channelPreviewTheme: this.channelPreviewTheme.merge(channelPreviewTheme), channelHeaderTheme: this.channelHeaderTheme.merge(channelHeaderTheme), - ownMessageTheme: this.ownMessageTheme.merge(ownMessageTheme), - otherMessageTheme: this.otherMessageTheme.merge(otherMessageTheme), - messageInputTheme: this.messageInputTheme.merge(messageInputTheme), galleryHeaderTheme: galleryHeaderTheme ?? this.galleryHeaderTheme, galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme, messageListViewTheme: messageListViewTheme ?? this.messageListViewTheme, @@ -562,9 +447,6 @@ class StreamChatThemeData { primaryIconTheme: other.primaryIconTheme, channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme), channelHeaderTheme: channelHeaderTheme.merge(other.channelHeaderTheme), - ownMessageTheme: ownMessageTheme.merge(other.ownMessageTheme), - otherMessageTheme: otherMessageTheme.merge(other.otherMessageTheme), - messageInputTheme: messageInputTheme.merge(other.messageInputTheme), galleryHeaderTheme: galleryHeaderTheme.merge(other.galleryHeaderTheme), galleryFooterTheme: galleryFooterTheme.merge(other.galleryFooterTheme), messageListViewTheme: messageListViewTheme.merge(other.messageListViewTheme), diff --git a/packages/stream_chat_flutter/lib/src/theme/themes.dart b/packages/stream_chat_flutter/lib/src/theme/themes.dart index 7bcf5a75e6..643872e953 100644 --- a/packages/stream_chat_flutter/lib/src/theme/themes.dart +++ b/packages/stream_chat_flutter/lib/src/theme/themes.dart @@ -6,9 +6,7 @@ export 'color_theme.dart'; export 'draft_list_tile_theme.dart'; export 'gallery_footer_theme.dart'; export 'gallery_header_theme.dart'; -export 'message_input_theme.dart'; export 'message_list_view_theme.dart'; -export 'message_theme.dart'; export 'poll_comments_dialog_theme.dart'; export 'poll_creator_theme.dart'; export 'poll_interactor_theme.dart'; diff --git a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart index 537ac60440..4756e3f712 100644 --- a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart +++ b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; import 'package:stream_chat_flutter/src/message_input/command_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -184,18 +183,6 @@ typedef CommandButtonBuilder = CommandButton commandButton, ); -/// {@template actionButtonBuilder} -/// A widget builder for building a custom action button. -/// -/// [attachmentButton] is the default [AttachmentButton] configuration, -/// use [attachmentButton.copyWith] to easily customize it. -/// {@endtemplate} -typedef AttachmentButtonBuilder = - Widget Function( - BuildContext context, - AttachmentButton attachmentButton, - ); - /// {@template quotedMessageAttachmentThumbnailBuilder} /// A widget builder for building a custom quoted message attachment thumbnail. /// {@endtemplate} diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 1c4bcabdba..64e69f06ef 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -90,7 +90,6 @@ export 'src/attachment_actions_modal/attachment_actions_modal.dart'; export 'src/autocomplete/stream_autocomplete.dart'; export 'src/avatars/gradient_avatar.dart'; export 'src/bottom_sheets/attachment_modal_sheet.dart'; -export 'src/bottom_sheets/edit_message_sheet.dart'; export 'src/bottom_sheets/error_alert_sheet.dart'; export 'src/bottom_sheets/stream_channel_info_bottom_sheet.dart'; export 'src/channel/channel_header.dart'; @@ -133,10 +132,8 @@ export 'src/message_input/audio_recorder/audio_recorder_state.dart'; export 'src/message_input/audio_recorder/stream_audio_recorder.dart'; export 'src/message_input/countdown_button.dart'; export 'src/message_input/enums.dart'; -export 'src/message_input/quoted_message_widget.dart'; export 'src/message_input/stream_message_composer_attachment_list.dart'; export 'src/message_input/stream_message_input.dart'; -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'; @@ -172,7 +169,6 @@ export 'src/poll/stream_poll_options_dialog.dart'; export 'src/poll/stream_poll_results_dialog.dart'; export 'src/reactions/detail/reaction_detail_sheet.dart'; export 'src/reactions/picker/reaction_picker.dart'; -export 'src/reactions/user_reactions.dart'; export 'src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart'; export 'src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart'; export 'src/scroll_view/channel_scroll_view/stream_channel_list_item.dart'; diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart b/packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart deleted file mode 100644 index 3314bba235..0000000000 --- a/packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:record/record.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../fakes.dart'; -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final originalRecordPlatform = RecordPlatform.instance; - setUp(() => RecordPlatform.instance = FakeRecordPlatform()); - tearDown(() => RecordPlatform.instance = originalRecordPlatform); - - group('EditMessageSheet tests', () { - testWidgets('appears on tap', (tester) async { - final channel = MockChannel(); - when(channel.getRemainingCooldown).thenReturn(0); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockClient(), - connectivityStream: Stream.value([ConnectivityResult.wifi]), - child: child, - ), - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: ElevatedButton( - child: const Text('Show Modal'), - onPressed: () => showModalBottomSheet( - context: context, - builder: (_) => EditMessageSheet( - channel: channel, - message: Message(id: 'msg123', text: 'Hello World!'), - ), - ), - ), - ); - }, - ), - ), - ), - ); - - final button = find.byType(ElevatedButton); - await tester.tap(button); - await tester.pumpAndSettle(); - expect(find.byType(EditMessageSheet), findsOneWidget); - expect(find.text('Edit Message'), findsOneWidget); - expect(find.byType(StreamMessageInput), findsOneWidget); - }); - - goldenTest( - 'golden test for EditMessageSheet', - fileName: 'edit_message_sheet_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () { - final channel = MockChannel(); - when(channel.getRemainingCooldown).thenReturn(0); - - return MaterialAppWrapper( - builder: (context, child) => StreamChat( - client: MockClient(), - connectivityStream: Stream.value([ConnectivityResult.wifi]), - child: child, - ), - home: Scaffold( - bottomSheet: EditMessageSheet( - channel: channel, - message: Message(id: 'msg123', text: 'Hello World!'), - ), - ), - ); - }, - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart b/packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart deleted file mode 100644 index e5c54d2ed7..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('AttachmentButton onPressed works', (tester) async { - var count = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: AttachmentButton( - color: Colors.red, - onPressed: () { - count++; - }, - ), - ), - ), - ), - ); - - final button = find.byType(IconButton); - expect(button, findsOneWidget); - expect(find.byType(Icon), findsOneWidget); - await tester.tap(button); - expect(count, 1); - }); - - testWidgets('AttachmentButton should accept icon', (tester) async { - const icon = Icon(Icons.attachment); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: AttachmentButton( - icon: icon, - onPressed: () {}, - ), - ), - ), - ), - ); - - expect(find.byIcon(Icons.attachment), findsOneWidget); - }); - - testWidgets('AttachmentButton should accept color', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: AttachmentButton( - color: Colors.red, - onPressed: () {}, - ), - ), - ), - ), - ); - - final buttonFinder = find.byType(AttachmentButton); - expect(buttonFinder, findsOneWidget); - - final button = tester.widget(buttonFinder); - expect(button.color, Colors.red); - }); - - goldenTest( - 'golden test for AttachmentButton', - fileName: 'attachment_button_0', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Center( - child: AttachmentButton( - color: StreamChatThemeData.light().messageInputTheme.actionButtonIdleColor, - onPressed: () {}, - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/stream_message_send_button_test.dart b/packages/stream_chat_flutter/test/src/message_input/stream_message_send_button_test.dart deleted file mode 100644 index bcde15940e..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/stream_message_send_button_test.dart +++ /dev/null @@ -1,195 +0,0 @@ -// ignore_for_file: avoid_redundant_argument_values - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_send_button.dart'; -import 'package:stream_chat_flutter/src/theme/message_input_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_core_flutter/stream_core_flutter.dart'; - -void main() { - group('StreamMessageSendButton', () { - testWidgets( - 'renders countdown button when timeout > 0', - (tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageSendButton( - timeOut: 5, - onSendMessage: () {}, - ), - ), - ); - - expect(find.byKey(const Key('countdown_button')), findsOneWidget); - expect(find.byKey(const Key('send_button')), findsNothing); - }, - ); - - testWidgets( - 'renders idle send button when isIdle is true', - (tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageSendButton( - isIdle: true, - onSendMessage: () {}, - ), - ), - ); - - final button = find.byKey(const Key('send_button')); - expect(button, findsOneWidget); - - // Verify the button is disabled - final iconButton = tester.widget(button); - expect(iconButton.onPressed, isNull); - - // Verify default idle icon is shown - expect(find.byIcon(StreamIconData.send), findsOneWidget); - }, - ); - - testWidgets( - 'renders active send button when isIdle is false', - (tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageSendButton( - isIdle: false, - onSendMessage: () {}, - ), - ), - ); - - final button = find.byKey(const Key('send_button')); - expect(button, findsOneWidget); - - // Verify the button is enabled - final iconButton = tester.widget(button); - expect(iconButton.onPressed, isNotNull); - - // Verify default active icon is shown - expect(find.byIcon(StreamIconData.arrowUp), findsOneWidget); - }, - ); - - testWidgets( - 'uses custom idle button when provided', - (tester) async { - final customIdleButton = Container(key: const Key('custom_idle')); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageSendButton( - isIdle: true, - idleSendIcon: customIdleButton, - onSendMessage: () {}, - ), - ), - ); - - expect(find.byKey(const Key('custom_idle')), findsOneWidget); - expect(find.byType(Icon), findsNothing); - }, - ); - - testWidgets( - 'uses custom active button when provided', - (tester) async { - final customActiveButton = Container(key: const Key('custom_active')); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageSendButton( - isIdle: false, - activeSendIcon: customActiveButton, - onSendMessage: () {}, - ), - ), - ); - - expect(find.byKey(const Key('custom_active')), findsOneWidget); - expect(find.byType(Icon), findsNothing); - }, - ); - - testWidgets( - 'calls onSendMessage when active button is pressed', - (tester) async { - var wasPressed = false; - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageSendButton( - isIdle: false, - onSendMessage: () => wasPressed = true, - ), - ), - ); - - await tester.tap(find.byKey(const Key('send_button'))); - expect(wasPressed, isTrue); - }, - ); - - testWidgets( - 'applies theme colors correctly', - (tester) async { - const theme = StreamMessageInputThemeData( - sendButtonColor: Colors.blue, - sendButtonIdleColor: Colors.grey, - sendAnimationDuration: Duration(milliseconds: 100), - ); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamMessageInputTheme( - data: theme, - child: StreamMessageSendButton( - isIdle: false, - onSendMessage: () {}, - ), - ), - ), - ); - - final iconButton = tester.widget( - find.byKey(const Key('send_button')), - ); - - expect(iconButton.color, Colors.blue); - expect(iconButton.disabledColor, Colors.grey); - }, - ); - }); -} - -Widget _wrapWithStreamChatApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - debugShowCheckedModeBanner: false, - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder( - builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - bottomNavigationBar: Material( - elevation: 10, - color: theme.colorTheme.barsBg, - child: Padding( - padding: const EdgeInsets.all(8), - child: widget, - ), - ), - ); - }, - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_modal/message_actions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_modal/message_actions_modal_test.dart index 457cb06ad7..30352e5505 100644 --- a/packages/stream_chat_flutter/test/src/message_modal/message_actions_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_modal/message_actions_modal_test.dart @@ -149,8 +149,8 @@ void main() { Widget buildMessageWidget({bool reverse = false}) { return Builder( builder: (context) { - final theme = StreamChatTheme.of(context); - final messageTheme = theme.getMessageTheme(reverse: reverse); + final messageTheme = context.streamMessageTheme.mergeWithDefaults(context); + final messageStyle = reverse ? messageTheme.outgoing! : messageTheme.incoming!; return Container( padding: const EdgeInsets.symmetric( @@ -159,11 +159,11 @@ void main() { ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), - color: messageTheme.messageBackgroundColor, + color: messageStyle.backgroundColor, ), child: Text( message.text ?? '', - style: messageTheme.messageTextStyle, + style: TextStyle(color: messageStyle.textColor), ), ); }, diff --git a/packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart deleted file mode 100644 index 96cb7fd979..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('MessageInputThemeData copyWith, ==, hashCode basics', () { - expect(const StreamMessageInputThemeData(), const StreamMessageInputThemeData().copyWith()); - expect(const StreamMessageInputThemeData().hashCode, const StreamMessageInputThemeData().copyWith().hashCode); - }); - - group('MessageInputThemeData lerps correctly', () { - test('Lerp completely from light to dark', () { - expect( - const StreamMessageInputThemeData().lerp(_messageInputThemeControl, _messageInputThemeControlDark, 1), - _messageInputThemeControlDark, - ); - }); - - test('Lerp halfway from light to dark', () { - expect( - const StreamMessageInputThemeData().lerp( - _messageInputThemeControl, - _messageInputThemeControlDark, - 0.5, - ), - _messageInputThemeControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test('Lerp completely from dark to light', () { - expect( - const StreamMessageInputThemeData().lerp(_messageInputThemeControlDark, _messageInputThemeControl, 1), - _messageInputThemeControl, - ); - }); - }); - - test('Merging two MessageInputThemeData results in the latter', () { - expect(_messageInputThemeControl.merge(_messageInputThemeControlDark), _messageInputThemeControlDark); - }); -} - -final _messageInputThemeControl = StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - actionButtonColor: const StreamColorTheme.light().accentPrimary, - actionButtonIdleColor: const StreamColorTheme.light().textLowEmphasis, - expandButtonColor: const StreamColorTheme.light().accentPrimary, - sendButtonColor: const StreamColorTheme.light().accentPrimary, - sendButtonIdleColor: const StreamColorTheme.light().disabled, - inputBackgroundColor: const StreamColorTheme.light().barsBg, - inputTextStyle: const StreamTextTheme.light().body, - idleBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - const StreamColorTheme.light().disabled, - const StreamColorTheme.light().disabled, - ], - ), - activeBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - const StreamColorTheme.light().disabled, - const StreamColorTheme.light().disabled, - ], - ), -); - -final _messageInputThemeControlMidLerp = StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - inputBackgroundColor: const Color(0xff88898a), - actionButtonColor: const Color(0xff196eff), - actionButtonIdleColor: const Color(0xff7a7a7a), - sendButtonColor: const Color(0xff196eff), - sendButtonIdleColor: const Color(0xff848585), - expandButtonColor: const Color(0xff196eff), - inputTextStyle: const TextStyle( - color: Color(0xff7f7f7f), - fontSize: 14, - fontWeight: FontWeight.w400, - ), - idleBorderGradient: const LinearGradient( - stops: [0.0, 1.0], - colors: [ - Color(0xff848585), - Color(0xff848585), - ], - ), - activeBorderGradient: const LinearGradient( - stops: [0.0, 1.0], - colors: [ - Color(0xff848585), - Color(0xff848585), - ], - ), -); - -final _messageInputThemeControlDark = StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - actionButtonColor: const StreamColorTheme.dark().accentPrimary, - actionButtonIdleColor: const StreamColorTheme.dark().textLowEmphasis, - expandButtonColor: const StreamColorTheme.dark().accentPrimary, - sendButtonColor: const StreamColorTheme.dark().accentPrimary, - sendButtonIdleColor: const StreamColorTheme.dark().disabled, - inputBackgroundColor: const StreamColorTheme.dark().barsBg, - inputTextStyle: const StreamTextTheme.dark().body, - idleBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - const StreamColorTheme.dark().disabled, - const StreamColorTheme.dark().disabled, - ], - ), - activeBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - const StreamColorTheme.dark().disabled, - const StreamColorTheme.dark().disabled, - ], - ), -); diff --git a/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart deleted file mode 100644 index d8759b2be3..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -String _dummyFormatter(BuildContext context, DateTime date) => 'formatted'; - -void main() { - test('MessageThemeData copyWith, ==, hashCode basics', () { - expect(const StreamMessageThemeData(), const StreamMessageThemeData().copyWith()); - expect(const StreamMessageThemeData().hashCode, const StreamMessageThemeData().copyWith().hashCode); - }); - - group('MessageThemeData lerps', () { - test('''Light MessageThemeData lerps completely to dark MessageThemeData''', () { - expect( - const StreamMessageThemeData().lerp(_messageThemeControl, _messageThemeControlDark, 1), - _messageThemeControlDark, - ); - }); - - test('''Dark MessageThemeData lerps completely to light MessageThemeData''', () { - expect( - const StreamMessageThemeData().lerp(_messageThemeControlDark, _messageThemeControl, 1), - _messageThemeControl, - ); - }); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect(_messageThemeControl.merge(_messageThemeControlDark), _messageThemeControlDark); - }); -} - -final _messageThemeControl = StreamMessageThemeData( - messageAuthorStyle: const StreamTextTheme.light().footnote.copyWith( - color: const StreamColorTheme.light().textLowEmphasis, - ), - messageTextStyle: const StreamTextTheme.light().body, - createdAtStyle: const StreamTextTheme.light().footnote.copyWith( - color: const StreamColorTheme.light().textLowEmphasis, - ), - createdAtFormatter: _dummyFormatter, - repliesStyle: const StreamTextTheme.light().footnoteBold.copyWith( - color: const StreamColorTheme.light().accentPrimary, - ), - messageBackgroundColor: const StreamColorTheme.light().disabled, - reactionsBackgroundColor: const StreamColorTheme.light().barsBg, - reactionsBorderColor: const StreamColorTheme.light().borders, - reactionsMaskColor: const StreamColorTheme.light().appBg, - messageBorderColor: const StreamColorTheme.light().disabled, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - messageLinksStyle: TextStyle( - color: const StreamColorTheme.light().accentPrimary, - ), - urlAttachmentBackgroundColor: const StreamColorTheme.light().linkBg, -); - -final _messageThemeControlDark = StreamMessageThemeData( - messageAuthorStyle: const StreamTextTheme.dark().footnote.copyWith( - color: const StreamColorTheme.dark().textLowEmphasis, - ), - messageTextStyle: const StreamTextTheme.dark().body, - createdAtStyle: const StreamTextTheme.dark().footnote.copyWith( - color: const StreamColorTheme.dark().textLowEmphasis, - ), - createdAtFormatter: _dummyFormatter, - repliesStyle: const StreamTextTheme.dark().footnoteBold.copyWith( - color: const StreamColorTheme.dark().accentPrimary, - ), - messageBackgroundColor: const StreamColorTheme.dark().disabled, - reactionsBackgroundColor: const StreamColorTheme.dark().barsBg, - reactionsBorderColor: const StreamColorTheme.dark().borders, - reactionsMaskColor: const StreamColorTheme.dark().appBg, - messageBorderColor: const StreamColorTheme.dark().disabled, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - messageLinksStyle: TextStyle( - color: const StreamColorTheme.dark().accentPrimary, - ), - urlAttachmentBackgroundColor: const StreamColorTheme.dark().linkBg, -); diff --git a/sample_app/lib/pages/channel_file_display_screen.dart b/sample_app/lib/pages/channel_file_display_screen.dart index 215778e9dc..ee11f065dd 100644 --- a/sample_app/lib/pages/channel_file_display_screen.dart +++ b/sample_app/lib/pages/channel_file_display_screen.dart @@ -5,11 +5,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; class ChannelFileDisplayScreen extends StatefulWidget { - const ChannelFileDisplayScreen({ - super.key, - required this.messageTheme, - }); - final StreamMessageThemeData messageTheme; + const ChannelFileDisplayScreen({super.key}); @override State createState() => _ChannelFileDisplayScreenState(); diff --git a/sample_app/lib/pages/channel_media_display_screen.dart b/sample_app/lib/pages/channel_media_display_screen.dart index 805dc45e65..9a82291b29 100644 --- a/sample_app/lib/pages/channel_media_display_screen.dart +++ b/sample_app/lib/pages/channel_media_display_screen.dart @@ -7,11 +7,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; class ChannelMediaDisplayScreen extends StatefulWidget { - const ChannelMediaDisplayScreen({ - super.key, - required this.messageTheme, - }); - final StreamMessageThemeData messageTheme; + const ChannelMediaDisplayScreen({super.key}); @override State createState() => _ChannelMediaDisplayScreenState(); diff --git a/sample_app/lib/pages/chat_info_screen.dart b/sample_app/lib/pages/chat_info_screen.dart index 8281ab7607..095bd6b065 100644 --- a/sample_app/lib/pages/chat_info_screen.dart +++ b/sample_app/lib/pages/chat_info_screen.dart @@ -11,15 +11,12 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class ChatInfoScreen extends StatefulWidget { const ChatInfoScreen({ super.key, - required this.messageTheme, this.user, }); /// User in consideration final User? user; - final StreamMessageThemeData messageTheme; - @override State createState() => _ChatInfoScreenState(); } @@ -207,9 +204,7 @@ class _ChatInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: ChannelMediaDisplayScreen( - messageTheme: widget.messageTheme, - ), + child: const ChannelMediaDisplayScreen(), ), ), ); @@ -239,9 +234,7 @@ class _ChatInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: ChannelFileDisplayScreen( - messageTheme: widget.messageTheme, - ), + child: const ChannelFileDisplayScreen(), ), ), ); diff --git a/sample_app/lib/pages/group_info_screen.dart b/sample_app/lib/pages/group_info_screen.dart index eef631162f..1d4688597d 100644 --- a/sample_app/lib/pages/group_info_screen.dart +++ b/sample_app/lib/pages/group_info_screen.dart @@ -13,11 +13,7 @@ import 'package:sample_app/routes/routes.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class GroupInfoScreen extends StatefulWidget { - const GroupInfoScreen({ - super.key, - required this.messageTheme, - }); - final StreamMessageThemeData messageTheme; + const GroupInfoScreen({super.key}); @override State createState() => _GroupInfoScreenState(); @@ -493,9 +489,7 @@ class _GroupInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: ChannelMediaDisplayScreen( - messageTheme: widget.messageTheme, - ), + child: const ChannelMediaDisplayScreen(), ), ), ); @@ -514,9 +508,7 @@ class _GroupInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: ChannelFileDisplayScreen( - messageTheme: widget.messageTheme, - ), + child: const ChannelFileDisplayScreen(), ), ), ); diff --git a/sample_app/lib/routes/app_routes.dart b/sample_app/lib/routes/app_routes.dart index ef0f41dce5..d3b8fd6a7f 100644 --- a/sample_app/lib/routes/app_routes.dart +++ b/sample_app/lib/routes/app_routes.dart @@ -58,7 +58,6 @@ final appRoutes = [ channel: channel!, child: ChatInfoScreen( user: state.extra as User?, - messageTheme: StreamChatTheme.of(context).ownMessageTheme, ), ); }, @@ -70,9 +69,7 @@ final appRoutes = [ final channel = StreamChat.of(context).client.state.channels[state.pathParameters['cid']]; return StreamChannel( channel: channel!, - child: GroupInfoScreen( - messageTheme: StreamChatTheme.of(context).ownMessageTheme, - ), + child: const GroupInfoScreen(), ); }, ), diff --git a/sample_app/lib/widgets/channel_list.dart b/sample_app/lib/widgets/channel_list.dart index 8eb24eb3ec..c41884bca9 100644 --- a/sample_app/lib/widgets/channel_list.dart +++ b/sample_app/lib/widgets/channel_list.dart @@ -151,15 +151,12 @@ class _ChannelListDefault extends StatelessWidget { channel: channel, child: isOneToOne ? ChatInfoScreen( - messageTheme: chatTheme.ownMessageTheme, user: channel.state!.members .where((m) => m.userId != channel.client.state.currentUser!.id) .first .user, ) - : GroupInfoScreen( - messageTheme: chatTheme.ownMessageTheme, - ), + : const GroupInfoScreen(), ); }, ), From 61eb799b323994ea236ea8e23876f4682eac3d7e Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 16 Apr 2026 17:13:49 +0200 Subject: [PATCH 2/5] formatting --- packages/stream_chat_flutter/example/lib/tutorial_part_6.dart | 1 - .../lib/src/message_input/stream_message_input.dart | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart index f0ef523b30..9d4bbe7a52 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart @@ -60,7 +60,6 @@ class MyApp extends StatelessWidget { ), ); final defaultTheme = StreamChatThemeData.fromTheme(themeData); - final colorTheme = defaultTheme.colorTheme; final customTheme = StreamChatThemeData( channelPreviewTheme: StreamChannelPreviewThemeData( avatarTheme: StreamAvatarThemeData( diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 3603560785..0f928fcb18 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -747,8 +747,7 @@ class StreamMessageInputState extends State PlatformType.android || PlatformType.ios => false, _ => true, }; - final useSystemPicker = - widget.useSystemAttachmentPicker || isWebOrDesktop; + final useSystemPicker = widget.useSystemAttachmentPicker || isWebOrDesktop; final child = useSystemPicker ? systemAttachmentPickerBuilder( From 478d51271dc4f9b2aa46b77f7bcfdbbdda47dcce Mon Sep 17 00:00:00 2001 From: renefloor <15101411+renefloor@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:17:58 +0000 Subject: [PATCH 3/5] chore: Update Goldens --- .../ci/stream_message_actions_modal_dark.png | Bin 3814 -> 3880 bytes .../ci/stream_message_actions_modal_light.png | Bin 4781 -> 4763 bytes ...am_message_actions_modal_reversed_dark.png | Bin 3837 -> 3907 bytes ...m_message_actions_modal_reversed_light.png | Bin 4720 -> 4745 bytes ...ons_modal_reversed_with_reactions_dark.png | Bin 6354 -> 6425 bytes ...ns_modal_reversed_with_reactions_light.png | Bin 9141 -> 9167 bytes ...sage_actions_modal_with_reactions_dark.png | Bin 6339 -> 6406 bytes ...age_actions_modal_with_reactions_light.png | Bin 9210 -> 9194 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_dark.png b/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_dark.png index 575be478cbefdaec5bb0269c17f3e0e059bf0027..424f99a3b6c602686710ce617f33e499cb3afc44 100644 GIT binary patch delta 1977 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 diff --git a/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_light.png b/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_light.png index aab51b08c11e7a274abc0bc7c240cc2985bf3940..e5463054039aaf8fd66644e3ce896ca4e5ff4ccf 100644 GIT binary patch delta 2904 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 delta 1939 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 Fe*yVUB?$lk delta 2862 zcmXX|dq9%s8pq4j)k$q#oEl)ct+SI_&D0V>mo6(?mnE8b!E#3%XkI~u;L|l%odPpY zYhKBmp`el?Uhsk~9rHdzMDc#0`p@g?4R-Hxg*PeUfpJG9l{;Q4#aXDXeT(r|7;QAc(|CDa!=2}QK!#O=wa-3(*jcJ@`5#WL3mVfaDQ}K4P3WM<$Y3$9-qnLFoD^kz) zsf)o8Av(P!E>=H?<6(GhZ6jh{`z>qE^Y!(Bf&L|18@C`31#xK-U0xm&85!y~WB2JE z2Udb;f{e6yf82c{eRO6fS~9xBwmAy~hiDXZ7g&ZLFCVTfP$F6zOS3vZ0?wQ{gXYSD za^j(8W;Z-gsNn}ZpRZ{9or=XB;h{F?utx#SI+`9|DuT3koLsW%S*Og5H(4GBeb(2>KlKA4)20Y)hs^h>#(sSkTz z;Z4y6hmIy&0H$_Hulw!e_Qrp?h{tbo@PKD-?pBHB6c-aBt-_D9#jO1OSe2AyYAWbd zC{7QQyh+>nPyp`LYTP==f8m{$yEikC~bYmo$vV3X|b?i>sj z%TXeTAzXRc^H;MVz`_#bg~iBb)!YWK`uD6mPx<`sd<{J-4sb?gPgR0gB3aU$Y&(?@ zA3yS5ev#MQd-xBUK=B%iY20%4iIm68-y(x+yyq&zTaloL%HRjazg0SZ+rq^2+Yhfw->%IsEb zrHFY>OWFxU-zh36s2ZZKaS45{O=$_=n#l8nFmg34^JQKt>Jr zp(QrtrYogrz|i%KZww_$y2h^3Xx8GHPCsDVTWu(o!7j{Zj8Ddkb>pDt8l!^x*u13J zD!M@^kf^CwvF&eP1S!=MO^H~gOdyzB&9Do3h+OH~G2b;uN2n?jfsK<>4f7>DJ)gZ; zgJ9yX#5eqGVcBJNG!Tm|2j6fh*Bhq!seB?fov?XOqg2DI5b8Fr0iamgz^XY^4I9c3 z+Ml$esL8SqHYnzqkI(J7&YFI=sAe@8UWDVWCOwkOnS_t@N=b(G?j0JhTw@sYXwoCY zi1wl2{VS5TRW?;eD9KLikxDVh?ne=q&lVT6 z;m8fFrWPr{pwS58Ql^JDC1G)I|PcsO4{B92x+)%vDHeV=Om}Lrot6^CEC``#rZyVPSHORcFyj zYAQIWWlBcYcz6ZiArDpEb3+=^Pg_$g zO#K4#6z?W`!w~w3h25 z`=b=0*i`=pizIoJk1>K1*zEK{{q_&)50i_SZdznP_H?pE-b%oyn?Q!XJsYTPy=)8? zN+51i-991YXCJfzn_3Amf|q@ibFq)oi?boNpc_85xBo$=!XLqL#R|Dk=Yxb)E*k7v z6d!MIX&wQjD>gPZiZH^WaUGGx7R@h8rp78m@LWn8MZZR`c6N$fR%G=LF=`+1G^K-P z5#u2t5?rz|O@GO+@u_2CTOCE;BZSv!P(4_J$3~`QwvzlPP_! z%88rSP?l(G5U-^r^O+v?{}(ye`0^)U)vRT);Kt>fytLS| z*mDC~lFQ7NBV
  • )?=hb;uWsM*I8w^Wug|>D8Y*MIJhI=sa1gN`T7%uGUP`J9*rd zQ@xhvHq%?*P>*4rJvbVv-l$oVpw^$+gkZ68Mz-BOw-)U*8K06;ub7Y9YXe)4XkQkM zdoM!6Ivt}8OXH+j=0QpJ5b%T!_l-iMMIsSF-L56ZgN5&YU0t2-KX!sK6m_XisOS-H z)Kk;m3-IFB`Ya&mDOi|%NwXC?w(my1t~a#fg;AGnW+^?#NI$+>4mT{!Bi2O6gdUQJ zi!m`Vg!ScF7E3qU)PeS;8hsa^6aieF1I@u6rqJB#+?|?ot&IBSMUNENP)o{lQBtGM zXwWaNO(d!pq?^-JvkSiuc76GoPA3seqdL|ADSlCHoMqr}IQ|!ZoWRAWRa#hD;2sGC zVE);?59=%vzJ48B!g)5xz(&`8)P+qZTitWB-VSnyP~kSnx93ZaCO?*xL&7I(BU=?BhA~Z8`!2)P zG{rtcCuc6sJiDQpOa-%WXY`$xl|LNuvJMJC2u)9;B$%|^ZF|#uKr^MLHi6X!Bcwyw zo1JiSGJ+#Cb=$jRqA`KJ$Psi9)zgvc3?V}Cx+t+$E|-@MID^eJ!m@6q5F9yo+5Ncz Zal5ngb-xI?8v;J3zBwCEdp!Eb{{rah7Bv6> diff --git a/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_reversed_with_reactions_dark.png b/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_reversed_with_reactions_dark.png index 38524bd115f62853d36981d582833523e4672bd1..eed7f37405fcdbcd363bc2b279aa4f0861d02f87 100644 GIT binary patch literal 6425 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 literal 6354 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 literal 9141 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 Q1p$YSrhx|LfkX8F0@>SqnE(I) diff --git a/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_with_reactions_dark.png b/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_with_reactions_dark.png index eb88364db3c2c8a0bdcb616615636056e6e63415..1e1492b3ba4f37b299893c354342792c9f94269c 100644 GIT binary patch literal 6406 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 literal 6339 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 diff --git a/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_with_reactions_light.png b/packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_actions_modal_with_reactions_light.png index 660f447e365a366dc52d34540c92ef37ecbfc81e..dafa52856cab79631f19a12b20e7aacfcc9b100b 100644 GIT binary patch literal 9194 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 From 584e1b281c627daf733d08de4fe7fd8b4501105a Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Fri, 17 Apr 2026 09:38:11 +0200 Subject: [PATCH 4/5] remove unused imports --- .../attachment_picker/options/stream_image_picker.dart | 1 - .../attachment_picker/options/stream_poll_creator.dart | 1 - .../attachment_picker/options/stream_video_picker.dart | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart index 7c78ef20ed..83e3e07cf5 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_core_flutter/stream_core_flutter.dart'; /// Widget used to pick images from the device. class StreamImagePicker extends StatelessWidget { diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart index 2d6fd841ea..81a2c40acb 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_core_flutter/stream_core_flutter.dart'; /// Widget used to create a poll. class StreamPollCreator extends StatelessWidget { diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart index 409b19fb3a..f45754ecca 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_core_flutter/stream_core_flutter.dart'; /// Widget used to capture video using the device camera. class StreamVideoPicker extends StatelessWidget { From bf44cf235a8c8b8fd0a2375d5454a9a1f1ce9d90 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Fri, 17 Apr 2026 14:51:18 +0200 Subject: [PATCH 5/5] Add changelog --- packages/stream_chat_flutter/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 410fec6b11..b936a4a1a1 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -6,6 +6,8 @@ `StreamComponentFactory.jumpToUnreadButton` instead. - Renamed `UnreadIndicatorButton.onTap` → `onJumpTap`. - Renamed stream icons to remove the size suffix from the icon names. +- Removed `StreamMessageThemeData` (ownMessageTheme and otherMessageTheme) and `StreamMessageInputThemeData` (messageInputTheme). +- Removed `AttachmentButton`, `StreamQuotedMessageWidget`, `EditMessageSheet`, `StreamMessageSendButton` and `DesktopReactionsBuilder`. ✅ Added