Merge pull request #609 from GetStream/cds-344
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
## Upcoming
|
## 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
|
🔄 Changed
|
||||||
|
|
||||||
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes
|
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class StreamChatThemeData {
|
|||||||
MessageThemeData? ownMessageTheme,
|
MessageThemeData? ownMessageTheme,
|
||||||
MessageInputThemeData? messageInputTheme,
|
MessageInputThemeData? messageInputTheme,
|
||||||
Widget Function(BuildContext, User)? defaultUserImage,
|
Widget Function(BuildContext, User)? defaultUserImage,
|
||||||
|
Widget Function(BuildContext, User)? placeholderUserImage,
|
||||||
IconThemeData? primaryIconTheme,
|
IconThemeData? primaryIconTheme,
|
||||||
List<ReactionIcon>? reactionIcons,
|
List<ReactionIcon>? reactionIcons,
|
||||||
GalleryHeaderThemeData? imageHeaderTheme,
|
GalleryHeaderThemeData? imageHeaderTheme,
|
||||||
@@ -68,7 +69,7 @@ class StreamChatThemeData {
|
|||||||
textTheme ??= isDark ? TextTheme.dark() : TextTheme.light();
|
textTheme ??= isDark ? TextTheme.dark() : TextTheme.light();
|
||||||
colorTheme ??= isDark ? ColorTheme.dark() : ColorTheme.light();
|
colorTheme ??= isDark ? ColorTheme.dark() : ColorTheme.light();
|
||||||
|
|
||||||
final defaultData = fromColorAndTextTheme(
|
final defaultData = StreamChatThemeData.fromColorAndTextTheme(
|
||||||
colorTheme,
|
colorTheme,
|
||||||
textTheme,
|
textTheme,
|
||||||
);
|
);
|
||||||
@@ -81,6 +82,7 @@ class StreamChatThemeData {
|
|||||||
ownMessageTheme: ownMessageTheme,
|
ownMessageTheme: ownMessageTheme,
|
||||||
messageInputTheme: messageInputTheme,
|
messageInputTheme: messageInputTheme,
|
||||||
defaultUserImage: defaultUserImage,
|
defaultUserImage: defaultUserImage,
|
||||||
|
placeholderUserImage: placeholderUserImage,
|
||||||
primaryIconTheme: primaryIconTheme,
|
primaryIconTheme: primaryIconTheme,
|
||||||
reactionIcons: reactionIcons,
|
reactionIcons: reactionIcons,
|
||||||
galleryHeaderTheme: imageHeaderTheme,
|
galleryHeaderTheme: imageHeaderTheme,
|
||||||
@@ -113,6 +115,7 @@ class StreamChatThemeData {
|
|||||||
required this.ownMessageTheme,
|
required this.ownMessageTheme,
|
||||||
required this.messageInputTheme,
|
required this.messageInputTheme,
|
||||||
required this.defaultUserImage,
|
required this.defaultUserImage,
|
||||||
|
this.placeholderUserImage,
|
||||||
required this.primaryIconTheme,
|
required this.primaryIconTheme,
|
||||||
required this.reactionIcons,
|
required this.reactionIcons,
|
||||||
required this.galleryHeaderTheme,
|
required this.galleryHeaderTheme,
|
||||||
@@ -135,134 +138,8 @@ class StreamChatThemeData {
|
|||||||
return defaultTheme.merge(customizedTheme);
|
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;
|
|
||||||
|
|
||||||
/// Primary icon theme
|
|
||||||
final IconThemeData primaryIconTheme;
|
|
||||||
|
|
||||||
/// Assets used for rendering reactions
|
|
||||||
final List<ReactionIcon> 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<ReactionIcon>? 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
|
/// Create theme from color and text theme
|
||||||
// ignore: prefer_constructors_over_static_methods
|
factory StreamChatThemeData.fromColorAndTextTheme(
|
||||||
static StreamChatThemeData fromColorAndTextTheme(
|
|
||||||
ColorTheme colorTheme,
|
ColorTheme colorTheme,
|
||||||
TextTheme textTheme,
|
TextTheme textTheme,
|
||||||
) {
|
) {
|
||||||
@@ -487,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<ReactionIcon> 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<ReactionIcon>? 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),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class UserAvatar extends StatelessWidget {
|
|||||||
this.selected = false,
|
this.selected = false,
|
||||||
this.selectionColor,
|
this.selectionColor,
|
||||||
this.selectionThickness = 4,
|
this.selectionThickness = 4,
|
||||||
|
this.placeholder,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// User whose avatar is to displayed
|
/// User whose avatar is to displayed
|
||||||
@@ -54,6 +55,9 @@ class UserAvatar extends StatelessWidget {
|
|||||||
/// Selection thickness around the avatar
|
/// Selection thickness around the avatar
|
||||||
final double selectionThickness;
|
final double selectionThickness;
|
||||||
|
|
||||||
|
/// The widget that will be built when the user image is loading
|
||||||
|
final Widget Function(BuildContext, User)? placeholder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final hasImage = user.extraData.containsKey('image') &&
|
final hasImage = user.extraData.containsKey('image') &&
|
||||||
@@ -61,6 +65,9 @@ class UserAvatar extends StatelessWidget {
|
|||||||
user.extraData['image'] != '';
|
user.extraData['image'] != '';
|
||||||
final streamChatTheme = StreamChatTheme.of(context);
|
final streamChatTheme = StreamChatTheme.of(context);
|
||||||
|
|
||||||
|
final placeholder =
|
||||||
|
this.placeholder ?? streamChatTheme.placeholderUserImage;
|
||||||
|
|
||||||
Widget avatar = FittedBox(
|
Widget avatar = FittedBox(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
@@ -69,17 +76,17 @@ class UserAvatar extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: streamChatTheme.colorTheme.accentPrimary,
|
|
||||||
),
|
|
||||||
child: hasImage
|
child: hasImage
|
||||||
? CachedNetworkImage(
|
? CachedNetworkImage(
|
||||||
|
fit: BoxFit.cover,
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
imageUrl: user.extraData['image'] as String,
|
imageUrl: user.extraData['image'] as String,
|
||||||
errorWidget: (_, __, ___) =>
|
errorWidget: (context, __, ___) =>
|
||||||
streamChatTheme.defaultUserImage(context, user),
|
streamChatTheme.defaultUserImage(context, user),
|
||||||
fit: BoxFit.cover,
|
placeholder: placeholder != null
|
||||||
|
? (context, __) => placeholder(context, user)
|
||||||
|
: null,
|
||||||
)
|
)
|
||||||
: streamChatTheme.defaultUserImage(context, user),
|
: streamChatTheme.defaultUserImage(context, user),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user