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 { BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy; final TextEditingController _textController = TextEditingController(); final ScrollController _scrollController = ScrollController(); final RefreshController _refreshController = RefreshController(); // FlutterSoundPlayer? flutterSound; RtmProvider? rtmProvider; V2TimConversation? currentConversation; List currentConversationMessageList = []; final FocusNode _focusNode = FocusNode(); ///是否显示工具栏 bool showTools = false; bool showEmoji = false; bool showDoNotClickUnfamiliarTips = true; ///互相关注才能互相发送消息 bool canSendMsg = false; SocialChatUserProfile? friend; ///是否显示发送按钮 bool showSend = false; List coinsTitles = ["100", "1000", "10000", "50000"]; String selecteCoins = "100"; @override void initState() { super.initState(); SCMessageNotifier.canPlay = false; SCMessageUtils.redPacketFutureCache.clear(); // flutterSound = FlutterSoundPlayer(); rtmProvider = Provider.of(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( 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: [ 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: [ 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> v2timValueCallback = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .getC2CHistoryMessageList( userID: currentConversation!.userID ?? "", count: 30, lastMsgID: currentConversationMessageList.last.msgID, ); List messages = v2timValueCallback.data as List; 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 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> v2timValueCallback = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .getC2CHistoryMessageList( userID: currentConversation!.userID!, count: 100, lastMsgID: null, ); List messages = v2timValueCallback.data!; // List 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( builder: (_, provider, __) => Container( color: Colors.transparent, height: 56.w, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ 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( context, listen: false, ).sendC2CTextMsg(msg, currentConversation!); } ///发消息 sendCustomMsg(String msg, String extension) async { if (!canSendMsg) { SCTts.show(SCAppLocalizations.of(context)!.canSendMsgTips); return; } Provider.of( 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: [ 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? 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 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 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: [ Visibility( visible: showTime, child: Container( margin: EdgeInsets.only(bottom: 12.w), child: Row( children: [ 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: [ 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: [ 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 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>( // 假设 getMessageOnlineUrlForMessage 是对 TencentImSDKPlugin.v2TIMManager.getMessageManager().getMessageOnlineUrl 的封装,返回 Future 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 updateRedPacketStatus(String messageID, String packetID) async { // 构建扩展信息,例如标记为已领取,并包含领取者ID Map extensionMap = {"packetID": packetID}; List 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); // } // }