From d74576a1971465865e75e9ffbb541c0c8ec1f7c5 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Wed, 4 Aug 2021 14:35:58 -0400 Subject: [PATCH 1/5] chore: add UserAvatar placeholder builder to stream_chat_theme.dart and user_avatar.dart --- packages/stream_chat_flutter/lib/src/stream_chat_theme.dart | 4 ++++ packages/stream_chat_flutter/lib/src/user_avatar.dart | 2 ++ 2 files changed, 6 insertions(+) diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 5fe56dbc..902f5271 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -113,6 +113,7 @@ class StreamChatThemeData { required this.ownMessageTheme, required this.messageInputTheme, required this.defaultUserImage, + this.placeholderUserImage, required this.primaryIconTheme, required this.reactionIcons, required this.galleryHeaderTheme, @@ -170,6 +171,9 @@ class StreamChatThemeData { /// The widget that will be built when the user image is unavailable final Widget Function(BuildContext, User) defaultUserImage; + /// The widget that will be built when the user image is loading + final Widget Function(BuildContext context, User user)? placeholderUserImage; + /// Primary icon theme final IconThemeData primaryIconTheme; diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index 31e07f0a..ba737e5e 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -79,6 +79,8 @@ class UserAvatar extends StatelessWidget { imageUrl: user.extraData['image'] as String, errorWidget: (_, __, ___) => streamChatTheme.defaultUserImage(context, user), + placeholder: (_, __) => + streamChatTheme.placeholderUserImage!(context, user), fit: BoxFit.cover, ) : streamChatTheme.defaultUserImage(context, user), From 1b9c718ed880e785ad0e36bb330864e8d71b23b1 Mon Sep 17 00:00:00 2001 From: groovinchip Date: Wed, 4 Aug 2021 15:34:48 -0400 Subject: [PATCH 2/5] chore: address PR feedback --- .../stream_chat_flutter/lib/src/user_avatar.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index ba737e5e..7c9437eb 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -19,6 +19,7 @@ class UserAvatar extends StatelessWidget { this.selected = false, this.selectionColor, this.selectionThickness = 4, + this.placeholderUserImageBuilder, }) : super(key: key); /// User whose avatar is to displayed @@ -54,6 +55,10 @@ class UserAvatar extends StatelessWidget { /// Selection thickness around the avatar final double selectionThickness; + /// The widget that will be built when the user image is loading + final Widget Function(BuildContext context, User user)? + placeholderUserImageBuilder; + @override Widget build(BuildContext context) { final hasImage = user.extraData.containsKey('image') && @@ -79,8 +84,12 @@ class UserAvatar extends StatelessWidget { imageUrl: user.extraData['image'] as String, errorWidget: (_, __, ___) => streamChatTheme.defaultUserImage(context, user), - placeholder: (_, __) => - streamChatTheme.placeholderUserImage!(context, user), + placeholder: placeholderUserImageBuilder == null + ? streamChatTheme.placeholderUserImage == null + ? null + : (_, __) => streamChatTheme.placeholderUserImage!( + context, user) + : (_, __) => placeholderUserImageBuilder!(context, user), fit: BoxFit.cover, ) : streamChatTheme.defaultUserImage(context, user), From f3b7f31badba82e26f3e1c8369e821422aad3976 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 5 Aug 2021 14:54:30 +0530 Subject: [PATCH 3/5] chore(ui): minor improvements Signed-off-by: xsahil03x --- .../lib/src/stream_chat_theme.dart | 266 +++++++++--------- .../lib/src/user_avatar.dart | 21 +- 2 files changed, 145 insertions(+), 142 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 902f5271..f7a05d7e 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -54,6 +54,7 @@ class StreamChatThemeData { MessageThemeData? ownMessageTheme, MessageInputThemeData? messageInputTheme, Widget Function(BuildContext, User)? defaultUserImage, + Widget Function(BuildContext, User)? placeholderUserImage, IconThemeData? primaryIconTheme, List? reactionIcons, GalleryHeaderThemeData? imageHeaderTheme, @@ -68,7 +69,7 @@ class StreamChatThemeData { textTheme ??= isDark ? TextTheme.dark() : TextTheme.light(); colorTheme ??= isDark ? ColorTheme.dark() : ColorTheme.light(); - final defaultData = fromColorAndTextTheme( + final defaultData = StreamChatThemeData.fromColorAndTextTheme( colorTheme, textTheme, ); @@ -81,6 +82,7 @@ class StreamChatThemeData { ownMessageTheme: ownMessageTheme, messageInputTheme: messageInputTheme, defaultUserImage: defaultUserImage, + placeholderUserImage: placeholderUserImage, primaryIconTheme: primaryIconTheme, reactionIcons: reactionIcons, galleryHeaderTheme: imageHeaderTheme, @@ -136,137 +138,8 @@ class StreamChatThemeData { return defaultTheme.merge(customizedTheme); } - /// The text themes used in the widgets - final TextTheme textTheme; - - /// The color themes used in the widgets - final ColorTheme colorTheme; - - /// Theme of the [ChannelPreview] - final ChannelPreviewThemeData channelPreviewTheme; - - /// Theme of the [ChannelListHeader] - final ChannelListHeaderThemeData channelListHeaderTheme; - - /// Theme of the chat widgets dedicated to a channel header - final ChannelHeaderThemeData channelHeaderTheme; - - /// The default style for [GalleryHeader]s below the overall - /// [StreamChatTheme]. - final GalleryHeaderThemeData galleryHeaderTheme; - - /// The default style for [GalleryFooter]s below the overall - /// [StreamChatTheme]. - final GalleryFooterThemeData galleryFooterTheme; - - /// Theme of the current user messages - final MessageThemeData ownMessageTheme; - - /// Theme of other users messages - final MessageThemeData otherMessageTheme; - - /// Theme dedicated to the [MessageInput] widget - final MessageInputThemeData messageInputTheme; - - /// The widget that will be built when the user image is unavailable - final Widget Function(BuildContext, User) defaultUserImage; - - /// The widget that will be built when the user image is loading - final Widget Function(BuildContext context, User user)? placeholderUserImage; - - /// Primary icon theme - final IconThemeData primaryIconTheme; - - /// Assets used for rendering reactions - final List reactionIcons; - - /// Theme configuration for the [MessageListView] widget. - final MessageListViewThemeData messageListViewTheme; - - /// Theme configuration for the [ChannelListView] widget. - final ChannelListViewThemeData channelListViewTheme; - - /// Theme configuration for the [UserListView] widget. - final UserListViewThemeData userListViewTheme; - - /// Theme configuration for the [MessageSearchListView] widget. - final MessageSearchListViewThemeData messageSearchListViewTheme; - - /// Creates a copy of [StreamChatThemeData] with specified attributes - /// overridden. - StreamChatThemeData copyWith({ - TextTheme? textTheme, - ColorTheme? colorTheme, - ChannelPreviewThemeData? channelPreviewTheme, - ChannelHeaderThemeData? channelHeaderTheme, - MessageThemeData? ownMessageTheme, - MessageThemeData? otherMessageTheme, - MessageInputThemeData? messageInputTheme, - Widget Function(BuildContext, User)? defaultUserImage, - IconThemeData? primaryIconTheme, - ChannelListHeaderThemeData? channelListHeaderTheme, - List? reactionIcons, - GalleryHeaderThemeData? galleryHeaderTheme, - GalleryFooterThemeData? galleryFooterTheme, - MessageListViewThemeData? messageListViewTheme, - ChannelListViewThemeData? channelListViewTheme, - UserListViewThemeData? userListViewTheme, - MessageSearchListViewThemeData? messageSearchListViewTheme, - }) => - StreamChatThemeData.raw( - channelListHeaderTheme: - this.channelListHeaderTheme.merge(channelListHeaderTheme), - textTheme: this.textTheme.merge(textTheme), - colorTheme: this.colorTheme.merge(colorTheme), - primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme), - defaultUserImage: defaultUserImage ?? this.defaultUserImage, - 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), - reactionIcons: reactionIcons ?? this.reactionIcons, - galleryHeaderTheme: galleryHeaderTheme ?? this.galleryHeaderTheme, - galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme, - messageListViewTheme: messageListViewTheme ?? this.messageListViewTheme, - channelListViewTheme: channelListViewTheme ?? this.channelListViewTheme, - userListViewTheme: userListViewTheme ?? this.userListViewTheme, - messageSearchListViewTheme: - messageSearchListViewTheme ?? this.messageSearchListViewTheme, - ); - - /// Merge themes - StreamChatThemeData merge(StreamChatThemeData? other) { - if (other == null) return this; - return copyWith( - channelListHeaderTheme: - channelListHeaderTheme.merge(other.channelListHeaderTheme), - textTheme: textTheme.merge(other.textTheme), - colorTheme: colorTheme.merge(other.colorTheme), - primaryIconTheme: other.primaryIconTheme, - defaultUserImage: other.defaultUserImage, - 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), - reactionIcons: other.reactionIcons, - galleryHeaderTheme: galleryHeaderTheme.merge(other.galleryHeaderTheme), - galleryFooterTheme: galleryFooterTheme.merge(other.galleryFooterTheme), - messageListViewTheme: - messageListViewTheme.merge(other.messageListViewTheme), - channelListViewTheme: - channelListViewTheme.merge(other.channelListViewTheme), - userListViewTheme: userListViewTheme.merge(other.userListViewTheme), - messageSearchListViewTheme: - messageSearchListViewTheme.merge(other.messageSearchListViewTheme), - ); - } - /// Create theme from color and text theme - // ignore: prefer_constructors_over_static_methods - static StreamChatThemeData fromColorAndTextTheme( + factory StreamChatThemeData.fromColorAndTextTheme( ColorTheme colorTheme, TextTheme textTheme, ) { @@ -491,4 +364,135 @@ class StreamChatThemeData { ), ); } + + /// The text themes used in the widgets + final TextTheme textTheme; + + /// The color themes used in the widgets + final ColorTheme colorTheme; + + /// Theme of the [ChannelPreview] + final ChannelPreviewThemeData channelPreviewTheme; + + /// Theme of the [ChannelListHeader] + final ChannelListHeaderThemeData channelListHeaderTheme; + + /// Theme of the chat widgets dedicated to a channel header + final ChannelHeaderThemeData channelHeaderTheme; + + /// The default style for [GalleryHeader]s below the overall + /// [StreamChatTheme]. + final GalleryHeaderThemeData galleryHeaderTheme; + + /// The default style for [GalleryFooter]s below the overall + /// [StreamChatTheme]. + final GalleryFooterThemeData galleryFooterTheme; + + /// Theme of the current user messages + final MessageThemeData ownMessageTheme; + + /// Theme of other users messages + final MessageThemeData otherMessageTheme; + + /// Theme dedicated to the [MessageInput] widget + final MessageInputThemeData messageInputTheme; + + /// The widget that will be built when the user image is unavailable + final Widget Function(BuildContext, User) defaultUserImage; + + /// The widget that will be built when the user image is loading + final Widget Function(BuildContext, User)? placeholderUserImage; + + /// Primary icon theme + final IconThemeData primaryIconTheme; + + /// Assets used for rendering reactions + final List reactionIcons; + + /// Theme configuration for the [MessageListView] widget. + final MessageListViewThemeData messageListViewTheme; + + /// Theme configuration for the [ChannelListView] widget. + final ChannelListViewThemeData channelListViewTheme; + + /// Theme configuration for the [UserListView] widget. + final UserListViewThemeData userListViewTheme; + + /// Theme configuration for the [MessageSearchListView] widget. + final MessageSearchListViewThemeData messageSearchListViewTheme; + + /// Creates a copy of [StreamChatThemeData] with specified attributes + /// overridden. + StreamChatThemeData copyWith({ + TextTheme? textTheme, + ColorTheme? colorTheme, + ChannelPreviewThemeData? channelPreviewTheme, + ChannelHeaderThemeData? channelHeaderTheme, + MessageThemeData? ownMessageTheme, + MessageThemeData? otherMessageTheme, + MessageInputThemeData? messageInputTheme, + Widget Function(BuildContext, User)? defaultUserImage, + Widget Function(BuildContext, User)? placeholderUserImage, + IconThemeData? primaryIconTheme, + ChannelListHeaderThemeData? channelListHeaderTheme, + List? reactionIcons, + GalleryHeaderThemeData? galleryHeaderTheme, + GalleryFooterThemeData? galleryFooterTheme, + MessageListViewThemeData? messageListViewTheme, + ChannelListViewThemeData? channelListViewTheme, + UserListViewThemeData? userListViewTheme, + MessageSearchListViewThemeData? messageSearchListViewTheme, + }) => + StreamChatThemeData.raw( + channelListHeaderTheme: + this.channelListHeaderTheme.merge(channelListHeaderTheme), + textTheme: this.textTheme.merge(textTheme), + colorTheme: this.colorTheme.merge(colorTheme), + primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme), + defaultUserImage: defaultUserImage ?? this.defaultUserImage, + placeholderUserImage: placeholderUserImage ?? this.placeholderUserImage, + 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), + reactionIcons: reactionIcons ?? this.reactionIcons, + galleryHeaderTheme: galleryHeaderTheme ?? this.galleryHeaderTheme, + galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme, + messageListViewTheme: messageListViewTheme ?? this.messageListViewTheme, + channelListViewTheme: channelListViewTheme ?? this.channelListViewTheme, + userListViewTheme: userListViewTheme ?? this.userListViewTheme, + messageSearchListViewTheme: + messageSearchListViewTheme ?? this.messageSearchListViewTheme, + ); + + /// Merge themes + StreamChatThemeData merge(StreamChatThemeData? other) { + if (other == null) return this; + return copyWith( + channelListHeaderTheme: + channelListHeaderTheme.merge(other.channelListHeaderTheme), + textTheme: textTheme.merge(other.textTheme), + colorTheme: colorTheme.merge(other.colorTheme), + primaryIconTheme: other.primaryIconTheme, + defaultUserImage: other.defaultUserImage, + placeholderUserImage: other.placeholderUserImage, + 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), + reactionIcons: other.reactionIcons, + galleryHeaderTheme: galleryHeaderTheme.merge(other.galleryHeaderTheme), + galleryFooterTheme: galleryFooterTheme.merge(other.galleryFooterTheme), + messageListViewTheme: + messageListViewTheme.merge(other.messageListViewTheme), + channelListViewTheme: + channelListViewTheme.merge(other.channelListViewTheme), + userListViewTheme: userListViewTheme.merge(other.userListViewTheme), + messageSearchListViewTheme: + messageSearchListViewTheme.merge(other.messageSearchListViewTheme), + ); + } } diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index 7c9437eb..40561d77 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -19,7 +19,7 @@ class UserAvatar extends StatelessWidget { this.selected = false, this.selectionColor, this.selectionThickness = 4, - this.placeholderUserImageBuilder, + this.placeholder, }) : super(key: key); /// User whose avatar is to displayed @@ -56,8 +56,7 @@ class UserAvatar extends StatelessWidget { final double selectionThickness; /// The widget that will be built when the user image is loading - final Widget Function(BuildContext context, User user)? - placeholderUserImageBuilder; + final Widget Function(BuildContext, User)? placeholder; @override Widget build(BuildContext context) { @@ -66,6 +65,9 @@ class UserAvatar extends StatelessWidget { user.extraData['image'] != ''; final streamChatTheme = StreamChatTheme.of(context); + final placeholder = + this.placeholder ?? streamChatTheme.placeholderUserImage; + Widget avatar = FittedBox( fit: BoxFit.cover, child: ClipRRect( @@ -79,18 +81,15 @@ class UserAvatar extends StatelessWidget { ), child: hasImage ? CachedNetworkImage( + fit: BoxFit.cover, filterQuality: FilterQuality.high, // ignore: cast_nullable_to_non_nullable imageUrl: user.extraData['image'] as String, - errorWidget: (_, __, ___) => + errorWidget: (context, __, ___) => streamChatTheme.defaultUserImage(context, user), - placeholder: placeholderUserImageBuilder == null - ? streamChatTheme.placeholderUserImage == null - ? null - : (_, __) => streamChatTheme.placeholderUserImage!( - context, user) - : (_, __) => placeholderUserImageBuilder!(context, user), - fit: BoxFit.cover, + placeholder: placeholder != null + ? (context, __) => placeholder(context, user) + : null, ) : streamChatTheme.defaultUserImage(context, user), ), From 29d0f91be1677752e0d53ccedca9c327d7330dc9 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 5 Aug 2021 15:02:39 +0530 Subject: [PATCH 4/5] fix(ui): remove background color from `UserAvatar` Signed-off-by: xsahil03x --- packages/stream_chat_flutter/lib/src/user_avatar.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index 40561d77..61627a81 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -76,9 +76,6 @@ class UserAvatar extends StatelessWidget { child: Container( constraints: constraints ?? streamChatTheme.ownMessageTheme.avatarTheme?.constraints, - decoration: BoxDecoration( - color: streamChatTheme.colorTheme.accentPrimary, - ), child: hasImage ? CachedNetworkImage( fit: BoxFit.cover, From 0df21e92c34f9a34812eed2ec4bcbc2c8ad118bb Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 5 Aug 2021 15:03:04 +0530 Subject: [PATCH 5/5] chore(ui): update CHANGELOG.md Signed-off-by: xsahil03x --- packages/stream_chat_flutter/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index efac9508..4fe9e6e9 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,5 +1,11 @@ ## Upcoming +✅ Added + +- [#516](https://github.com/GetStream/stream-chat-flutter/issues/516): Added `StreamChatThemeData.placeholderUserImage` for + building a widget when the `UserAvatar` image is loading + + 🔄 Changed Theming has been upgraded! Most theme classes now have `InheritedTheme` classes