diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 9646608c..f47aa3ed 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -2311,75 +2311,78 @@ class _PickerWidgetState extends State<_PickerWidget> { if (widget.filePickerIndex != 0) { return const Offstage(); } - return FutureBuilder( - future: requestPermission, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center(child: CircularProgressIndicator()); - } - - if (snapshot.data!) { - if (widget.containsFile) { - return GestureDetector( - onTap: () { - widget.onAddMoreFilesClick(DefaultAttachmentTypes.file); - }, - child: Container( - constraints: const BoxConstraints.expand(), - color: widget.streamChatTheme.colorTheme.inputBg, - alignment: Alignment.center, - child: Text( - context.translations.addMoreFilesLabel, - style: TextStyle( - color: widget.streamChatTheme.colorTheme.accentPrimary, - fontWeight: FontWeight.bold, - ), - ), - ), - ); + return RepaintBoundary( + child: FutureBuilder( + future: requestPermission, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); } - return MediaListView( - selectedIds: widget.selectedMedias, - onSelect: widget.onMediaSelected, - ); - } - return InkWell( - onTap: () async { - PhotoManager.openSetting(); - }, - child: Container( - color: widget.streamChatTheme.colorTheme.inputBg, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SvgPicture.asset( - 'svgs/icon_picture_empty_state.svg', - package: 'stream_chat_flutter', - height: 140, - color: widget.streamChatTheme.colorTheme.disabled, - ), - Text( - context.translations.enablePhotoAndVideoAccessMessage, - style: widget.streamChatTheme.textTheme.body.copyWith( - color: widget.streamChatTheme.colorTheme.textLowEmphasis), - textAlign: TextAlign.center, - ), - const SizedBox(height: 6), - Center( + if (snapshot.data!) { + if (widget.containsFile) { + return GestureDetector( + onTap: () { + widget.onAddMoreFilesClick(DefaultAttachmentTypes.file); + }, + child: Container( + constraints: const BoxConstraints.expand(), + color: widget.streamChatTheme.colorTheme.inputBg, + alignment: Alignment.center, child: Text( - context.translations.allowGalleryAccessMessage, - style: widget.streamChatTheme.textTheme.bodyBold.copyWith( + context.translations.addMoreFilesLabel, + style: TextStyle( color: widget.streamChatTheme.colorTheme.accentPrimary, + fontWeight: FontWeight.bold, ), ), ), - ], + ); + } + return MediaListView( + selectedIds: widget.selectedMedias, + onSelect: widget.onMediaSelected, + ); + } + + return InkWell( + onTap: () async { + PhotoManager.openSetting(); + }, + child: Container( + color: widget.streamChatTheme.colorTheme.inputBg, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SvgPicture.asset( + 'svgs/icon_picture_empty_state.svg', + package: 'stream_chat_flutter', + height: 140, + color: widget.streamChatTheme.colorTheme.disabled, + ), + Text( + context.translations.enablePhotoAndVideoAccessMessage, + style: widget.streamChatTheme.textTheme.body.copyWith( + color: + widget.streamChatTheme.colorTheme.textLowEmphasis), + textAlign: TextAlign.center, + ), + const SizedBox(height: 6), + Center( + child: Text( + context.translations.allowGalleryAccessMessage, + style: widget.streamChatTheme.textTheme.bodyBold.copyWith( + color: widget.streamChatTheme.colorTheme.accentPrimary, + ), + ), + ), + ], + ), ), - ), - ); - }, + ); + }, + ), ); } } diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index ab4ae04f..25d10bb1 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -455,7 +455,7 @@ class _MessageListViewState extends State { onInBetweenOfPage: () { _inBetweenList = true; }, - child: ScrollablePositionedList.separated( + child: ScrollablePositionedList.builder( key: _upToDate ? null : ValueKey(initialIndex + initialAlignment), @@ -465,8 +465,13 @@ class _MessageListViewState extends State { physics: widget.scrollPhysics, itemScrollController: _scrollController, reverse: widget.reverse, - addAutomaticKeepAlives: false, - itemCount: itemCount, + itemCount: messages.length, + findChildIndexCallback: (Key key) { + final String data = (key as ValueKey).value; + final index = + messages.indexWhere((element) => element.id == data); + return index + 1; + }, // Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages) // eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)| @@ -486,72 +491,74 @@ class _MessageListViewState extends State { // Separator(Footer -> 8??30) -> 0 (count-8) // Footer -> 0 (count-8) - separatorBuilder: (context, i) { - if (i == itemCount - 2) { - if (widget.parentMessage == null) { - return const Offstage(); - } - return _buildThreadSeparator(); - } - if (i == itemCount - 3) { - if (widget.headerBuilder == null) { - if (_isThreadConversation) return const Offstage(); - return const SizedBox(height: 52); - } - return const SizedBox(height: 8); - } - if (i == 0) { - if (widget.footerBuilder == null) { - return const SizedBox(height: 30); - } - return const SizedBox(height: 8); - } + // separatorBuilder: (context, i) { + // if (i == itemCount - 2) { + // if (widget.parentMessage == null) { + // return const Offstage(); + // } + // return _buildThreadSeparator(); + // } + // if (i == itemCount - 3) { + // if (widget.headerBuilder == null) { + // if (_isThreadConversation) return const Offstage(); + // return const SizedBox(height: 52); + // } + // return const SizedBox(height: 8); + // } + // if (i == 0) { + // if (widget.footerBuilder == null) { + // return const SizedBox(height: 30); + // } + // return const SizedBox(height: 8); + // } - if (i == 1 || i == itemCount - 4) return const Offstage(); + // if (i == 1 || i == itemCount - 4) return const Offstage(); - late final Message message, nextMessage; - if (widget.reverse) { - message = messages[i - 1]; - nextMessage = messages[i - 2]; - } else { - message = messages[i - 2]; - nextMessage = messages[i - 1]; - } - if (!Jiffy(message.createdAt.toLocal()).isSame( - nextMessage.createdAt.toLocal(), - Units.DAY, - )) { - final divider = widget.dateDividerBuilder != null - ? widget.dateDividerBuilder!( - nextMessage.createdAt.toLocal(), - ) - : DateDivider( - dateTime: nextMessage.createdAt.toLocal(), - ); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: divider, - ); - } - final timeDiff = - Jiffy(nextMessage.createdAt.toLocal()).diff( - message.createdAt.toLocal(), - Units.MINUTE, - ); + // late final Message message, nextMessage; + // if (widget.reverse) { + // message = messages[i - 1]; + // nextMessage = messages[i - 2]; + // } else { + // message = messages[i - 2]; + // nextMessage = messages[i - 1]; + // } + // if (!Jiffy(message.createdAt.toLocal()).isSame( + // nextMessage.createdAt.toLocal(), + // Units.DAY, + // )) { + // final divider = widget.dateDividerBuilder != null + // ? widget.dateDividerBuilder!( + // nextMessage.createdAt.toLocal(), + // ) + // : DateDivider( + // dateTime: nextMessage.createdAt.toLocal(), + // ); + // return Padding( + // padding: const EdgeInsets.symmetric(vertical: 12), + // child: divider, + // ); + // } + // final timeDiff = + // Jiffy(nextMessage.createdAt.toLocal()).diff( + // message.createdAt.toLocal(), + // Units.MINUTE, + // ); - final isNextUserSame = - message.user!.id == nextMessage.user?.id; - final isThread = message.replyCount! > 0; - final isDeleted = message.isDeleted; - if (timeDiff >= 1 || - !isNextUserSame || - isThread || - isDeleted) { - return const SizedBox(height: 8); - } - return const SizedBox(height: 2); - }, + // final isNextUserSame = + // message.user!.id == nextMessage.user?.id; + // final isThread = message.replyCount! > 0; + // final isDeleted = message.isDeleted; + // if (timeDiff >= 1 || + // !isNextUserSame || + // isThread || + // isDeleted) { + // return const SizedBox(height: 8); + // } + // return const SizedBox(height: 2); + // }, itemBuilder: (context, i) { + // final myMessage = messages[i]; + // return buildMessage(myMessage, messages, i, i); if (i == itemCount - 1) { if (widget.parentMessage == null) { return const Offstage(); @@ -597,7 +604,7 @@ class _MessageListViewState extends State { i - 2, ); } else { - messageWidget = buildMessage(message, messages, i - 2); + messageWidget = buildMessage(message, messages, i - 2, i); } return messageWidget; }, @@ -915,16 +922,13 @@ class _MessageListViewState extends State { return defaultMessageWidget; } - Widget buildMessage( - Message message, - List messages, - int index, - ) { + Widget buildMessage(Message message, List messages, int index, + [int? actualIndex]) { if ((message.type == 'system' || message.type == 'error') && message.text?.isNotEmpty == true) { return widget.systemMessageBuilder?.call(context, message) ?? SystemMessage( - key: ValueKey('MESSAGE-${message.id}'), + key: ValueKey(message.id), message: message, onMessageTap: (message) { if (widget.onSystemMessageTap != null) { @@ -1004,7 +1008,8 @@ class _MessageListViewState extends State { members.firstWhereOrNull((e) => e.user!.id == currentUser!.id); Widget messageWidget = MessageWidget( - key: ValueKey('MESSAGE-${message.id}'), + key: ValueKey(message.id), + actualIndex: actualIndex, message: message, reverse: isMyMessage, showReactions: !message.isDeleted, diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 7619f21b..87f02b7c 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -74,6 +74,7 @@ class MessageWidget extends StatefulWidget { this.showInChannelIndicator = false, this.onReplyTap, this.onThreadTap, + this.actualIndex, this.showUsername = true, this.showTimestamp = true, this.showReactions = true, @@ -257,6 +258,8 @@ class MessageWidget extends StatefulWidget { }..addAll(customAttachmentBuilders ?? {}), super(key: key); + final int? actualIndex; + /// Function called on mention tap final void Function(User)? onMentionTap; @@ -590,6 +593,10 @@ class _MessageWidgetState extends State @override Widget build(BuildContext context) { super.build(context); + return Container( + decoration: BoxDecoration(border: Border.all()), + child: Text('${widget.message.text ?? 'valu'} ${widget.actualIndex}'), + ); final avatarWidth = widget.messageTheme.avatarTheme?.constraints.maxWidth ?? 40; final bottomRowPadding = @@ -658,6 +665,7 @@ class _MessageWidgetState extends State SizedBox(width: avatarWidth + 4), Flexible( child: PortalEntry( + visible: false, portal: Container( transform: Matrix4.translationValues( widget.reverse ? 12 : -12, 0, 0),