|
|
@ -1,3 +1,4 @@ |
|
|
|
|
|
import 'package:dating_touchme_app/im/im_manager.dart'; |
|
|
import 'package:dating_touchme_app/pages/message/chat_page.dart'; |
|
|
import 'package:dating_touchme_app/pages/message/chat_page.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:dating_touchme_app/generated/assets.dart'; |
|
|
import 'package:dating_touchme_app/generated/assets.dart'; |
|
|
@ -71,7 +72,7 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
itemCount: filteredConversations.length, |
|
|
itemCount: filteredConversations.length, |
|
|
itemBuilder: (context, index) { |
|
|
itemBuilder: (context, index) { |
|
|
final conversation = filteredConversations[index]; |
|
|
final conversation = filteredConversations[index]; |
|
|
return _buildConversationItem(conversation); |
|
|
|
|
|
|
|
|
return BuildConversationItem(conversation: conversation); |
|
|
}, |
|
|
}, |
|
|
); |
|
|
); |
|
|
}); |
|
|
}); |
|
|
@ -81,14 +82,49 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 构建会话项 |
|
|
|
|
|
Widget _buildConversationItem(EMConversation conversation) { |
|
|
|
|
|
// 优先从缓存获取用户信息,避免闪烁 |
|
|
|
|
|
final cachedUserInfo = controller.getCachedUserInfo(conversation.id); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
bool get wantKeepAlive => true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BuildConversationItem extends StatefulWidget { |
|
|
|
|
|
final EMConversation conversation; |
|
|
|
|
|
const BuildConversationItem({super.key, required this.conversation}); |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
State<BuildConversationItem> createState() => _BuildConversationItemState(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class _BuildConversationItemState extends State<BuildConversationItem> { |
|
|
|
|
|
|
|
|
|
|
|
late EMConversation conversation; |
|
|
|
|
|
|
|
|
|
|
|
bool isOnline = false; |
|
|
|
|
|
late var cachedUserInfo; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final ConversationController controller = Get.find<ConversationController>(); |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
void initState() { |
|
|
|
|
|
super.initState(); |
|
|
|
|
|
conversation = widget.conversation; |
|
|
|
|
|
cachedUserInfo = controller.getCachedUserInfo(conversation.id); |
|
|
|
|
|
IMManager.instance.getUserPresenceStatus(conversation.id).then((e){ |
|
|
|
|
|
isOnline = e == true; |
|
|
|
|
|
setState(() { |
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
return FutureBuilder<ExtendedUserInfo?>( |
|
|
return FutureBuilder<ExtendedUserInfo?>( |
|
|
future: cachedUserInfo != null |
|
|
|
|
|
? Future.value(cachedUserInfo) |
|
|
|
|
|
|
|
|
future: cachedUserInfo != null |
|
|
|
|
|
? Future.value(cachedUserInfo) |
|
|
: controller.loadContact(conversation.id), |
|
|
: controller.loadContact(conversation.id), |
|
|
initialData: cachedUserInfo, |
|
|
initialData: cachedUserInfo, |
|
|
builder: (context, userSnapshot) { |
|
|
builder: (context, userSnapshot) { |
|
|
@ -126,41 +162,57 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
child: Row( |
|
|
child: Row( |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
children: [ |
|
|
children: [ |
|
|
Container( |
|
|
|
|
|
width: 56, |
|
|
|
|
|
height: 56, |
|
|
|
|
|
decoration: BoxDecoration( |
|
|
|
|
|
borderRadius: BorderRadius.circular(28), |
|
|
|
|
|
color: Colors.grey[300], |
|
|
|
|
|
), |
|
|
|
|
|
child: ClipRRect( |
|
|
|
|
|
borderRadius: BorderRadius.circular(28), |
|
|
|
|
|
child: (userInfo?.avatarUrl != null && userInfo!.avatarUrl!.isNotEmpty) |
|
|
|
|
|
? CachedNetworkImage( |
|
|
|
|
|
imageUrl: userInfo.avatarUrl!, |
|
|
|
|
|
|
|
|
Stack( |
|
|
|
|
|
children: [ |
|
|
|
|
|
Container( |
|
|
|
|
|
width: 56, |
|
|
|
|
|
height: 56, |
|
|
|
|
|
decoration: BoxDecoration( |
|
|
|
|
|
borderRadius: BorderRadius.circular(28), |
|
|
|
|
|
color: Colors.grey[300], |
|
|
|
|
|
), |
|
|
|
|
|
child: ClipRRect( |
|
|
|
|
|
borderRadius: BorderRadius.circular(28), |
|
|
|
|
|
child: (userInfo?.avatarUrl != null && userInfo!.avatarUrl!.isNotEmpty) |
|
|
|
|
|
? CachedNetworkImage( |
|
|
|
|
|
imageUrl: userInfo.avatarUrl!, |
|
|
|
|
|
width: 56, |
|
|
|
|
|
height: 56, |
|
|
|
|
|
fit: BoxFit.cover, |
|
|
|
|
|
placeholder: (context, url) => Image.asset( |
|
|
|
|
|
Assets.imagesUserAvatar, |
|
|
width: 56, |
|
|
width: 56, |
|
|
height: 56, |
|
|
height: 56, |
|
|
fit: BoxFit.cover, |
|
|
fit: BoxFit.cover, |
|
|
placeholder: (context, url) => Image.asset( |
|
|
|
|
|
Assets.imagesUserAvatar, |
|
|
|
|
|
width: 56, |
|
|
|
|
|
height: 56, |
|
|
|
|
|
fit: BoxFit.cover, |
|
|
|
|
|
), |
|
|
|
|
|
errorWidget: (context, url, error) => Image.asset( |
|
|
|
|
|
Assets.imagesUserAvatar, |
|
|
|
|
|
width: 56, |
|
|
|
|
|
height: 56, |
|
|
|
|
|
fit: BoxFit.cover, |
|
|
|
|
|
), |
|
|
|
|
|
) |
|
|
|
|
|
: Image.asset( |
|
|
|
|
|
|
|
|
), |
|
|
|
|
|
errorWidget: (context, url, error) => Image.asset( |
|
|
Assets.imagesUserAvatar, |
|
|
Assets.imagesUserAvatar, |
|
|
width: 56, |
|
|
width: 56, |
|
|
height: 56, |
|
|
height: 56, |
|
|
fit: BoxFit.cover, |
|
|
fit: BoxFit.cover, |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
: Image.asset( |
|
|
|
|
|
Assets.imagesUserAvatar, |
|
|
|
|
|
width: 56, |
|
|
|
|
|
height: 56, |
|
|
|
|
|
fit: BoxFit.cover, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
if(isOnline) Positioned( |
|
|
|
|
|
bottom: 0, |
|
|
|
|
|
right: 0, |
|
|
|
|
|
child: Container( |
|
|
|
|
|
width: 11.w, |
|
|
|
|
|
height: 11.w, |
|
|
|
|
|
decoration: BoxDecoration( |
|
|
|
|
|
borderRadius: BorderRadius.all(Radius.circular(11.w)), |
|
|
|
|
|
color: const Color.fromRGBO(43, 255, 191, 1) |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
) |
|
|
|
|
|
], |
|
|
), |
|
|
), |
|
|
const SizedBox(width: 12), |
|
|
const SizedBox(width: 12), |
|
|
Expanded( |
|
|
Expanded( |
|
|
@ -169,7 +221,7 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
children: [ |
|
|
children: [ |
|
|
Row( |
|
|
Row( |
|
|
mainAxisAlignment: |
|
|
mainAxisAlignment: |
|
|
MainAxisAlignment.spaceBetween, |
|
|
|
|
|
|
|
|
MainAxisAlignment.spaceBetween, |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
children: [ |
|
|
children: [ |
|
|
Expanded( |
|
|
Expanded( |
|
|
@ -246,16 +298,20 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 构建最后一条消息内容(支持表情显示) |
|
|
// 构建最后一条消息内容(支持表情显示) |
|
|
Widget _buildLastMessageContent(String content, EMMessage? message) { |
|
|
Widget _buildLastMessageContent(String content, EMMessage? message) { |
|
|
// 检查是否是发送失败的消息 |
|
|
// 检查是否是发送失败的消息 |
|
|
final isFailed = message != null && |
|
|
|
|
|
message.direction == MessageDirection.SEND && |
|
|
|
|
|
message.status == MessageStatus.FAIL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final isFailed = message != null && |
|
|
|
|
|
message.direction == MessageDirection.SEND && |
|
|
|
|
|
message.status == MessageStatus.FAIL; |
|
|
|
|
|
|
|
|
// 检查是否包含表情 |
|
|
// 检查是否包含表情 |
|
|
final containsEmoji = EmojiTextHelper.containsEmoji(content); |
|
|
final containsEmoji = EmojiTextHelper.containsEmoji(content); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (containsEmoji && !isFailed) { |
|
|
if (containsEmoji && !isFailed) { |
|
|
// 如果包含表情且不是失败消息,使用自定义的单行表情文本 widget |
|
|
// 如果包含表情且不是失败消息,使用自定义的单行表情文本 widget |
|
|
return _buildSingleLineEmojiText( |
|
|
return _buildSingleLineEmojiText( |
|
|
@ -281,6 +337,8 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 构建单行表情文本(支持省略) |
|
|
// 构建单行表情文本(支持省略) |
|
|
Widget _buildSingleLineEmojiText(String text, TextStyle textStyle) { |
|
|
Widget _buildSingleLineEmojiText(String text, TextStyle textStyle) { |
|
|
final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]'); |
|
|
final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]'); |
|
|
@ -333,7 +391,5 @@ class _ConversationTabState extends State<ConversationTab> |
|
|
overflow: TextOverflow.ellipsis, |
|
|
overflow: TextOverflow.ellipsis, |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
bool get wantKeepAlive => true; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|