import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:chat/chat/download_item.dart'; import 'package:chat/chat/file_msg_item.dart'; import 'package:chat/chat/msg_state_widge.dart'; import 'package:chat/chat/place_item.dart'; import 'package:chat/chat/redbag_widget.dart'; import 'package:chat/chat/upload_item.dart'; import 'package:chat/chat/video_view.dart'; import 'package:chat/data/UserData.dart'; import 'package:chat/data/constants.dart'; import 'package:chat/utils/friend_list_mgr.dart'; import 'package:chat/utils/wpop/w_popup_menu.dart'; import 'package:flutter/services.dart'; import 'package:chat/generated/i18n.dart'; import 'package:chat/home/invite_detail_page.dart'; import 'package:chat/models/ChatMsg.dart'; import 'package:chat/models/group_info_model.dart'; import 'package:chat/models/keyboard_provider.dart'; import 'package:chat/proto/chat.pbenum.dart'; import 'package:chat/proto/chat.pbserver.dart'; import 'package:chat/utils/CustomUI.dart'; import 'package:chat/utils/MessageMgr.dart'; import 'package:chat/utils/app_navigator.dart'; import 'package:chat/utils/date_utils.dart'; import 'package:chat/utils/group_member_model.dart'; import 'package:chat/utils/msgHandler.dart'; import 'package:chat/utils/screen.dart'; import 'package:chat/utils/sound_util.dart'; import 'package:chat/utils/upload_util.dart'; import 'package:chat/utils/video_anim.dart'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:oktoast/oktoast.dart'; import 'package:chat/utils/HttpUtil.dart'; import 'package:share_extend/share_extend.dart'; import 'package:url_launcher/url_launcher.dart'; import '../r.dart'; import '../utils/file_cache_mgr.dart'; import 'full_img_view.dart'; import 'upload_item.dart'; import 'package:chat/models/ref_name_provider.dart'; import 'package:chat/models/money_change.dart'; import 'package:chat/models/voucher_change.dart'; const double ChatRadius = 7.5; const Color SendMsgBg = Color(0xFFD4F0FF); const Color SendMsgText = Colors.black; const double TextHeight = 1.2; const double PaddingLeft = 9.5; const Color ReciveBorderColor = Color(0xFFDCDCDC); const double FontSize = 15; class GroupChatPageItem extends StatefulWidget { final MsgModel msg; final int lastMsgTime; final GroupMemberModel memberModel; final Function hideKeyboard; const GroupChatPageItem( {Key key, this.msg, this.lastMsgTime, this.hideKeyboard, this.memberModel}) : assert(msg != null), super(key: key); @override _GroupChatPageItemState createState() => _GroupChatPageItemState(); } class _GroupChatPageItemState extends State with SingleTickerProviderStateMixin { int curTextType = 0; //文字、译文切换索引 List textList = []; String curSoundUrl; CancelToken _cancelToken = CancelToken(); bool isLongPressed = false; double maxWidth = Screen.width - 140; @override void initState() { super.initState(); if (widget.msg.from == UserData().basicInfo.userId && widget.msg.state == MsgState.None) { print('重新发送消息'); MsgHandler.sendChatMsg(widget.msg); } textList = widget.msg.getTransTextList(); MessageMgr().on('Update Translate Message', updateTranslateMsg); MessageMgr().on('Cancel Request', _deleteItem); } @override void dispose() { MessageMgr().off('Cancel Request', _deleteItem); MessageMgr().off('Update Translate Message', updateTranslateMsg); super.dispose(); } _deleteItem(msg) { if (msg == widget.msg) { print(widget.msg.state); if (widget.msg.state == MsgState.Uploading) { print('取消上传'); UploadUtil().cancelRequests(_cancelToken); } } } updateTextList() { textList.clear(); curTextType = 0; textList = widget.msg.getTransTextList(); } updateTranslateMsg(msg) { if (msg.time == widget.msg.time) { if (mounted) { updateTextList(); if (!mounted) { return; } setState(() { print('更新翻译文字'); }); } } } getTodayTime(msgDate, {var sendTime}) { String showTimeStr; sendTime = sendTime ?? widget.msg.time; var today = DateTime.now(); //今天 if (msgDate.year == today.year && msgDate.month == today.month && msgDate.day == today.day) { showTimeStr = DateUtils().getFormartData(timeSamp: sendTime, format: 'HH:mm'); } else { showTimeStr = DateUtils() .getFormartData(timeSamp: sendTime, format: 'yyyy/MM/dd HH:mm'); } return showTimeStr; } String getMsgTime() { String showTimeStr; var sendTime = widget.msg.time; var msgDate = DateTime.fromMillisecondsSinceEpoch(sendTime); if (widget.lastMsgTime == null) { showTimeStr = getTodayTime(msgDate); } else { if (sendTime - widget.lastMsgTime > 3 * 60 * 1000) { showTimeStr = getTodayTime(msgDate); } } return showTimeStr; } @override Widget build(BuildContext context) { var showTime = getMsgTime(); return Container( width: Screen.width, color: Colors.white.withOpacity(0), margin: const EdgeInsets.symmetric(vertical: 10.0), child: Column( children: [ showTime == null ? SizedBox() : Container( decoration: BoxDecoration( color: Color(0xFF2C2F36).withOpacity(0.25), borderRadius: BorderRadius.all(Radius.circular(9.5))), padding: EdgeInsets.only(left: 7, right: 7, top: 3, bottom: 2), child: Text(showTime, textScaleFactor: 1.0, style: TextStyle(fontSize: 10.0, color: Colors.white)), ), SizedBox(height: 10), _msgWidget() ], ), ); } _msgWidget() { if (widget.msg.from == 0) { return _serverNotifyMsg(); } else { if (widget.msg.from == UserData().basicInfo.userId) { return _getSentMessageLayout(context); } else { return _getReceivedMessageLayout(context); } } } _serverNotifyMsg() { var type = widget.msg.msgType; if (type == ChatType.GroupChatNoticeType.value) { var res = GroupChatNotice.fromBuffer(widget.msg.msgContent); var groupInfoModel = Provider.of(context); var optId = res.operatuId; var showStr = MsgHandler.getGroupNoticeMsg(res, groupInfoModel); return Container( alignment: Alignment.center, constraints: BoxConstraints(maxWidth: Screen.width - 120), child: extendedText(showStr, color: Constants.GreyTextColor, fontSize: 12, onSpecialTextTap: (dynamic parameter) { // print('点击事件 $parameter'); if (parameter.startsWith("\$")) { // showToast('点击: ${res.operateduId[0]}'); List list = []; for (int k = 0; k < res.operateduId.length; k++) { list.add(GroupMemberModel.fromBaseInfo(res.operateduId[k])); } AppNavigator.push( context, InviteDetailPage( originalList: list, opt: GroupMemberModel.fromBaseInfo(optId), groupId: groupInfoModel.sessionId, )); } }), ); } return Container(); } _textGif(List msgContent) { var msg = utf8.decode(msgContent); return Container( constraints: BoxConstraints(maxWidth: Screen.width - 120), child: extendedText( msg, selectionEnabled: false, hideKeyboard: widget.hideKeyboard, fontSize: FontSize, color: Colors.black, ), ); } _textMsg(MsgModel msgModel) { List showMsg = []; var width = 0.0; var msg = utf8.decode(msgModel.msgContent); if (msg.contains('[ ')) { msg = msg.replaceAll('[ ', '['); } if (msg.contains(' ]')) { msg = msg.replaceAll(' ]', ']'); } bool isUrl = false; if (textList[curTextType].contains('http')) { isUrl = true; } if (msgModel.refMsgContent != null && msgModel.refMsgContent.length > 0) { QuoteMsg quoteMsg = QuoteMsg.fromBuffer(msgModel.refMsgContent); var refName = Provider.of(context) .getGroupRefName(msgModel.sessionId, quoteMsg.sendUserId); var msgDate = DateTime.fromMillisecondsSinceEpoch(quoteMsg.sendTime.toInt()); var showTimeStr = getTodayTime(msgDate, sendTime: quoteMsg.sendTime.toInt()); var timetWidth = CustomUI.getRealTextWidht(showTimeStr, 13); var nameWidhth = CustomUI.getRealTextWidht(refName, 13); width = timetWidth + (nameWidhth > 70 ? 70 : nameWidhth) + 36; var tempIndex = quoteMsg.content.indexOf(':'); var quoteContent = tempIndex == -1 ? quoteMsg.content : quoteMsg.content.substring(quoteMsg.content.indexOf(':') + 2); var contentWidht = _getTextWidth(quoteContent, fontSize: 13); width = min(max(width, contentWidht), maxWidth); showMsg.add(InkWell( onTap: () { MessageMgr().emit('Jump to Msg', quoteMsg.sendTime.toInt()); }, child: Container( padding: EdgeInsets.all(8), margin: EdgeInsets.only(bottom: 5), decoration: BoxDecoration( color: Color(0xFFC4E0F5), borderRadius: BorderRadius.circular(5.5)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ Container( constraints: BoxConstraints(maxWidth: 70), child: Text( refName, style: TextStyle(fontSize: 13), overflow: TextOverflow.ellipsis, )), SizedBox(width: 20), Text( showTimeStr, style: TextStyle(fontSize: 13), ) ]), SizedBox(height: 7.5), Text( quoteContent, style: TextStyle(fontSize: 13), ) ], ), ))); } var contentWidth = _getTextWidth(textList[curTextType]); if (contentWidth > width && contentWidth <= maxWidth) { width = contentWidth; } showMsg.add(Container( constraints: BoxConstraints(maxWidth: maxWidth, minHeight: 22), alignment: Alignment.centerLeft, child: extendedText( textList[curTextType], color: isUrl ? Colors.blue : Constants.BlackTextColor, hideKeyboard: widget.hideKeyboard, fontSize: FontSize, ))); return Container( constraints: BoxConstraints(maxWidth: Screen.width - 120), width: width + 20, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: showMsg), padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5), decoration: BoxDecoration( color: isLongPressed ? Colors.grey[300] : SendMsgBg, border: Border.all(color: Color(0xFFB9CBD7), width: 0.6), borderRadius: BorderRadius.all(Radius.circular(ChatRadius))), ); } _soundMsg() { double time = widget.msg.extraInfo / 1000; if (time > 60) { time = 60.0; } bool isPlaying = false; var soundPath = widget.msg.localFile; isPlaying = SoundUtils().isPlaying(soundPath); var soundWidget = GestureDetector( child: Container( width: 120, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Container( alignment: Alignment.center, padding: EdgeInsets.only(bottom: 2), margin: EdgeInsets.only(right: 10), width: 25.5, height: 25.5, decoration: BoxDecoration( border: Border.all(color: const Color(0xFF1B92C7), width: 0.5), color: const Color(0xFF04A4FE), shape: BoxShape.circle), child: Icon( IconData(isPlaying ? 0xe652 : 0xe653, fontFamily: Constants.IconFontFamily), size: isPlaying ? 15 : 18, color: Colors.white, ), ), isPlaying ? Stack( children: [ Container( height: 18, width: 19, ), Positioned( bottom: 0, child: VideoAnim( begin: 18, start: 0.444, end: 4.5, )), Positioned( left: 7, bottom: 0, child: VideoAnim( begin: 4.5, end: 18, )), Positioned( left: 14, bottom: 0, child: VideoAnim( begin: 18, end: 4.5, )) ], ) : Stack( children: [ Container( height: 18, width: 19, ), Positioned( bottom: 0, child: CustomUI.buildAudioContaniner(12)), Positioned( left: 7, bottom: 0, child: CustomUI.buildAudioContaniner(4.5)), Positioned( left: 14, bottom: 0, child: CustomUI.buildAudioContaniner(18)) ], ), Expanded(child: SizedBox()), fixedText(time.toStringAsFixed(0), color: SendMsgText, fontSize: 16) ], ), padding: EdgeInsets.symmetric(horizontal: 15, vertical: 12), decoration: BoxDecoration( color: SendMsgBg, border: Border.all(color: Color(0xFFB9CBD7), width: 0.6), borderRadius: BorderRadius.all(Radius.circular(ChatRadius))), ), onTap: () async { print('播放状态 : $isPlaying'); print('当前文件$soundPath '); if (isPlaying) { SoundUtils().pause(); } else { SoundUtils().play(soundPath, onPlayed: () { if (mounted) { setState(() {}); } }, complete: () { if (mounted) { setState(() {}); } }); } }, ); return UploadImgItem( msg: widget.msg, child: soundWidget, isShowProgress: false, ); } Size _getImgSize() { double aspectRatio = widget.msg.extraInfo / 100; var maxWidth = Screen.width * 0.65; var maxHeight = Screen.height / 4; var width, height; if (maxWidth / maxHeight > aspectRatio) { height = maxHeight; width = maxHeight * aspectRatio; } else { width = maxWidth; height = maxWidth / aspectRatio; } return Size(width, height); } _imgMsg(List imgData) { var imgSize = _getImgSize(); ImageProvider provider = MemoryImage(widget.msg.localFile == null ? Uint8List.fromList(imgData) : File(widget.msg.localFile).readAsBytesSync()); return GestureDetector( child: ClipRRect( child: UploadImgItem( msg: widget.msg, cancelToken: _cancelToken, child: Container( height: imgSize.height, width: imgSize.width, child: Image( fit: BoxFit.contain, image: provider, ), )), borderRadius: BorderRadius.circular(5), ), onTap: () async { showFullImg(context, widget.msg); }); } _videoMsg(BuildContext context, List thumbnail) { var imgSize = _getImgSize(); return InkWell( child: ClipRRect( child: Stack( alignment: Alignment.center, children: [ UploadImgItem( msg: widget.msg, cancelToken: _cancelToken, child: Container( width: imgSize.width, height: imgSize.height, child: Image( fit: BoxFit.contain, image: MemoryImage(Uint8List.fromList(thumbnail)), ), )) ], ), borderRadius: BorderRadius.circular(5), ), onTap: () { showVideoPage(context, widget.msg.localFile); }, ); } _msgLayout(BuildContext context, MsgModel msg) { Widget item; switch (ChatType.valueOf(msg.msgType)) { case ChatType.TextChatType: item = _textMsg(msg); break; case ChatType.EmoticonType: item = _textGif(msg.msgContent); break; case ChatType.ImageChatType: item = _imgMsg(msg.msgContent); break; case ChatType.ShortVideoChatType: item = _videoMsg(context, msg.msgContent); break; case ChatType.ShortVoiceChatType: item = _soundMsg(); break; case ChatType.RedWalletChatType: item = RedBagItem( Key(msg.time.toString()), UserData().basicInfo.userId, msg); break; case ChatType.PlaceChatType: item = PlaceItem(isMe: true, placeContent: msg.msgContent); break; case ChatType.FileChatType: item = _fileMsgItem(); break; default: } return wrapItemWithMenu(item); } Widget _fileMsgItem() { return UploadImgItem( msg: widget.msg, cancelToken: _cancelToken, child: Container( height: 100, constraints: BoxConstraints(maxWidth: Screen.width - 120), padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5), decoration: BoxDecoration( color: isLongPressed ? Colors.grey[300] : SendMsgBg, borderRadius: BorderRadius.all(Radius.circular(ChatRadius))), child: FileMsgItem(widget.msg))); } Widget wrapItemWithMenu(item) { List actionsFunc = []; List actions = [ I18n.of(context).reply, I18n.of(context).delete, ]; actionsFunc.add(() { print('发送引用的消息'); MessageMgr().emit('Reply Select Message', widget.msg); }); actionsFunc.add(() { MessageMgr().emit('Delete Select Message', widget.msg); }); ///转发 if (widget.msg.msgType == ChatType.TextChatType.value || widget.msg.msgType == ChatType.ImageChatType.value || widget.msg.msgType == ChatType.ShortVideoChatType.value || widget.msg.msgType == ChatType.PlaceChatType.value || widget.msg.msgType == ChatType.EmoticonType.value || widget.msg.msgType == ChatType.FileChatType.value) { actions.add(I18n.of(context).forward); actionsFunc.add(() { print('转发消息'); if (widget.msg.msgType == ChatType.FileChatType.value && widget.msg.localFile == null) { showToast('请先下载文件'); return; } AppNavigator.pushForwardPage(context, widget.msg); }); } if (widget.msg.msgType == ChatType.FileChatType.value && widget.msg.localFile != null) { //分享文件 actions.add(I18n.of(context).copy_download_url); actionsFunc.add(() async { //如果是文件增加复制下载地址 String path = widget.msg.localFile; UploadUtil().copyFileUrl(widget.msg, context); String type = 'file'; if (path.contains('mp4') || path.contains('mp3')) { type = 'video'; } else if (path.contains('png') || path.contains('jpg') || path.contains('jpeg') || path.contains('JPG') || path.contains('PNG')) { type = 'image'; } ShareExtend.share(FileCacheMgr.replacePath(path), type); }); } if (widget.msg.msgType == ChatType.TextChatType.value) { actions.insert(0, I18n.of(context).copy); actionsFunc.insert(0, () { //复制当前的文字 print('复制文字 ${textList[curTextType]}'); ClipboardData clipboardData = ClipboardData(text: textList[curTextType]); Clipboard.setData(clipboardData); }); } if (widget.msg.msgType == ChatType.ShortVoiceChatType.value) { var soundPlayMode = Provider.of(context).soundPlayMode; actions.add(soundPlayMode ? I18n.of(context).handset_playback : I18n.of(context).speaker_play); actionsFunc.add(() { Provider.of(context) .changeSoundPlayMode(!soundPlayMode); SoundUtils.instance.savePlayModeConfig(soundPlayMode); }); } // String date2 = DateTime.fromMillisecondsSinceEpoch(widget.msg.time).toString(); bool isUrl = false; if (widget.msg.msgType == ChatType.TextChatType.value) { if (textList[curTextType].contains('http')) { isUrl = true; } } return WPopupMenu( child: item, actions: actions, onTap: () async { if (isUrl) { if (await canLaunch(textList[curTextType])) { launch(textList[curTextType]); } } }, onLongPressStart: () { isLongPressed = true; setState(() {}); }, onLongPressEnd: () { isLongPressed = false; setState(() {}); }, onValueChanged: (int value) { print('选择的是$value个菜单'); if (value >= 0 && value < actionsFunc.length) { actionsFunc[value](); } }, ); } Widget _getSentMessageLayout(BuildContext context) { bool hasHeadImg = true; if (UserData().basicInfo.headimgurl == null || UserData().basicInfo.headimgurl.length == 0) { hasHeadImg = false; } return Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, children: [ MsgStateWidget(widget.msg), SizedBox(width: 3), _msgLayout(context, widget.msg), SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: hasHeadImg ? CachedNetworkImage( imageUrl: UserData().basicInfo.headimgurl, placeholder: (context, url) => Image.asset( Constants.DefaultHeadImgUrl, width: 40, height: 40, ), width: 40, height: 40, ) : SizedBox( width: 40, height: 40, child: Image.asset(R.assetsImagesDefaultNorAvatar))), ], ) ]); } _receiveJIF(MsgModel msg) { var text = utf8.decode(msg.msgContent); return Container( constraints: BoxConstraints(maxWidth: Screen.width - 120), child: extendedText(text, selectionEnabled: false, hideKeyboard: widget.hideKeyboard)); } double _getTextWidth(String text, {double fontSize = FontSize}) { var tp = TextPainter( text: TextSpan( style: TextStyle(fontSize: fontSize), text: textList[curTextType]), textAlign: TextAlign.left, textDirection: TextDirection.ltr, textScaleFactor: 1, ); tp.layout(maxWidth: maxWidth); if (text.contains('[')) { if (text.length < 5 && text.startsWith('[') && text.endsWith(']')) { ///单表情 return 25; } int length = text.split('[').length; if (length > 1) { ///包含表情 int scale = 6; if (length > 6) scale = 7; double width = tp.width + length * scale; return width > (maxWidth) ? maxWidth : width; } } return tp.width; ///单文字 } blueDot(bool isShow) { return Container( margin: EdgeInsets.only(right: 5), decoration: BoxDecoration( shape: BoxShape.circle, color: isShow ? Color(0xFFFF7E00) : Color(0xFFCFCFCF), ), width: 6, height: 6, ); } _receiveText(MsgModel msg) { List showMsg = []; double width = 0.0; if (textList.length > 0) { bool isUrl = false; if (textList[curTextType].contains('http')) { isUrl = true; } if (msg.refMsgContent != null && msg.refMsgContent.length > 0) { QuoteMsg quoteMsg = QuoteMsg.fromBuffer(msg.refMsgContent); var refName = Provider.of(context) .getGroupRefName(msg.sessionId, quoteMsg.sendUserId); var msgDate = DateTime.fromMillisecondsSinceEpoch(quoteMsg.sendTime.toInt()); var showTimeStr = getTodayTime(msgDate, sendTime: quoteMsg.sendTime.toInt()); var timetWidth = CustomUI.getRealTextWidht(showTimeStr, 13); var nameWidhth = CustomUI.getRealTextWidht(refName, 13); width = timetWidth + (nameWidhth > 70 ? 70 : nameWidhth) + 36; var quoteContent = quoteMsg.content.substring(quoteMsg.content.indexOf(':') + 2); var contentWidht = _getTextWidth(quoteContent, fontSize: 13); width = min(max(width, contentWidht), maxWidth); showMsg.add(InkWell( onTap: () { MessageMgr().emit('Jump to Msg', quoteMsg.sendTime.toInt()); }, child: Container( padding: EdgeInsets.all(8), margin: EdgeInsets.only(bottom: 5), decoration: BoxDecoration( color: Color(0xFFC4E0F5), borderRadius: BorderRadius.circular(5.5)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ Container( constraints: BoxConstraints(maxWidth: 70), child: Text( refName, style: TextStyle(fontSize: 13), overflow: TextOverflow.ellipsis, )), SizedBox(width: 20), Text( showTimeStr, style: TextStyle(fontSize: 13), ) ]), SizedBox(height: 7.5), Text( quoteContent, style: TextStyle(fontSize: 13), ) ], ), ))); } var contentWidth = _getTextWidth(textList[curTextType]); if (contentWidth > width && contentWidth <= maxWidth) { width = contentWidth; } showMsg.add(InkWell( onTap: () { if (msg.transTag == 1) { return; } if (msg.transTag == 2 || msg.transTag == 3) { setState(() { curTextType += 1; curTextType %= textList.length; }); return; } }, child: Container( constraints: BoxConstraints(maxWidth: maxWidth, minHeight: 22), alignment: Alignment.centerLeft, child: extendedText( textList[curTextType], color: isUrl ? Colors.blue : Constants.BlackTextColor, hideKeyboard: widget.hideKeyboard, fontSize: FontSize, )))); } var minWidth = width + 20; if (msg.transTag != 0) { minWidth = 200; showMsg.add(Padding( padding: EdgeInsets.symmetric(vertical: 5), child: Divider(color: Color(0xFFECECEC), height: 1))); Widget tranWidget = _transProcessWidget(msg.transTag); showMsg.add(tranWidget); } Widget text = Stack(children: [ Container( width: width + 20, constraints: BoxConstraints(maxWidth: Screen.width - 120, minWidth: minWidth), padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: showMsg), decoration: BoxDecoration( border: Border.all(color: ReciveBorderColor, width: 0.5), color: isLongPressed ? Colors.grey[300] : Colors.white, borderRadius: BorderRadius.all(Radius.circular(ChatRadius))), ), msg.transTag != 1 && msg.transTag != 0 ? Positioned( right: 5, top: 5, child: Container( child: Row(children: [ blueDot(curTextType == 0), blueDot(curTextType == 1), blueDot(curTextType == 2), //blueDot(true), ]))) : Container() ]); return text; } //用户评价人工翻译,差评 rateTranslateResult() async { return await HttpUtil().rateTranslateResult(widget.msg, () { if (mounted) { setState(() {}); } }); } _translateItemWidget(int code, String title, Function onTap) { Color color = onTap == null ? Constants.GreyTextColor : Color(0xFF087FF3); return InkWell( onTap: onTap, splashColor: Colors.red, child: Container( width: 80, child: Row( children: [ Icon( IconData(code, fontFamily: Constants.IconFontFamily), color: color, size: 20, ), SizedBox(width: 5), Expanded( child: SizedBox( child: Text( title, style: TextStyle(color: color, fontSize: 10), textScaleFactor: 1.0, maxLines: 1, overflow: TextOverflow.ellipsis, ), )) ], ))); } bool isTranslating = false; _transProcessWidget(int transTag) { double width = 160; Widget userTranslateWidget; Widget machineTranslateWidget; if (transTag == 1) { //机器翻译中 userTranslateWidget = _translateItemWidget(0xe670, I18n.of(context).man_retranslate, null); machineTranslateWidget = _translateItemWidget(0xe671, I18n.of(context).robotTranslate, null); } else if (transTag == 2) { //人工翻译中 userTranslateWidget = _translateItemWidget(0xe670, I18n.of(context).ManTranslate, null); machineTranslateWidget = _translateItemWidget( 0xe671, I18n.of(context).robot_retranslate, textList.length <= 1 ? null : () { setState(() { curTextType += 1; curTextType %= textList.length; }); }); } else if (transTag == 3) { //机器翻译完成 userTranslateWidget = _translateItemWidget( 0xe670, I18n.of(context).man_retranslate, isTranslating ? null : () async { isTranslating = true; int money = Provider.of(context, listen: false) .money; int voucher = Provider.of(context, listen: false) .voucher; int needMoney = widget.msg.getNeedMoney(); if (needMoney > voucher + money) { showToast('翻译券和H币不足'); return; } var res = await HttpUtil().getPersonalTranslate(widget.msg); if (res) { print('请求人工翻译成功,进行扣费'); setState(() { widget.msg.transTag = 2; //优先扣券 if (voucher > 0) { int costQuan = min(voucher, needMoney); Provider.of(context, listen: false) .subVoucher(costQuan); } //不足的话再扣H币 if (needMoney > voucher) { Provider.of(context, listen: false) .subMoney(needMoney - voucher); } }); } }); machineTranslateWidget = _translateItemWidget( 0xe671, I18n.of(context).robot_retranslate, textList.length <= 1 ? null : () { setState(() { curTextType += 1; curTextType %= textList.length; }); }); } else if (transTag == 4 || transTag == 10) { //4人工翻译完成,未评论 10人工翻译完成已评论 userTranslateWidget = InkWell( onTap: () { setState(() { curTextType = 0; }); }, child: Container( width: width / 2, child: Row( children: [ InkWell( child: Icon( IconData(0xe641, fontFamily: Constants.IconFontFamily), size: 18, color: transTag == 10 ? Colors.grey : Color(0xFF087FF3)), onTap: transTag == 10 ? null : () { CustomUI.showIosDialog( context, I18n.of(context).bad_ev, () async { bool isSuccess = await rateTranslateResult(); if (isSuccess) { Navigator.of(context).pop(true); showToast(I18n.of(context).success); } else { showToast(I18n.of(context).fail); Navigator.of(context).pop(); } }, () { Navigator.of(context).pop(); }); }, ), SizedBox(width: 5), Expanded( child: SizedBox( child: Text( I18n.of(context).over, style: TextStyle(color: Color(0xFF087FF3), fontSize: 10), textScaleFactor: 1.0, maxLines: 1, overflow: TextOverflow.ellipsis, ), )) ], ))); machineTranslateWidget = _translateItemWidget(0xe675, I18n.of(context).see_original, () { setState(() { curTextType = textList.length - 1; }); }); } return Container( height: 26, alignment: Alignment.center, child: Row( children: [ userTranslateWidget, Expanded( child: SizedBox(child: VerticalDivider(), height: 20, width: 2)), machineTranslateWidget ], ), ); } _receiveImg(BuildContext context, List imgData, {String downloadData}) { // ImageProvider provider = MemoryImage(Uint8List.fromList(imgData)); ImageProvider provider = MemoryImage(widget.msg.localFile == null ? Uint8List.fromList(imgData) : File(widget.msg.localFile).readAsBytesSync()); var imgSize = _getImgSize(); return DownloadItem( isAutoDown: false, msg: widget.msg, onFinishTap: () { widget.hideKeyboard(); showFullImg(context, widget.msg); }, child: Container( alignment: Alignment.centerLeft, width: imgSize.width, height: imgSize.height, child: ClipRRect( child: Image( image: provider ?? AssetImage(R.assetsImagesIcAlbum), ), borderRadius: BorderRadius.circular(5), ), ), ); } _receiveVideo(BuildContext context, List imgData, {String downloadData}) { ImageProvider provider = MemoryImage(Uint8List.fromList(imgData)); var imgSize = _getImgSize(); return InkWell( onTap: () { if (widget.msg.localFile != null) { showVideoPage(context, widget.msg.localFile); } }, child: DownloadItem( isAutoDown: false, msg: widget.msg, child: Container( width: imgSize.width, height: imgSize.height, child: ClipRRect( child: Image( image: provider ?? AssetImage(R.assetsImagesIcAlbum), ), borderRadius: BorderRadius.circular(5), ), ), )); } showVideoPage(BuildContext context, String filePath) { widget.hideKeyboard(); Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return VideoPage(videoPath: filePath); })); } _receiveSound(BuildContext context, List soundData) { print(' build 收到的语音消息'); MsgModel msg = widget.msg; var time = widget.msg.extraInfo / 1000; if (time > 60) { time = 60.0; } bool isPlaying = false; if (curSoundUrl != null) { isPlaying = SoundUtils().isPlaying(curSoundUrl); } var soundWidget = InkWell( onTap: () async { bool isLocal = true; if (msg.localFile != null) { isLocal = true; curSoundUrl = msg.localFile; } else { isLocal = false; var sessionId = msg.sessionId; curSoundUrl = UploadUtil() .getFullUrl(msg.extraFile, sessionId, msg.channelType); } print('当前文件$curSoundUrl 本地?$isLocal'); if (isPlaying) { await SoundUtils().pause(); } else { print('开始播放'); if (widget.msg.soundListened == 0) { widget.msg.updateSoundListened(); } await SoundUtils().play(curSoundUrl, isLocal: isLocal, onPlayed: () { if (mounted) { this.setState(() {}); } }, complete: () { if (mounted) { this.setState(() {}); } }); } }, child: Container( width: 130, child: Row(children: [ Container( alignment: Alignment.center, padding: EdgeInsets.only(bottom: 2), margin: EdgeInsets.only(right: 10), width: 25.5, height: 25.5, decoration: BoxDecoration( border: Border.all(color: const Color(0xFF1B92C7), width: 0.5), color: const Color(0xFF04A4FE), shape: BoxShape.circle), child: Icon( IconData(isPlaying ? 0xe652 : 0xe653, fontFamily: Constants.IconFontFamily), size: isPlaying ? 15 : 18, color: Colors.white, ), ), isPlaying ? Stack( children: [ Container( height: 18, width: 19, ), Positioned( bottom: 0, child: VideoAnim( begin: 18, start: 0.444, end: 4.5, )), Positioned( left: 7, bottom: 0, child: VideoAnim( begin: 4.5, end: 18, )), Positioned( left: 14, bottom: 0, child: VideoAnim( begin: 18, end: 4.5, )) ], ) : Stack( children: [ Container( height: 18, width: 19, ), Positioned( bottom: 0, child: CustomUI.buildAudioContaniner(12)), Positioned( left: 7, bottom: 0, child: CustomUI.buildAudioContaniner(4.5)), Positioned( left: 14, bottom: 0, child: CustomUI.buildAudioContaniner(18)) ], ), Expanded(child: SizedBox()), fixedText(time.toStringAsFixed(0), color: Constants.BlackTextColor, fontSize: 16) ])), ); List showMsg = []; showMsg.add(DownloadItem( msg: widget.msg, child: soundWidget, isShowProgress: false, )); double width = 130; double minWidth = 0; if (textList.length > 0) { width = _getTextWidth(textList[curTextType]) + 20; width = min(width, Screen.width - 120); minWidth = max(width, 150); showMsg.add(Padding( padding: EdgeInsets.symmetric(vertical: 5), child: Divider( height: 1, ))); showMsg.add(Container( child: extendedText( textList[curTextType], color: Constants.BlackTextColor, hideKeyboard: widget.hideKeyboard, fontSize: FontSize, ), alignment: Alignment.centerLeft, constraints: BoxConstraints(maxWidth: Screen.width - 120, minHeight: 22), )); } if (msg.transTag != 0) { minWidth = 200; showMsg.add(Divider(color: Color(0xFFECECEC), height: 3)); Widget tranWidget = _transProcessWidget(msg.transTag); showMsg.add(tranWidget); } return Container( width: width + 20, constraints: msg.transTag != 0 ? BoxConstraints(minWidth: minWidth) : null, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: showMsg), padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5), decoration: BoxDecoration( color: Colors.white, border: Border.all(color: ReciveBorderColor, width: 0.5), borderRadius: BorderRadius.all(Radius.circular(ChatRadius))), ); } void showFullImg(BuildContext context, MsgModel msg) { print('显示图片'); Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return PhotoPage(msg: msg); })); } _reveiveMsg(BuildContext context) { Widget item; switch (ChatType.valueOf(widget.msg.msgType)) { case ChatType.TextChatType: item = _receiveText(widget.msg); break; case ChatType.EmoticonType: item = _receiveJIF(widget.msg); break; case ChatType.ImageChatType: if (widget.msg.extraFile != null) { item = _receiveImg(context, widget.msg.msgContent, downloadData: widget.msg.extraFile); } else { item = _receiveImg(context, widget.msg.msgContent); } break; case ChatType.ShortVideoChatType: item = _receiveVideo(context, widget.msg.msgContent, downloadData: widget.msg.extraFile); break; case ChatType.ShortVoiceChatType: item = _receiveSound(context, widget.msg.msgContent); break; case ChatType.PlaceChatType: item = PlaceItem(isMe: false, placeContent: widget.msg.msgContent); break; case ChatType.FileChatType: item = _receiveFileMsgItem(); break; default: } return wrapItemWithMenu(item); } Widget _receiveFileMsgItem() { return DownloadItem( isAutoDown: false, msg: widget.msg, onComplete: () { if (mounted) { setState(() {}); } }, child: Container( height: 100, constraints: BoxConstraints(maxWidth: Screen.width - 120), padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5), decoration: BoxDecoration( color: Colors.white, border: Border.all(color: ReciveBorderColor, width: 0.5), borderRadius: BorderRadius.all(Radius.circular(ChatRadius))), child: FileMsgItem(widget.msg))); } Widget _getReceivedMessageLayout(BuildContext context) { bool hasHeadImg = true; var memberModel = widget.memberModel; if (memberModel == null) { return Container(); } if (memberModel.avtar == null || memberModel.avtar.length == 0) { hasHeadImg = false; } GroupInfoModel infoModel = Provider.of(context); var refName = Provider.of(context) .getGroupRefName(infoModel.sessionId, memberModel.memberId); bool isShowSoundSate = widget.msg.msgType == ChatType.ShortVoiceChatType.value && widget.msg.soundListened == 0; return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.only(right: 8.0), child: GestureDetector( child: ClipRRect( borderRadius: BorderRadius.circular(8), child: hasHeadImg ? CachedNetworkImage( imageUrl: memberModel.avtar, placeholder: (context, url) => Image.asset( Constants.DefaultHeadImgUrl, width: 40, height: 40, ), width: 40, height: 40, ) : SizedBox( width: 40, height: 40, child: Image.asset(R.assetsImagesDefaultNorAvatar))), onTap: () { AppNavigator.pushProfileInfoPage(context, memberModel.memberId, fromWhere: 2, addMode: !FriendListMgr().isMyFriend(memberModel.memberId) ? 1 : 0); }, onLongPress: () { print('long press user'); MessageMgr().emit('Alter User Message', memberModel); }, )), infoModel.isShowName > 0 ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.only(left: 9), child: fixedText(refName, fontSize: 12, color: Colors.grey), ), SizedBox(height: 2), _reveiveMsg(context), ], ) : _reveiveMsg(context), isShowSoundSate ? Container( margin: EdgeInsets.only( left: 8, top: infoModel.isShowName > 0 ? 38 : 20), child: CircleAvatar( radius: 3.5, backgroundColor: Colors.red, )) : Container() ]); } }