diff --git a/example/lib/main.dart b/example/lib/main.dart index f0f1e59b..803df8e6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -470,6 +470,7 @@ class ThreadPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Color.fromRGBO(252, 252, 252, 1), appBar: ThreadHeader( parent: parent, ), diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 1967cd2b..f4cd47c8 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -113,7 +113,7 @@ class MessageActionsModal extends StatelessWidget { messageTheme: messageTheme, showReactions: false, showUsername: false, - showReplyIndicator: false, + showThreadReplyIndicator: false, showUserAvatar: showUserAvatar, showTimestamp: false, translateUserAvatar: false, diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 5a850ad3..24d32d88 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -241,7 +241,7 @@ class _MessageListViewState extends State { builder: (context, snapshot) { if (!snapshot.hasData) { return Center( - child: CircularProgressIndicator(), + child: const CircularProgressIndicator(), ); } @@ -336,18 +336,25 @@ class _MessageListViewState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ buildParentMessage(widget.parentMessage), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 32), - child: Container( - padding: const EdgeInsets.all(8), + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color(0XFFF7F7F7), + Color(0XFFFCFCFC), + ], + ), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), child: Text( - 'Start of thread', + '${widget.parentMessage.replyCount} ${widget.parentMessage.replyCount == 1 ? 'Reply' : 'Replies'}', textAlign: TextAlign.center, + style: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .lastMessageAt, ), - color: Theme.of(context) - .accentColor - .withAlpha(50), ), ), ], @@ -585,7 +592,7 @@ class _MessageListViewState extends State { if (!snapshot.data) { if (direction == QueryDirection.top) { return Container( - height: 50, + height: 52, width: double.infinity, ); } @@ -679,7 +686,8 @@ class _MessageListViewState extends State { final isMyMessage = message.user.id == StreamChat.of(context).user.id; return MessageWidget( - showReplyIndicator: false, + showThreadReplyIndicator: false, + showInChannelIndicator: false, message: message, reverse: isMyMessage, showUsername: !isMyMessage, @@ -748,6 +756,8 @@ class _MessageListViewState extends State { bottom: index == 0 ? 30 : (isNextUser ? 2 : 7), top: 3, ), + showInChannelIndicator: widget.parentMessage == null, + showThreadReplyIndicator: widget.parentMessage == null, showUsername: !isMyMessage && !isNextUser, showSendingIndicator: isMyMessage && (index == 0 || message.status != MessageSendingStatus.SENT) diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index db5d8710..e5043e62 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -105,7 +105,7 @@ class MessageReactionsModal extends StatelessWidget { showReactions: false, showUsername: false, showUserAvatar: showUserAvatar, - showReplyIndicator: false, + showThreadReplyIndicator: false, showTimestamp: false, translateUserAvatar: false, showSendingIndicator: DisplayWidget.gone, diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index c7c6acdb..4fe84311 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -99,8 +99,11 @@ class MessageWidget extends StatefulWidget { final bool allRead; - /// If true the widget will show the reply indicator - final bool showReplyIndicator; + /// If true the widget will show the thread reply indicator + final bool showThreadReplyIndicator; + + /// If true the widget will show the show in channel indicator + final bool showInChannelIndicator; /// The function called when tapping on UserAvatar final void Function(User) onUserAvatarTap; @@ -123,6 +126,7 @@ class MessageWidget extends StatefulWidget { /// Center user avatar with bottom of the message final bool translateUserAvatar; + /// MessageWidget({ Key key, @required this.message, @@ -139,7 +143,8 @@ class MessageWidget extends StatefulWidget { this.showReactionPickerIndicator = false, this.showUserAvatar = DisplayWidget.show, this.showSendingIndicator = DisplayWidget.show, - this.showReplyIndicator = true, + this.showThreadReplyIndicator = true, + this.showInChannelIndicator = true, this.onThreadTap, this.showUsername = true, this.showTimestamp = true, @@ -211,10 +216,23 @@ class MessageWidget extends StatefulWidget { } class _MessageWidgetState extends State { + bool get showThreadReplyIndicator => + widget.showThreadReplyIndicator && widget.message.replyCount > 0; + + bool get showUsername => widget.showUsername; + + bool get showTimeStamp => + widget.message.createdAt != null && widget.showTimestamp; + + bool get showReadList => widget.readList?.isNotEmpty == true; + + bool get showInChannel => + widget.showInChannelIndicator && widget.message?.showInChannel == true; + @override Widget build(BuildContext context) { var leftPadding = widget.showUserAvatar != DisplayWidget.gone - ? widget.messageTheme.avatarTheme.constraints.maxWidth + 16.0 + ? widget.messageTheme.avatarTheme.constraints.maxWidth + 14.5 : 6.0; final hasFiles = @@ -234,140 +252,151 @@ class _MessageWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.end, + Stack( + alignment: AlignmentDirectional.bottomStart, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, - children: [ - if (widget.showUserAvatar == DisplayWidget.show) - _buildUserAvatar(), - SizedBox( - width: 6, - ), - if (widget.showUserAvatar == DisplayWidget.hide) - SizedBox( - width: widget.messageTheme.avatarTheme.constraints - .maxWidth + - 8, - ), - Flexible( - child: PortalEntry( - portal: Container( - transform: Matrix4.translationValues(-16, 2, 0), - child: _buildReactionIndicator(context), - constraints: BoxConstraints(maxWidth: 22 * 6.0), - ), - portalAnchor: Alignment(-1.0, -1.0), - childAnchor: Alignment(1, -1.0), - child: Stack( - clipBehavior: Clip.none, - children: [ - Padding( - padding: widget.showReactions - ? EdgeInsets.only( - top: widget.message.reactionCounts - ?.isNotEmpty == - true - ? 18 - : 0, - ) - : EdgeInsets.zero, - child: (widget.message.isDeleted && - widget.message.status != - MessageSendingStatus - .FAILED_DELETE) - ? Transform( - alignment: Alignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.showUserAvatar == DisplayWidget.show) + _buildUserAvatar(), + SizedBox(width: 6), + if (widget.showUserAvatar == DisplayWidget.hide) + SizedBox( + width: widget.messageTheme.avatarTheme + .constraints.maxWidth + + 8, + ), + Flexible( + child: PortalEntry( + portal: Container( + transform: + Matrix4.translationValues(-16, 2, 0), + child: _buildReactionIndicator(context), + constraints: + BoxConstraints(maxWidth: 22 * 6.0), + ), + portalAnchor: Alignment(-1.0, -1.0), + childAnchor: Alignment(1, -1.0), + child: Stack( + clipBehavior: Clip.none, + children: [ + Padding( + padding: widget.showReactions + ? EdgeInsets.only( + top: widget.message.reactionCounts + ?.isNotEmpty == + true + ? 18 + : 0, + ) + : EdgeInsets.zero, + child: (widget.message.isDeleted && + widget.message.status != + MessageSendingStatus + .FAILED_DELETE) + ? Transform( + alignment: Alignment.center, + transform: Matrix4.rotationY( + widget.reverse ? pi : 0), + child: DeletedMessage( + reverse: widget.reverse, + borderRadiusGeometry: + widget.borderRadiusGeometry, + borderSide: widget.borderSide, + shape: widget.shape, + messageTheme: + widget.messageTheme, + ), + ) + : Material( + clipBehavior: Clip.antiAlias, + shape: widget.shape ?? + RoundedRectangleBorder( + side: isOnlyEmoji + ? BorderSide.none + : widget.borderSide ?? + BorderSide( + color: Theme.of(context) + .brightness == + Brightness + .dark + ? Colors.white + .withAlpha( + 24) + : Colors.black + .withAlpha( + 24), + ), + borderRadius: widget + .borderRadiusGeometry ?? + BorderRadius.zero, + ), + color: _getBackgroundColor(), + child: Padding( + padding: EdgeInsets.all( + hasFiles ? 2.0 : 0.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisSize: + MainAxisSize.min, + children: [ + ..._parseAttachments( + context), + if (widget.message.text + .trim() + .isNotEmpty && + !isGiphy) + _buildTextBubble(context), + ], + ), + ), + ), + ), + if (widget.showReactionPickerIndicator) + Positioned( + right: 0, + top: -6, + child: Transform( transform: Matrix4.rotationY( widget.reverse ? pi : 0), - child: DeletedMessage( - reverse: widget.reverse, - borderRadiusGeometry: - widget.borderRadiusGeometry, - borderSide: widget.borderSide, - shape: widget.shape, - messageTheme: widget.messageTheme, - ), - ) - : Material( - clipBehavior: Clip.antiAlias, - shape: widget.shape ?? - RoundedRectangleBorder( - side: isOnlyEmoji - ? BorderSide.none - : widget.borderSide ?? - BorderSide( - color: Theme.of(context) - .brightness == - Brightness - .dark - ? Colors.white - .withAlpha(24) - : Colors.black - .withAlpha( - 24), - ), - borderRadius: widget - .borderRadiusGeometry ?? - BorderRadius.zero, - ), - color: _getBackgroundColor(), - child: Padding( - padding: EdgeInsets.all( - hasFiles ? 2.0 : 0.0), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - ..._parseAttachments(context), - if (widget.message.text - .trim() - .isNotEmpty && - !isGiphy) - _buildTextBubble(context), - ], + child: CustomPaint( + painter: ReactionBubblePainter( + widget.messageTheme + .reactionsBackgroundColor, + widget.messageTheme + .reactionsBorderColor, ), ), ), - ), - if (widget.showReactionPickerIndicator) - Positioned( - right: 0, - top: -6, - child: Transform( - transform: Matrix4.rotationY( - widget.reverse ? pi : 0), - child: CustomPaint( - painter: ReactionBubblePainter( - widget.messageTheme - .reactionsBackgroundColor, - widget.messageTheme - .reactionsBorderColor, - ), ), - ), - ), - ], + ], + ), + ), ), - ), + ], ), + if (showThreadReplyIndicator || + showUsername || + showTimeStamp || + showInChannel) + SizedBox(height: 20.0), ], ), - if (widget.showReplyIndicator && - widget.message.replyCount > 0) - _buildReplyIndicator(leftPadding), + if (showThreadReplyIndicator || + showUsername || + showTimeStamp || + showInChannel) + _buildBottomRows(leftPadding) ], ), - if ((widget.message.createdAt != null && - widget.showTimestamp) || - widget.showUsername || - widget.readList?.isNotEmpty == true) - _buildBottomRow(leftPadding), ], ), ), @@ -376,6 +405,113 @@ class _MessageWidgetState extends State { ); } + Widget _buildBottomRows(double leftPadding) { + final deleted = widget.message.isDeleted; + var children = []; + if (deleted) { + children.add( + Row( + mainAxisSize: MainAxisSize.min, + children: [ + StreamSvgIcon.eye( + color: Colors.black.withOpacity(0.5), + size: 16.0, + ), + SizedBox(width: 8.0), + Text( + 'Only visible to you', + style: TextStyle( + color: Colors.black.withOpacity(0.5), + fontSize: 12.0, + ), + ), + ], + ), + ); + } else { + final showSendingIndicator = + widget.showSendingIndicator == DisplayWidget.show; + final replyCount = widget.message.replyCount; + final msg = showInChannel + ? 'Thread Reply' + : replyCount != 0 + ? '$replyCount ${replyCount > 1 ? 'Thread Replies' : 'Thread Reply'}' + : 'Thread Reply'; + + final onThreadTap = () async { + try { + var message = widget.message; + if (showInChannel && message.parentId != null) { + final channel = StreamChannel.of(context); + message = await channel.getMessage(widget.message.parentId); + } + return widget.onThreadTap(message); + } catch (e, stk) { + print(e); + print(stk); + return null; + } + }; + + children.addAll([ + if (showSendingIndicator) _buildSendingIndicator(), + if (showReadList) + SizedBox.fromSize( + size: Size((widget.readList.length * 10.0) + 10, 17), + child: Padding( + padding: const EdgeInsets.only(left: 4.0), + child: _buildReadIndicator(), + ), + ), + if (showThreadReplyIndicator || showInChannel) + InkWell( + onTap: widget.onThreadTap != null ? onThreadTap : null, + child: Text(msg, style: widget.messageTheme?.replies), + ), + if (showUsername) + Text( + widget.message.user.name, + style: widget.messageTheme.replies.copyWith( + color: widget.messageTheme.createdAt.color, + ), + ), + if (showTimeStamp) + Text( + Jiffy(widget.message.createdAt.toLocal()).jm, + style: widget.messageTheme.createdAt, + ), + ]); + } + if (widget.reverse) children = children.reversed.toList(); + + return Padding( + padding: EdgeInsets.only(left: leftPadding), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (!deleted && (showThreadReplyIndicator || showInChannel)) + Container( + margin: EdgeInsets.only( + bottom: widget.messageTheme.replies.fontSize / 2), + child: CustomPaint( + size: const Size(16, 32), + painter: _ThreadReplyPainter( + color: widget.messageTheme.replyThreadColor, + ), + ), + ), + ...children.map( + (child) => Transform( + transform: Matrix4.rotationY(widget.reverse ? pi : 0), + alignment: Alignment.center, + child: child, + ), + ), + ].insertBetween(const SizedBox(width: 8.0)), + ), + ); + } + Widget _buildUrlAttachment() { var urlAttachment = widget.message.attachments .firstWhere((element) => element.ogScrapeUrl != null); @@ -394,87 +530,6 @@ class _MessageWidgetState extends State { ); } - Padding _buildBottomRow(double leftPadding) { - return Padding( - padding: EdgeInsets.only( - left: leftPadding, - top: 2, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Transform( - alignment: Alignment.center, - transform: Matrix4.rotationY(widget.reverse ? pi : 0), - child: RichText( - text: TextSpan( - style: widget.messageTheme.createdAt, - children: [ - if (widget.showUsername) - TextSpan( - text: widget.message.user.name, - style: TextStyle( - fontWeight: FontWeight.bold, - color: widget.messageTheme.createdAt.color - .withOpacity(1)), - ), - if (widget.message.createdAt != null && widget.showTimestamp) - TextSpan( - text: Jiffy(widget.message.createdAt.toLocal()) - .format(' HH:mm'), - ), - ], - ), - ), - ), - if (widget.showSendingIndicator == DisplayWidget.show) - _buildSendingIndicator(), - if (widget.readList?.isNotEmpty == true) - SizedBox.fromSize( - size: Size((widget.readList.length * 10.0) + 10, 17), - child: Transform( - alignment: Alignment.center, - transform: Matrix4.rotationY(widget.reverse ? pi : 0), - child: Padding( - padding: const EdgeInsets.only(left: 4.0), - child: _buildReadIndicator(), - ), - ), - ), - if (widget.message.isDeleted) - Transform( - transform: Matrix4.rotationY(widget.reverse ? pi : 0), - alignment: Alignment.center, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - StreamSvgIcon.eye( - color: Colors.black.withOpacity(0.5), - size: 16.0, - ), - SizedBox( - width: 8.0, - ), - Text( - 'Only visible to you', - style: TextStyle( - color: Colors.black.withOpacity(0.5), - fontSize: 12.0, - ), - ), - ], - ), - ), - ), - ], - ), - ); - } - bool get isGiphy => widget.message.attachments?.any((element) => element.type == 'giphy') == true; @@ -565,7 +620,7 @@ class _MessageWidgetState extends State { true, showReactions: widget.showReactions, showReply: - widget.showReplyIndicator && widget.onThreadTap != null, + widget.showThreadReplyIndicator && widget.onThreadTap != null, ), ); }); @@ -715,38 +770,13 @@ class _MessageWidgetState extends State { return; } - Widget _buildReplyIndicator(double leftPadding) { - return Padding( - padding: EdgeInsets.only( - left: leftPadding, - ), - child: Transform( - transform: Matrix4.rotationY(widget.reverse ? pi : 0), - alignment: Alignment.center, - child: ReplyIndicator( - message: widget.message, - reversed: widget.reverse, - messageTheme: widget.messageTheme, - onTap: widget.onThreadTap != null - ? () { - widget.onThreadTap(widget.message); - } - : null, - ), - ), - ); - } - Widget _buildSendingIndicator() { - return Padding( - padding: const EdgeInsets.only(right: 4.0), - child: Transform( - transform: Matrix4.rotationY(widget.reverse ? pi : 0), - alignment: Alignment.center, - child: SendingIndicator( - message: widget.message, - allRead: widget.allRead, - ), + return Container( + height: widget.messageTheme.createdAt.fontSize + 2, + width: widget.messageTheme.createdAt.fontSize + 2, + child: SendingIndicator( + message: widget.message, + allRead: widget.allRead, ), ); } @@ -958,3 +988,32 @@ class _MessageWidgetState extends State { } } } + +class _ThreadReplyPainter extends CustomPainter { + final Color color; + + const _ThreadReplyPainter({@required this.color}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color ?? Color(0XFFDBDBDB) + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..strokeCap = StrokeCap.round; + + final path = Path() + ..moveTo(0, 0) + ..quadraticBezierTo(0, size.height * 0.38, 0, size.height * 0.50) + ..quadraticBezierTo( + 0, + size.height, + size.width, + size.height, + ); + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/lib/src/reply_indicator.dart b/lib/src/reply_indicator.dart deleted file mode 100644 index cdb1cd57..00000000 --- a/lib/src/reply_indicator.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A reply button indicator -class ReplyIndicator extends StatelessWidget { - final Message message; - final VoidCallback onTap; - final bool reversed; - final MessageTheme messageTheme; - - const ReplyIndicator({ - Key key, - this.message, - this.onTap, - this.reversed = false, - this.messageTheme, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - var row = [ - Text( - 'Replies: ${message.replyCount}', - style: messageTheme?.replies, - ), - Transform( - transform: Matrix4.rotationY(reversed ? 0 : pi), - alignment: Alignment.center, - child: Icon( - Icons.subdirectory_arrow_left, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white12 - : Colors.black12, - ), - ), - ]; - - if (!reversed) { - row = row.reversed.toList(); - } - - return GestureDetector( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: row, - ), - ), - ); - } -} diff --git a/lib/src/stream_channel.dart b/lib/src/stream_channel.dart index 001d98ff..d5a4b030 100644 --- a/lib/src/stream_channel.dart +++ b/lib/src/stream_channel.dart @@ -267,6 +267,19 @@ class StreamChannelState extends State { return state; } + /// + Future getMessage(String messageId) async { + var message = channel.state.messages.firstWhere( + (it) => it.id == messageId, + orElse: () => null, + ); + if (message == null) { + final response = await channel.getMessagesById([messageId]); + message = response.messages.first; + } + return message; + } + /// Reloads the channel with latest message Future reloadChannel() => queryAtMessage(before: 30); diff --git a/lib/src/stream_chat_theme.dart b/lib/src/stream_chat_theme.dart index 54b096d1..a0f552c4 100644 --- a/lib/src/stream_chat_theme.dart +++ b/lib/src/stream_chat_theme.dart @@ -191,6 +191,8 @@ class StreamChatThemeData { this.ownMessageTheme.messageBackgroundColor, avatarTheme: ownMessageTheme?.avatarTheme ?? this.ownMessageTheme.avatarTheme, + replyThreadColor: ownMessageTheme?.replyThreadColor ?? + this.ownMessageTheme.replyThreadColor, ) ?? this.ownMessageTheme, otherMessageTheme: otherMessageTheme?.copyWith( @@ -209,6 +211,8 @@ class StreamChatThemeData { this.otherMessageTheme.messageBackgroundColor, avatarTheme: otherMessageTheme?.avatarTheme ?? this.otherMessageTheme.avatarTheme, + replyThreadColor: ownMessageTheme?.replyThreadColor ?? + this.ownMessageTheme.replyThreadColor, ) ?? this.otherMessageTheme, reactionIcons: reactionIcons ?? this.reactionIcons, @@ -297,16 +301,17 @@ class StreamChatThemeData { color: isDark ? Colors.white.withOpacity(.5) : Colors.black.withOpacity(.5), - fontSize: 11, + fontSize: 12, ), replies: TextStyle( color: accentColor, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, fontSize: 12, ), messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA), reactionsBackgroundColor: isDark ? Colors.black : Colors.white, reactionsBorderColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA), + replyThreadColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA), avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: BoxConstraints.tightFor( @@ -330,17 +335,19 @@ class StreamChatThemeData { color: isDark ? Colors.white.withOpacity(.5) : Colors.black.withOpacity(.5), - fontSize: 11, + fontSize: 12, ), replies: TextStyle( color: accentColor, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, fontSize: 12, ), messageLinks: TextStyle( color: accentColor, ), messageBackgroundColor: isDark ? Colors.black : Colors.white, + replyThreadColor: + isDark ? Colors.white.withAlpha(24) : Colors.black.withAlpha(24), avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), constraints: BoxConstraints.tightFor( @@ -455,6 +462,7 @@ class MessageTheme { final Color messageBackgroundColor; final Color reactionsBackgroundColor; final Color reactionsBorderColor; + final Color replyThreadColor; final AvatarTheme avatarTheme; const MessageTheme({ @@ -465,6 +473,7 @@ class MessageTheme { this.messageBackgroundColor, this.reactionsBackgroundColor, this.reactionsBorderColor, + this.replyThreadColor, this.avatarTheme, this.createdAt, }); @@ -479,6 +488,7 @@ class MessageTheme { AvatarTheme avatarTheme, Color reactionsBackgroundColor, Color reactionsBorderColor, + Color replyThreadColor, }) => MessageTheme( messageText: messageText ?? this.messageText, @@ -492,6 +502,7 @@ class MessageTheme { reactionsBackgroundColor: reactionsBackgroundColor ?? this.reactionsBackgroundColor, reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, + replyThreadColor: replyThreadColor ?? this.replyThreadColor, ); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 74b1da64..681a28aa 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -92,3 +92,12 @@ Future showConfirmationDialog( /// Get random png with initials String getRandomPicUrl(User user) => 'https://getstream.io/random_png/?id=${user.id}&name=${user.name}'; + +/// List extension +extension ListX on List { + /// Insert any item inBetween the list items + List insertBetween(T item) => expand((e) sync* { + yield item; + yield e; + }).skip(1).toList(growable: false); +} diff --git a/lib/stream_chat_flutter.dart b/lib/stream_chat_flutter.dart index 5dd46fa3..1cef26a6 100644 --- a/lib/stream_chat_flutter.dart +++ b/lib/stream_chat_flutter.dart @@ -21,7 +21,6 @@ export 'src/message_list_view.dart'; export 'src/message_text.dart'; export 'src/message_widget.dart'; export 'src/reaction_picker.dart'; -export 'src/reply_indicator.dart'; export 'src/sending_indicator.dart'; export 'src/stream_channel.dart'; export 'src/stream_chat.dart';