chatapp3-flutter/lib/modules/chat/message_chat_page.dart
2026-04-14 14:50:12 +08:00

2105 lines
80 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'dart:io';
import 'package:extended_image/extended_image.dart'
show ExtendedImage, ExtendedRawImage, ExtendedImageState, LoadState;
import 'package:extended_text/extended_text.dart';
import 'package:extended_text_field/extended_text_field.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/app/constants/sc_emoji_datas.dart';
import 'package:yumi/shared/tools/sc_date_utils.dart';
import 'package:yumi/shared/tools/sc_loading_manager.dart';
import 'package:yumi/shared/tools/sc_message_utils.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
import 'package:yumi/services/audio/rtm_manager.dart';
import 'package:marquee/marquee.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_custom_elem.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_elem.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_image_elem.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_extension.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_online_url.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_text_elem.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_video_elem.dart';
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
import 'package:yumi/config/pickImage.dart';
import 'package:yumi/ui_kit/components/dialog/dialog_base.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/app/config/business_logic_strategy.dart';
import 'package:yumi/app/constants/sc_screen.dart';
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
import 'package:yumi/shared/tools/sc_keybord_util.dart';
import 'package:yumi/shared/tools/sc_message_notifier.dart';
import 'package:yumi/shared/tools/sc_path_utils.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/business_logic/usecases/custom_tab_selector.dart';
import 'package:yumi/services/auth/user_profile_manager.dart';
import 'package:yumi/modules/index/main_route.dart';
import '../../shared/business_logic/models/res/sc_user_red_packet_send_res.dart';
class SCMessageChatPage extends StatefulWidget {
final V2TimConversation? conversation;
final bool shrinkWrap;
//是否是房间内聊天界面。
final bool inRoom;
const SCMessageChatPage({
Key? key,
this.conversation,
this.shrinkWrap = false,
this.inRoom = false,
}) : super(key: key);
@override
_SCMessageChatPageState createState() => _SCMessageChatPageState();
}
class _SCMessageChatPageState extends State<SCMessageChatPage> {
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
final TextEditingController _textController = TextEditingController();
final ScrollController _scrollController = ScrollController();
final RefreshController _refreshController = RefreshController();
// FlutterSoundPlayer? flutterSound;
RtmProvider? rtmProvider;
V2TimConversation? currentConversation;
List<V2TimMessage> currentConversationMessageList = [];
final FocusNode _focusNode = FocusNode();
///是否显示工具栏
bool showTools = false;
bool showEmoji = false;
bool showDoNotClickUnfamiliarTips = true;
///互相关注才能互相发送消息
bool canSendMsg = false;
SocialChatUserProfile? friend;
///是否显示发送按钮
bool showSend = false;
List<String> coinsTitles = ["100", "1000", "10000", "50000"];
String selecteCoins = "100";
@override
void initState() {
super.initState();
SCMessageNotifier.canPlay = false;
SCMessageUtils.redPacketFutureCache.clear();
// flutterSound = FlutterSoundPlayer();
rtmProvider = Provider.of<RtmProvider>(context, listen: false);
currentConversation = widget.conversation;
rtmProvider?.onMessageRecvC2CReadListener = _onMessageRecvC2CRead;
rtmProvider?.onRevokeMessageListener = _onRevokeMessage;
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
loadMsg();
if (friend == null) {
loadFriend();
}
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
setState(() {
showTools = false;
showEmoji = false;
});
}
});
}
@override
void dispose() {
rtmProvider?.onMessageRecvC2CReadListener = null;
rtmProvider?.onRevokeMessageListener = null;
rtmProvider?.onNewMessageCurrentConversationListener = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return Consumer<RtmProvider>(
builder: (_, provider, __) {
return WillPopScope(
onWillPop: () {
if (showEmoji || showTools) {
setState(() {
showEmoji = false;
showTools = false;
});
return Future.value(false);
}
SCNavigatorUtils.goBack(context);
return Future.value(true);
},
child: Stack(
children: [
if (!widget.inRoom)
// 渐变背景
Positioned(
left: 0,
right: 0,
top: 0,
child: Column(
children: [
SizedBox(
width: ScreenUtil().screenWidth,
height: ScreenUtil().screenHeight,
child: Image.asset(
_strategy.getSCMessageChatPageRoomSettingBackground(),
fit: BoxFit.fill,
),
),
],
),
),
Scaffold(
//backgroundColor: widget.inRoom ? Colors.transparent: Color(0xfff8f8f8),
backgroundColor: Colors.transparent,
// appBar: _appBar(),
appBar: null,
body: SafeArea(
top: widget.shrinkWrap ? false : true,
bottom: false,
child: Column(
children: <Widget>[
Row(
children: [
!widget.inRoom
? GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.pop(context);
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 10.w,
),
child: Icon(
SCGlobalConfig.lang == "ar"
? Icons.keyboard_arrow_right
: Icons.keyboard_arrow_left,
size: 28.w,
color: Colors.white,
),
),
)
: Container(),
SizedBox(width: 5.w),
Expanded(
child: Container(
alignment: AlignmentDirectional.center,
child: socialchatNickNameText(
friend?.userNickname ?? "",
fontSize: 14.sp,
fontWeight: FontWeight.w500,
textColor: Colors.white,
type: "",
needScroll:
(friend?.userNickname?.characters.length ??
0) >
22,
),
),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SCNavigatorUtils.push(
context,
replace: true,
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}",
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Icon(
Icons.more_vert,
size: 22.w,
color: Colors.white,
),
),
),
],
),
Expanded(child: _msgList()),
Container(
decoration: BoxDecoration(
//color: Colors.white,
//border: Border(top: BorderSide(color: Color(0xfff1f1f1)))
),
child: SafeArea(
top: false,
child: Column(
children: <Widget>[
if (!SCGlobalConfig.isSystemConversationId(
currentConversation?.conversationID,
))
_input(),
_tools(provider),
_emoji(),
// _fahongbao(),
],
),
),
),
],
),
),
),
],
),
);
},
);
}
void loadFriend() {
if (SCGlobalConfig.isSystemConversationId(currentConversation?.conversationID) ||
SCGlobalConfig.isSystemUserId(currentConversation?.userID)) {
return;
}
SCLoadingManager.show();
Future.wait([
SCAccountRepository().loadUserInfo("${currentConversation?.userID}"),
SCAccountRepository().friendRelationCheck(
"${currentConversation?.userID}",
),
])
.then((result) {
SCLoadingManager.hide();
friend = result[0] as SocialChatUserProfile;
canSendMsg = result[1] as bool;
setState(() {});
})
.catchError((e) {
SCLoadingManager.hide();
});
}
///消息列表
Widget _msgList() {
print('xiaoxiliebiao:${currentConversationMessageList.length}');
return SmartRefresher(
enablePullDown: false,
enablePullUp: true,
onLoading: () async {
print('onLoading');
if (currentConversationMessageList.isNotEmpty) {
// 拉取单聊历史消息
// 首次拉取lastMsgID 设置为 null
// 再次拉取时lastMsgID 可以使用返回的消息列表中的最后一条消息的id
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback =
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.getC2CHistoryMessageList(
userID: currentConversation!.userID ?? "",
count: 30,
lastMsgID: currentConversationMessageList.last.msgID,
);
List<V2TimMessage> messages =
v2timValueCallback.data as List<V2TimMessage>;
print('加载前:${currentConversationMessageList.length}');
currentConversationMessageList.addAll(messages);
print('加载后:${currentConversationMessageList.length}');
if (messages.length == 30) {
_refreshController.loadComplete();
} else {
_refreshController.loadNoData();
}
} else {
_refreshController.loadNoData();
}
setState(() {});
},
footer: CustomFooter(
height: 1,
builder: (context, mode) {
return SizedBox(
height: 1,
width: 1,
child: SizedBox(height: 1, width: 1),
);
},
),
controller: _refreshController,
child: CustomScrollView(
shrinkWrap: true,
controller: _scrollController,
reverse: true,
physics: const ClampingScrollPhysics(),
// 禁用回弹效果
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(c, i) => _MessageItem(
message: currentConversationMessageList[i],
preMessage:
i < currentConversationMessageList.length - 1
? currentConversationMessageList[i + 1]
: null,
isSystem:
currentConversationMessageList[i].sender == "administrator",
friend: friend,
currentConversationMessageList: currentConversationMessageList,
updateCall: () {
setState(() {});
},
),
childCount: currentConversationMessageList.length,
),
),
],
),
);
}
Future<void> loadMsg() async {
// 1. 在初始化时,添加高级消息监听器
TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(
listener: V2TimAdvancedMsgListener(
// 监听消息扩展更新
onRecvMessageExtensionsChanged: (msgID, extensions) {
for (var ext in extensions) {
if (ext.extensionKey == "packetID") {
String packetID = ext.extensionValue;
SCMessageUtils.redPacketFutureCache[packetID] =
SCAccountRepository().userRedPacketDetail(packetID);
setState(() {});
break;
}
}
},
),
);
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback =
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.getC2CHistoryMessageList(
userID: currentConversation!.userID!,
count: 100,
lastMsgID: null,
);
List<V2TimMessage> messages = v2timValueCallback.data!;
// List<V2TimMessage> messages = await FTIM.getMessageManager().getMessages(conversation: currentConversation);
print("messages : ${messages?.length ?? 0}");
currentConversationMessageList ??= [];
currentConversationMessageList?.clear();
for (var msg in messages) {
if (!msg.isSelf! && msg.isPeerRead!) {
TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.sendMessageReadReceipts(messageIDList: [msg.msgID!]);
}
}
currentConversationMessageList.insertAll(0, messages ?? []);
setState(() {});
}
///输入栏
Widget _input() {
return Consumer<SocialChatUserProfileManager>(
builder:
(_, provider, __) => Container(
color: Colors.transparent,
height: 56.w,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(width: 15.w),
Expanded(
child: Container(
height: 34.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(52.w),
color: Color(0xff18F2B1).withOpacity(0.1),
),
child: Row(
children: [
Expanded(
child: ExtendedTextField(
controller: _textController,
focusNode: _focusNode,
onChanged: (s) {
setState(() {
showSend = _textController.text.isNotEmpty;
});
},
onSubmitted: (s) {
if (s.isNotEmpty) {
sendMsg(s);
showSend = false;
setState(() {});
_scrollController.jumpTo(0.0);
_textController.clear();
}
},
decoration: InputDecoration(
hintText:
SCAppLocalizations.of(
context,
)!.clickHereToStartChatting,
hintStyle: TextStyle(
color: Color(0xff999999),
fontSize: 14.sp,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 15,
),
counterText: '',
isDense: true,
filled: false,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
),
style: TextStyle(
fontSize: ScreenUtil().setSp(14),
color: Colors.white,
),
),
),
//原来的emoji按钮
//SizedBox(width: 10.w,),
],
),
),
),
SizedBox(width: 10.w),
!showEmoji
? GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SCKeybordUtil.hide(context);
setState(() {
if (showTools) {
showTools = false;
}
showEmoji = !showEmoji;
});
},
child: Image.asset(
_strategy.getSCMessageChatPageEmojiIcon(),
width: 24.w,
color: Colors.white,
fit: BoxFit.fill,
),
)
: Container(),
showEmoji
? GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SCKeybordUtil.hide(context);
setState(() {
if (showTools) {
showTools = false;
}
showEmoji = !showEmoji;
});
},
child: Image.asset(
_strategy.getSCMessageChatPageChatKeyboardIcon(),
width: 24.w,
color: Colors.white,
fit: BoxFit.fill,
),
)
: Container(),
if (!showSend) SizedBox(width: 12.w),
if (showSend) SizedBox(width: 5.w),
Visibility(
visible: !showSend,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SCKeybordUtil.hide(context);
setState(() {
if (showEmoji) {
showEmoji = false;
}
showTools = !showTools;
});
},
child: Image.asset(
_strategy.getSCMessageChatPageAddIcon(),
width: 24.w,
color: Colors.white,
fit: BoxFit.fill,
),
),
),
Visibility(
visible: showSend,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
_scrollController.jumpTo(0.0);
if (_textController.text.isNotEmpty) {
SCKeybordUtil.hide(context);
sendMsg(_textController.text);
showSend = false;
setState(() {});
_textController.clear();
}
},
child: Image.asset(
_strategy.getSCMessageChatPageSendMessageIcon(),
height: 22.w,
color: Colors.white,
width: 32.w,
),
),
),
SizedBox(width: 15.w),
],
),
),
);
}
///发消息
sendMsg(String msg) async {
if (!canSendMsg) {
SCTts.show(SCAppLocalizations.of(context)!.canSendMsgTips);
return;
}
Provider.of<RtmProvider>(
context,
listen: false,
).sendC2CTextMsg(msg, currentConversation!);
}
///发消息
sendCustomMsg(String msg, String extension) async {
if (!canSendMsg) {
SCTts.show(SCAppLocalizations.of(context)!.canSendMsgTips);
return;
}
Provider.of<RtmProvider>(
context,
listen: false,
).sendC2CCustomMsg(msg, currentConversation!, extension);
}
///工具栏
Widget _tools(RtmProvider provider) {
return Visibility(
visible: showTools,
child: Container(
color: Colors.white,
padding: EdgeInsets.only(bottom: 18.w, top: 5.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(width: 20.w),
Expanded(
child: _toolsItem(
_strategy.getSCMessageChatPageCameraIcon(),
SCAppLocalizations.of(context)!.camera,
() async {
if (!canSendMsg) {
SCTts.show(SCAppLocalizations.of(context)!.canSendMsgTips);
return;
}
var pick = await ImagePick.pickFromCamera(context);
if (pick != null) {
provider.sendImageMsg(
file: pick,
conversation: currentConversation!,
);
}
},
),
),
Expanded(
child: _toolsItem(
_strategy.getSCMessageChatPagePictureIcon(),
SCAppLocalizations.of(context)!.album,
() async {
if (!canSendMsg) {
SCTts.show(SCAppLocalizations.of(context)!.canSendMsgTips);
return;
}
final List<File>? result =
await ImagePick.pickPicAndVideoFromGallery(context);
if (result == null) return; // 用户取消选择
if (result.isNotEmpty) {
provider.sendImageMsg(
selectedList: result,
conversation: currentConversation!,
);
}
},
),
),
Expanded(flex: 1, child: SizedBox()),
Expanded(flex: 1, child: SizedBox()),
],
),
),
);
}
_emoji() {
return Visibility(
visible: showEmoji,
child: Container(
color: Colors.white,
height: 150.w,
child: GridView.builder(
padding: EdgeInsets.symmetric(horizontal: 15.w),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8,
crossAxisSpacing: 8.w,
mainAxisSpacing: 8.w,
),
itemBuilder: (c, index) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
var text = SCEmojiDatas.smileys[index];
final TextEditingValue value = _textController.value;
_textController.value = value.copyWith(
text: value.text + text,
selection: TextSelection.fromPosition(
TextPosition(offset: text.length),
),
);
showSend = true;
setState(() {});
},
child: Padding(
padding: EdgeInsets.all(4.0.w),
child: ExtendedText(
SCEmojiDatas.smileys[index],
// specialTextSpanBuilder: MySpecialTextSpanBuilder(),
style: TextStyle(fontSize: sp(15)),
),
),
);
},
itemCount: SCEmojiDatas.smileys.length,
),
),
);
}
///工具项
_toolsItem(String image, String title, Function() onclick) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onclick,
child: Column(
children: [
Image.asset(image, width: 50.w, fit: BoxFit.fitWidth),
SizedBox(height: 9.w),
Text(title, style: TextStyle(fontSize: 11.sp, color: Colors.black)),
],
),
);
}
_onRevokeMessage(String msgId) {
int index = 0;
for (var msg in currentConversationMessageList) {
if (msgId == msg.msgID) {
break;
}
index = index + 1;
}
currentConversationMessageList.removeAt(index);
setState(() {});
}
_onMessageRecvC2CRead(List<String> messageIDList) {
for (var msg in currentConversationMessageList) {
for (var id in messageIDList) {
if (id == msg.msgID) {
msg.isPeerRead = true;
}
}
}
setState(() {});
}
_onNewMessage(V2TimMessage? message, {String? msgId}) async {
if (message == null) {
return;
}
if (message.userID != currentConversation?.userID) return;
int? index;
for (var element in currentConversationMessageList) {
if (msgId != null) {
if (msgId == element.msgID) {
element.status = MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL;
setState(() {});
continue;
}
} else {
if (element.msgID == message.msgID) {
index = currentConversationMessageList.indexOf(element);
continue;
}
}
}
if (msgId != null) {
return;
}
if (index != null) {
currentConversationMessageList.removeAt(index!);
currentConversationMessageList.insert(index!, message);
} else {
currentConversationMessageList.insert(0, message);
}
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.markC2CMessageAsRead(userID: currentConversation!.userID!);
//发送已读回执
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.sendMessageReadReceipts(messageIDList: [message.msgID!]);
setState(() {});
// await FTIM.getContactManager().setReadMessage(currentConversation);
}
}
class _MessageItem extends StatelessWidget {
final V2TimMessage message;
final V2TimMessage? preMessage;
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
// final FTIMUserProfile userProfile;
final SocialChatUserProfile? friend;
bool isSystem = false;
List<V2TimMessage> currentConversationMessageList = [];
Function updateCall;
///上一条
late BuildContext context;
_MessageItem({
required this.message,
this.preMessage,
// this.userProfile,
this.isSystem = false,
required this.currentConversationMessageList,
this.friend,
required this.updateCall,
});
@override
Widget build(BuildContext context) {
this.context = context;
SocialChatUserProfile? me = AccountStorage().getCurrentUser()?.userProfile;
bool showTime = true;
int timestamp = (message.timestamp ?? 0) * 1000;
int preTimestamp = (preMessage?.timestamp ?? 0) * 1000;
if (preMessage != null) {
///5分钟以内 不显示
// print('timestamp:$timestamp===preTimestamp:${preTimestamp}');
if (DateTime.fromMillisecondsSinceEpoch(timestamp)
.difference(DateTime.fromMillisecondsSinceEpoch(preTimestamp))
.inMilliseconds <
1000 * 60 * 5) {
showTime = false;
}
}
String time = SCMDateUtils.formatMessageTime(
context,
DateTime.fromMillisecondsSinceEpoch(timestamp ?? 0),
);
if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) {
return Container(
alignment: AlignmentDirectional.center,
padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 5.w),
child: text(
SCAppLocalizations.of(context)!.messageHasBeenRecalled,
fontSize: 12.sp,
textColor: Colors.white54,
fontWeight: FontWeight.w400,
),
);
}
return Container(
margin: EdgeInsets.symmetric(horizontal: 15.w, vertical: 8.w),
child: Column(
crossAxisAlignment:
!message.isSelf!
? CrossAxisAlignment.start
: CrossAxisAlignment.end,
// textDirection: message.isSelf ? TextDirection.rtl : TextDirection.ltr,
children: <Widget>[
Visibility(
visible: showTime,
child: Container(
margin: EdgeInsets.only(bottom: 12.w),
child: Row(
children: <Widget>[
Spacer(),
Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(
horizontal: width(15),
vertical: 8.w,
),
decoration: BoxDecoration(
//color: Color(0xFFEAEFF7),
borderRadius: BorderRadius.all(
Radius.circular(height(13)),
),
),
child: Text(
time,
style: TextStyle(
fontSize: sp(12),
color: Colors.white54,
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
height: 1,
),
),
),
Spacer(),
],
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
textDirection:
message.isSelf! ? TextDirection.rtl : TextDirection.ltr,
children: <Widget>[
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (isSystem) {
return;
}
SCNavigatorUtils.push(
context,
replace:
AccountStorage().getCurrentUser()?.userProfile?.id !=
message.sender,
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == message.sender}&tageId=${message.sender}",
);
},
child:
message.sender == "administrator" ||
message.sender == "customer"
? ExtendedImage.asset(
_strategy.getSCMessageChatPageSystemHeadImage(),
width: 45.w,
shape: BoxShape.circle,
fit: BoxFit.cover,
)
: netImage(
url:
message.isSelf!
? (me?.userAvatar ?? "")
: (friend?.userAvatar ?? ""),
shape: BoxShape.circle,
width: width(45),
),
),
// 他人消息的阴影间隔是10dp
Container(width: 8.w),
Row(
textDirection:
message.isSelf! ? TextDirection.rtl : TextDirection.ltr,
children: <Widget>[
Container(
alignment:
message.isSelf!
? AlignmentDirectional.topEnd
: AlignmentDirectional.topStart,
margin: EdgeInsets.only(top: 4.w),
child: _msg(),
),
SizedBox(width: 6.w),
Visibility(
visible:
message.status ==
MessageStatus.V2TIM_MSG_STATUS_SENDING,
child: CupertinoActivityIndicator(),
),
Visibility(
visible:
message.status ==
MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL,
child: Icon(Icons.error, color: Colors.red),
),
],
),
Spacer(),
],
),
],
),
);
}
_msg() {
V2TIMElem? elem = message.textElem;
if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT) {
V2TimTextElem textElem = elem as V2TimTextElem;
String content = textElem.text ?? "";
} else if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE) {
V2TimImageElem imageElem = message.imageElem as V2TimImageElem;
var image;
if (imageElem.path!.length > 2 &&
File("${imageElem.path}").existsSync()) {
image = Image.file(File(imageElem.path!), fit: BoxFit.cover);
} else {
image = ExtendedImage.network(
'${imageElem.imageList![1]?.url}',
fit: BoxFit.cover,
width: double.infinity,
cache: true,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(width(6))),
loadStateChanged: (ExtendedImageState state) {
if (state.extendedImageLoadState == LoadState.completed) {
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
fit: BoxFit.cover,
);
} else {
return Image.asset(
_strategy.getSCMessageChatPageLoadingIcon(),
fit: BoxFit.cover,
);
}
},
);
}
return Builder(
builder: (ct) {
return GestureDetector(
onTap: () {
List<String> ims = [];
int initialIndex = 0;
int index = 0;
for (var imageMsg in currentConversationMessageList) {
if (imageMsg.elemType ==
MessageElemType.V2TIM_ELEM_TYPE_IMAGE) {
String path = imageMsg.imageElem?.imageList![1]?.url ?? "";
if (path.isNotEmpty) {
ims.add(imageMsg.imageElem?.imageList![1]?.url ?? "");
if (imageMsg.msgID == message.msgID) {
initialIndex = index;
}
index = index + 1;
}
}
}
String encodedUrls = Uri.encodeComponent(jsonEncode(ims));
SCNavigatorUtils.push(
context,
"${SCMainRoute.imagePreview}?imageUrls=$encodedUrls&initialIndex=$initialIndex",
);
},
onLongPress: () {
_showMsgItemMenu(ct, "");
},
child: Container(
constraints: BoxConstraints(maxWidth: 200.w),
child: ClipRRect(
borderRadius: BorderRadius.circular(6.w),
child: image,
),
),
);
},
);
} else if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_VIDEO) {
V2TimVideoElem videoElem = message.videoElem as V2TimVideoElem;
if (videoElem.snapshotPath != null &&
videoElem.snapshotPath!.isNotEmpty) {
return Builder(
builder: (ct) {
return GestureDetector(
child: Container(
constraints: BoxConstraints(maxWidth: 200.w),
child: ClipRRect(
borderRadius: BorderRadius.circular(6.w),
child: Stack(
alignment: Alignment.center,
children: [
Image.file(
File(videoElem.snapshotPath!),
fit: BoxFit.cover,
),
Icon(Icons.play_circle, color: Colors.white, size: 55.w),
],
),
),
),
onTap: () {
String encodedVideoUrl = Uri.encodeComponent(
videoElem.videoPath ?? "",
);
SCNavigatorUtils.push(
context,
"${SCMainRoute.videoPlayer}?videoUrl=$encodedVideoUrl",
);
},
onLongPress: () {
_showMsgItemMenu(ct, "");
},
);
},
);
} else {
return FutureBuilder<V2TimValueCallback<V2TimMessageOnlineUrl>>(
// 假设 getMessageOnlineUrlForMessage 是对 TencentImSDKPlugin.v2TIMManager.getMessageManager().getMessageOnlineUrl 的封装,返回 Future<String>
future: SCMessageUtils().getMessageOnlineUrl(
msgID: message.msgID ?? "",
),
builder: (context, snapshot) {
// 根据异步任务状态构建UI
if (snapshot.connectionState == ConnectionState.waiting) {
// 数据加载中,显示占位符
return Container();
} else if (snapshot.hasError) {
// 数据加载失败显示错误UI
return Container();
} else if (snapshot.hasData) {
// 数据加载成功更新消息的mediaUrl并构建正常消息UI
message.videoElem = snapshot.data?.data?.videoElem!;
return Builder(
builder: (ct) {
return GestureDetector(
child: Container(
constraints: BoxConstraints(maxWidth: 200.w),
child: ClipRRect(
borderRadius: BorderRadius.circular(6.w),
child: Stack(
alignment: Alignment.center,
children: [
SCPathUtils.getPathType(
message.videoElem?.snapshotPath ?? "",
) ==
PathType.file
? Image.file(
File(message.videoElem?.snapshotPath ?? ""),
fit: BoxFit.cover,
)
: netImage(
url: message.videoElem?.snapshotUrl ?? "",
),
Icon(
Icons.play_circle,
color: Colors.white,
size: 55.w,
),
],
),
),
),
onTap: () {
String encodedVideoUrl = Uri.encodeComponent(
message.videoElem?.videoUrl ?? "",
);
SCNavigatorUtils.push(
context,
"${SCMainRoute.videoPlayer}?videoUrl=$encodedVideoUrl",
);
},
onLongPress: () {
_showMsgItemMenu(ct, "");
},
);
},
);
} else {
// 其他情况
return Container();
}
},
);
}
}
return _text();
}
///文本消息
_text() {
String content = "";
if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT) {
V2TimTextElem textElem = message.textElem!;
content = textElem.text ?? "";
if (isSystem) {}
} else if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE) {
content = SCAppLocalizations.of(context)!.image;
} else if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_VIDEO) {
content = SCAppLocalizations.of(context)!.video;
} else if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND) {
content = SCAppLocalizations.of(context)!.sound;
} else if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_CUSTOM) {
V2TimCustomElem customElem = message.customElem!;
content = customElem.data ?? "";
}
return Builder(
builder: (ct) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
color: Color(0xff18F2B1).withOpacity(0.1),
borderRadius: BorderRadius.only(
topLeft:
message.isSelf! ? Radius.circular(8) : Radius.circular(0),
topRight: Radius.circular(8.w),
bottomLeft: Radius.circular(8.w),
bottomRight:
message.isSelf! ? Radius.circular(0) : Radius.circular(8),
),
),
padding: EdgeInsets.all(8.0.w),
child: Container(
constraints: BoxConstraints(maxWidth: width(220)),
child: ExtendedText(
content,
// specialTextSpanBuilder: MySpecialTextSpanBuilder(),
style: TextStyle(
fontSize: sp(14),
color: message.isSelf! ? Colors.white : Colors.white,
),
),
),
),
onLongPress: () {
_showMsgItemMenu(ct, content);
},
);
},
);
}
void _showMsgItemMenu(BuildContext ct, String content) {
SmartDialog.showAttach(
tag: "showMsgItemMenu",
targetContext: ct,
alignment: Alignment.topCenter,
maskColor: Colors.transparent,
animationType: SmartAnimationType.fade,
scalePointBuilder: (selfSize) => Offset(selfSize.width, 10),
builder: (_) {
return Container(
margin: EdgeInsets.only(bottom: 10.w),
decoration: BoxDecoration(
color: Color(0xffdcdddf),
borderRadius: BorderRadius.circular(10),
),
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SmartDialog.dismiss(tag: "showMsgItemMenu");
_delete();
},
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.w),
child: Column(
children: [
Image.asset(
_strategy.getSCMessageChatPageMessageMenuDeleteIcon(),
width: 20.w,
),
SizedBox(height: 3.w),
text(
SCAppLocalizations.of(context)!.delete,
fontSize: 12.sp,
textColor: Color(0xff777777),
),
],
),
),
),
content.isNotEmpty
? GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SmartDialog.dismiss(tag: "showMsgItemMenu");
Clipboard.setData(ClipboardData(text: content));
SCTts.show(
SCAppLocalizations.of(context)!.copiedToClipboard,
);
},
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.w),
child: Column(
children: [
Image.asset(
_strategy.getSCMessageChatPageMessageMenuCopyIcon(),
width: 20.w,
),
SizedBox(height: 3.w),
text(
SCAppLocalizations.of(context)!.copy,
fontSize: 12.sp,
textColor: Color(0xff777777),
),
],
),
),
)
: Container(),
(message.isSelf ?? false)
? GestureDetector(
onTap: () {
SmartDialog.dismiss(tag: "showMsgItemMenu");
_revokeMessage();
},
behavior: HitTestBehavior.opaque,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.w),
child: Column(
children: [
Image.asset(
_strategy
.getSCMessageChatPageMessageMenuRecallIcon(),
width: 20.w,
),
SizedBox(height: 3.w),
text(
SCAppLocalizations.of(context)!.recall,
fontSize: 12.sp,
textColor: Color(0xff777777),
),
],
),
),
)
: Container(),
],
),
);
},
);
}
///撤回消息
void _revokeMessage() async {
SmartDialog.show(
tag: "showConfirmDialog",
alignment: Alignment.center,
debounce: true,
animationType: SmartAnimationType.fade,
builder: (_) {
return MsgDialog(
title: SCAppLocalizations.of(context)!.tips,
msg: SCAppLocalizations.of(context)!.recallThisMessage,
btnText: SCAppLocalizations.of(context)!.confirm,
onEnsure: () async {
// 撤回指定msgID的消息
V2TimCallback revokeMessage = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.revokeMessage(msgID: message.msgID ?? "");
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.deleteMessages(msgIDs: [message.msgID ?? ""]);
if (revokeMessage.code == 0) {
// 撤回成功
int index = 0;
for (var value in currentConversationMessageList) {
if (value.msgID == message.msgID) {
break;
}
index = index + 1;
}
currentConversationMessageList.removeAt(index);
updateCall();
} else {
// 撤回失败,可通过 revokeMessage.desc 获取错误描述
SCTts.show("fail:${revokeMessage.desc}");
}
},
);
},
);
}
///删除消息
void _delete() {
SmartDialog.show(
tag: "showDeleteDialog",
alignment: Alignment.bottomCenter,
debounce: true,
animationType: SmartAnimationType.fade,
builder: (_) {
return SafeArea(
child: Container(
height: 168.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0),
),
),
child: Column(
children: [
SizedBox(height: 10.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(context)!.tips,
fontSize: 16.sp,
textColor: Colors.black,
fontWeight: FontWeight.bold,
),
],
),
SizedBox(height: 5.w),
Divider(height: height(1), color: SocialChatTheme.dividerColor),
GestureDetector(
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.w),
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(context)!.deleteFromMyDevice,
fontSize: 14.sp,
textColor: Colors.black,
),
],
),
),
onTap: () async {
V2TimCallback deleteMessageFromLocalStorage =
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.deleteMessageFromLocalStorage(
msgID: message.msgID ?? "",
);
if (deleteMessageFromLocalStorage.code == 0) {
//删除成功
int index = 0;
for (var value in currentConversationMessageList) {
if (value.msgID == message.msgID) {
break;
}
index = index + 1;
}
currentConversationMessageList.removeAt(index);
updateCall();
SmartDialog.dismiss(tag: "showDeleteDialog");
}
},
),
Divider(height: height(1), color: SocialChatTheme.dividerColor),
GestureDetector(
onTap: () async {
V2TimCallback deleteMessageFromLocalStorage =
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.deleteMessages(msgIDs: [message.msgID ?? ""]);
if (deleteMessageFromLocalStorage.code == 0) {
//删除成功
int index = 0;
for (var value in currentConversationMessageList) {
if (value.msgID == message.msgID) {
break;
}
index = index + 1;
}
currentConversationMessageList.removeAt(index);
updateCall();
SmartDialog.dismiss(tag: "showDeleteDialog");
}
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.w),
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(context)!.deleteOnAllDevices,
fontSize: 14.sp,
textColor: Colors.black,
),
],
),
),
),
Divider(height: height(1), color: SocialChatTheme.dividerColor),
GestureDetector(
onTap: () async {
SmartDialog.dismiss(tag: "showDeleteDialog");
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 10.w),
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(context)!.cancel,
fontSize: 14.sp,
textColor: Colors.black,
),
],
),
),
),
],
),
),
);
},
);
}
Future<void> updateRedPacketStatus(String messageID, String packetID) async {
// 构建扩展信息例如标记为已领取并包含领取者ID
Map<String, String> extensionMap = {"packetID": packetID};
List<V2TimMessageExtension> extensions =
extensionMap.entries
.map(
(e) => V2TimMessageExtension(
extensionKey: e.key,
extensionValue: e.value,
),
)
.toList();
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.setMessageExtensions(
msgID: messageID, // 原始红包消息的ID
extensions: extensions,
);
}
void _showOpenDialog(SCUserRedPacketSendRes resData) {
SmartDialog.show(
tag: "showOpenDialog",
alignment: Alignment.center,
debounce: true,
animationType: SmartAnimationType.fade,
builder: (_) {
return Container(
height: 400.w,
alignment: Alignment.topCenter,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
_strategy.getSCMessageChatPageRedEnvelopeOpenBackground(),
),
fit: BoxFit.fill,
),
),
child: Column(
children: [
SizedBox(height: 100.w),
SCDebounceWidget(
child: Container(
alignment: Alignment.center,
width: 60.w,
height: 60.w,
color: Colors.transparent,
child: text(
SCAppLocalizations.of(context)!.open,
textColor: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w900,
),
),
onTap: () {
SmartDialog.dismiss(tag: "showOpenDialog");
SCLoadingManager.show();
SCAccountRepository()
.userRedPacketGrab(resData.packetId ?? "")
.then((res) {
var future = SCAccountRepository().userRedPacketDetail(
resData.packetId ?? "",
);
updateRedPacketStatus(
message.msgID ?? "",
resData.packetId ?? "",
);
SCMessageUtils.redPacketFutureCache[resData.packetId ??
""] =
future;
future
.then((res2) {
SCLoadingManager.hide();
updateCall();
_showOpenedDialog(res2);
})
.catchError((e) {
SCLoadingManager.hide();
});
})
.catchError((e) {
SCLoadingManager.hide();
});
},
),
SizedBox(height: 35.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(context)!.openRedPackDialogTip,
fontWeight: FontWeight.w600,
textColor: Colors.white,
fontSize: 14.sp,
),
SizedBox(width: 3.w),
Container(
margin: EdgeInsets.only(bottom: 1.w),
height: 18.w,
constraints: BoxConstraints(maxWidth: 65.w),
child:
(resData.senderInfo?.nickName?.length ?? 0) > 6
? Marquee(
text: resData.senderInfo?.nickName ?? "",
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
scrollAxis: Axis.horizontal,
crossAxisAlignment: CrossAxisAlignment.start,
blankSpace: 20.0,
velocity: 40.0,
pauseAfterRound: Duration(seconds: 1),
accelerationDuration: Duration(seconds: 1),
accelerationCurve: Curves.easeOut,
decelerationDuration: Duration(milliseconds: 550),
decelerationCurve: Curves.easeOut,
)
: Text(
resData.senderInfo?.nickName ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
),
),
SizedBox(width: 3.w),
Container(
child: text(
SCAppLocalizations.of(context)!.sent,
fontWeight: FontWeight.w600,
textColor: Colors.white,
fontSize: 14.sp,
),
height: 18.w,
),
],
),
SizedBox(height: 15.w),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(context)!.wishingYouHappinessEveryDay,
fontWeight: FontWeight.w600,
textColor: Colors.white70,
fontSize: 13.sp,
),
],
),
],
),
);
},
);
}
void _showOpenedDialog(SCUserRedPacketSendRes resData) {
SmartDialog.show(
tag: "showOpenedDialog",
alignment: Alignment.center,
debounce: true,
animationType: SmartAnimationType.fade,
builder: (_) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 20.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
_strategy.getSCMessageChatPageRedEnvelopeOpenedBackground(),
),
fit: BoxFit.fill,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 70.w),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
child: Icon(Icons.close, size: 18.w, color: Colors.white),
onTap: () {
SmartDialog.dismiss(tag: "showOpenedDialog");
},
),
SizedBox(width: 10.w),
],
),
SizedBox(height: 25.w),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 20.w),
netImage(
url: resData.senderInfo?.avatar ?? "",
shape: BoxShape.circle,
width: 45.w,
height: 45.w,
),
SizedBox(width: 10.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
height: 18.w,
constraints: BoxConstraints(maxWidth: 85.w),
child:
(resData.senderInfo?.nickName?.length ?? 0) > 8
? Marquee(
text: resData.senderInfo?.nickName ?? "",
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
scrollAxis: Axis.horizontal,
crossAxisAlignment:
CrossAxisAlignment.start,
blankSpace: 20.0,
velocity: 40.0,
pauseAfterRound: Duration(seconds: 1),
accelerationDuration: Duration(
seconds: 1,
),
accelerationCurve: Curves.easeOut,
decelerationDuration: Duration(
milliseconds: 500,
),
decelerationCurve: Curves.easeOut,
)
: Text(
resData.senderInfo?.nickName ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
),
),
SizedBox(width: 5.w),
Container(
child: text(
SCAppLocalizations.of(context)!.sentARedEnvelope,
fontWeight: FontWeight.w600,
textColor: Colors.white,
fontSize: 14.sp,
),
height: 18.w,
),
],
),
SizedBox(height: 5.w),
Row(
children: [
text(
SCAppLocalizations.of(
context,
)!.wishingYouHappinessEveryDay,
fontWeight: FontWeight.w600,
textColor: Colors.white70,
fontSize: 13.sp,
),
],
),
],
),
],
),
SizedBox(height: 10.w),
AccountStorage().getCurrentUser()?.userProfile?.id ==
"${resData.receiverInfo?.userId}"
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
_strategy.getSCMessageChatPageGoldCoinIcon(),
height: 35.w,
),
text(
"${resData.totalAmount ?? 0}",
fontWeight: FontWeight.w900,
textColor: Color(0xffFFF836),
fontSize: 22.sp,
),
],
)
: Container(),
Divider(
color: Color(0xffF3F3F3),
endIndent: 15.w,
indent: 15.w,
thickness: 0.4,
),
SizedBox(height: 5.w),
Row(
children: [
SizedBox(width: 15.w),
text(
SCAppLocalizations.of(
context,
)!.redEnvelopeAmount("${resData.totalAmount}"),
fontWeight: FontWeight.w600,
textColor: Colors.white70,
fontSize: 13.sp,
),
Spacer(),
resData.status == 2
? Row(
children: [
Image.asset(
_strategy.getSCMessageChatPageGoldCoinIcon(),
height: 15.w,
),
text(
"${resData.totalAmount ?? 0}",
fontWeight: FontWeight.w400,
textColor: Color(0xffFFF836),
fontSize: 14.sp,
),
],
)
: Container(),
SizedBox(width: 15.w),
],
),
SizedBox(height: 8.w),
resData.status == 2
? Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 20.w),
netImage(
url: resData.receiverInfo?.avatar ?? "",
shape: BoxShape.circle,
width: 45.w,
height: 45.w,
),
SizedBox(width: 10.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
height: 18.w,
constraints: BoxConstraints(maxWidth: 120.w),
child:
(resData.receiverInfo?.nickName?.length ??
0) >
12
? Marquee(
text:
resData.receiverInfo?.nickName ??
"",
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
scrollAxis: Axis.horizontal,
crossAxisAlignment:
CrossAxisAlignment.start,
blankSpace: 20.0,
velocity: 40.0,
pauseAfterRound: Duration(seconds: 1),
accelerationDuration: Duration(
seconds: 1,
),
accelerationCurve: Curves.easeOut,
decelerationDuration: Duration(
milliseconds: 500,
),
decelerationCurve: Curves.easeOut,
)
: Text(
resData.receiverInfo?.nickName ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
),
),
SizedBox(width: 5.w),
],
),
SizedBox(height: 5.w),
Row(
children: [
text(
SCMDateUtils.formatDateTime3(
DateTime.fromMillisecondsSinceEpoch(
resData.grabbedAt ?? 0,
),
),
fontWeight: FontWeight.w600,
textColor: Colors.white70,
fontSize: 13.sp,
),
],
),
],
),
],
)
: (resData.status == 1
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
text(
SCAppLocalizations.of(
context,
)!.redEnvelopeNotYetClaimed,
fontWeight: FontWeight.w600,
textColor: Colors.white,
fontSize: 15.sp,
),
],
)
: Container()),
SizedBox(height: 18.w),
],
),
);
},
);
}
_buildPackTips(SCUserRedPacketSendRes? resData) {
//状态1-待领取 2-已领取 3-已过期
if (resData?.status == 2) {
if ("${resData?.receiverInfo?.userId}" ==
AccountStorage().getCurrentUser()?.userProfile?.id) {
return Container(
width: ScreenUtil().screenWidth * 0.6,
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: SCAppLocalizations.of(
context,
)!.youAccepted(resData?.senderInfo?.nickName ?? ""),
style: TextStyle(
fontSize: 12.sp,
color: Colors.black26,
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: SCAppLocalizations.of(context)!.redEnvelope,
style: TextStyle(
fontSize: 12.sp,
color: Color(0xffFF7759),
fontWeight: FontWeight.w600,
),
),
TextSpan(
text: ".",
style: TextStyle(
fontSize: 12.sp,
color: Color(0xffFF7759),
fontWeight: FontWeight.bold,
),
),
],
),
),
);
} else if ("${resData?.senderInfo?.userId}" ==
AccountStorage().getCurrentUser()?.userProfile?.id) {
return Container(
width: ScreenUtil().screenWidth * 0.6,
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: SCAppLocalizations.of(
context,
)!.acceptedYour(resData?.receiverInfo?.nickName ?? ""),
style: TextStyle(
fontSize: 12.sp,
color: Colors.black26,
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: SCAppLocalizations.of(context)!.redEnvelope,
style: TextStyle(
fontSize: 12.sp,
color: Color(0xffFF7759),
fontWeight: FontWeight.w600,
),
),
TextSpan(
text: ".",
style: TextStyle(
fontSize: 12.sp,
color: Color(0xffFF7759),
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
return Container();
}
}
// class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
// /// whether show background for @somebody
// MySpecialTextSpanBuilder();
//
// @override
// TextSpan build(String data, {TextStyle? textStyle, onTap}) {
// var textSpan = super.build(data, textStyle: textStyle, onTap: onTap);
// return textSpan;
// }
//
// @override
// SpecialText? createSpecialText(
// String flag, {
// TextStyle? textStyle,
// SpecialTextGestureTapCallback? onTap,
// required int index,
// }) {
// if (flag == null || flag == "") return null;
//
// ///index is end index of start flag, so text start index should be index-(flag.length-1)
// if (textStyle != null) {
// if (isStart(flag, EmojiText.flag)) {
// return EmojiText(textStyle, start: index - (EmojiText.flag.length - 1));
// }
// }
// return null;
// }
// }
// class EmojiText extends SpecialText {
// EmojiText(TextStyle textStyle, {required this.start})
// : super(EmojiText.flag, ']', textStyle);
// static const String flag = '[';
// final int start;
//
// @override
// InlineSpan finishText() {
// String key = toString();
// key = key.replaceAll("[", "");
// key = key.replaceAll("]", "");
// int index;
// try {
// index = int.parse(key);
// } catch (e) {
// return TextSpan(text: toString(), style: textStyle);
// }
//
// ///https://github.com/flutter/flutter/issues/42086
// /// widget span is not working on web
// if (index <= 113) {
// //fontsize id define image height
// //size = 30.0/26.0 * fontSize
// double size = 16.w;
//
// ///fontSize 26 and text height =30.0
// //final double fontSize = 26.0;
// return ImageSpan(
// AssetImage("sc_images/msg/emoji/$key.png"),
// actualText: key,
// imageWidth: textStyle!.fontSize!,
// imageHeight: textStyle!.fontSize!,
// start: start,
// fit: BoxFit.fill,
// margin: const EdgeInsets.only(left: 2.0, top: 2.0, right: 2.0),
// );
// }
//
// return TextSpan(text: toString(), style: textStyle);
// }
// }