Hibok
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

1559 行
50 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'dart:math';
  4. import 'dart:typed_data';
  5. import 'package:cached_network_image/cached_network_image.dart';
  6. import 'package:chat/chat/download_item.dart';
  7. import 'package:chat/chat/file_msg_item.dart';
  8. import 'package:chat/chat/msg_state_widge.dart';
  9. import 'package:chat/chat/place_item.dart';
  10. import 'package:chat/chat/redbag_widget.dart';
  11. import 'package:chat/chat/upload_item.dart';
  12. import 'package:chat/chat/video_view.dart';
  13. import 'package:chat/data/UserData.dart';
  14. import 'package:chat/data/constants.dart';
  15. import 'package:chat/utils/friend_list_mgr.dart';
  16. import 'package:chat/utils/wpop/w_popup_menu.dart';
  17. import 'package:flutter/services.dart';
  18. import 'package:chat/generated/i18n.dart';
  19. import 'package:chat/home/invite_detail_page.dart';
  20. import 'package:chat/models/ChatMsg.dart';
  21. import 'package:chat/models/group_info_model.dart';
  22. import 'package:chat/models/keyboard_provider.dart';
  23. import 'package:chat/proto/chat.pbenum.dart';
  24. import 'package:chat/proto/chat.pbserver.dart';
  25. import 'package:chat/utils/CustomUI.dart';
  26. import 'package:chat/utils/MessageMgr.dart';
  27. import 'package:chat/utils/app_navigator.dart';
  28. import 'package:chat/utils/date_utils.dart';
  29. import 'package:chat/utils/group_member_model.dart';
  30. import 'package:chat/utils/msgHandler.dart';
  31. import 'package:chat/utils/screen.dart';
  32. import 'package:chat/utils/sound_util.dart';
  33. import 'package:chat/utils/upload_util.dart';
  34. import 'package:chat/utils/video_anim.dart';
  35. import 'package:dio/dio.dart';
  36. import 'package:flutter/cupertino.dart';
  37. import 'package:flutter/material.dart';
  38. import 'package:provider/provider.dart';
  39. import 'package:oktoast/oktoast.dart';
  40. import 'package:chat/utils/HttpUtil.dart';
  41. import 'package:share_extend/share_extend.dart';
  42. import 'package:url_launcher/url_launcher.dart';
  43. import '../r.dart';
  44. import '../utils/file_cache_mgr.dart';
  45. import 'full_img_view.dart';
  46. import 'upload_item.dart';
  47. import 'package:chat/models/ref_name_provider.dart';
  48. import 'package:chat/models/money_change.dart';
  49. import 'package:chat/models/voucher_change.dart';
  50. const double ChatRadius = 7.5;
  51. const Color SendMsgBg = Color(0xFFD4F0FF);
  52. const Color SendMsgText = Colors.black;
  53. const double TextHeight = 1.2;
  54. const double PaddingLeft = 9.5;
  55. const Color ReciveBorderColor = Color(0xFFDCDCDC);
  56. const double FontSize = 15;
  57. class GroupChatPageItem extends StatefulWidget {
  58. final MsgModel msg;
  59. final int lastMsgTime;
  60. final GroupMemberModel memberModel;
  61. final Function hideKeyboard;
  62. const GroupChatPageItem(
  63. {Key key,
  64. this.msg,
  65. this.lastMsgTime,
  66. this.hideKeyboard,
  67. this.memberModel})
  68. : assert(msg != null),
  69. super(key: key);
  70. @override
  71. _GroupChatPageItemState createState() => _GroupChatPageItemState();
  72. }
  73. class _GroupChatPageItemState extends State<GroupChatPageItem>
  74. with SingleTickerProviderStateMixin {
  75. int curTextType = 0; //文字、译文切换索引
  76. List<String> textList = [];
  77. String curSoundUrl;
  78. CancelToken _cancelToken = CancelToken();
  79. bool isLongPressed = false;
  80. double maxWidth = Screen.width - 140;
  81. @override
  82. void initState() {
  83. super.initState();
  84. if (widget.msg.from == UserData().basicInfo.userId &&
  85. widget.msg.state == MsgState.None) {
  86. print('重新发送消息');
  87. MsgHandler.sendChatMsg(widget.msg);
  88. }
  89. textList = widget.msg.getTransTextList();
  90. MessageMgr().on('Update Translate Message', updateTranslateMsg);
  91. MessageMgr().on('Cancel Request', _deleteItem);
  92. }
  93. @override
  94. void dispose() {
  95. MessageMgr().off('Cancel Request', _deleteItem);
  96. MessageMgr().off('Update Translate Message', updateTranslateMsg);
  97. super.dispose();
  98. }
  99. _deleteItem(msg) {
  100. if (msg == widget.msg) {
  101. print(widget.msg.state);
  102. if (widget.msg.state == MsgState.Uploading) {
  103. print('取消上传');
  104. UploadUtil().cancelRequests(_cancelToken);
  105. }
  106. }
  107. }
  108. updateTextList() {
  109. textList.clear();
  110. curTextType = 0;
  111. textList = widget.msg.getTransTextList();
  112. }
  113. updateTranslateMsg(msg) {
  114. if (msg.time == widget.msg.time) {
  115. if (mounted) {
  116. updateTextList();
  117. if (msg.transTag == 5 || msg.transTag == 6) {
  118. print('人工翻译失败');
  119. //更新最新的金额
  120. HttpUtil().getWealth(context, (data) {
  121. if (mounted) {
  122. Provider.of<MoneyChangeProvider>(context)
  123. .initMoney(data['CoinValue']);
  124. Provider.of<VoucherChangeProvider>(context)
  125. .initVoucher(data['Voucher']);
  126. }
  127. });
  128. }
  129. setState(() {
  130. print('更新翻译文字');
  131. });
  132. }
  133. }
  134. }
  135. getTodayTime(msgDate, {var sendTime}) {
  136. String showTimeStr;
  137. sendTime = sendTime ?? widget.msg.time;
  138. var today = DateTime.now();
  139. //今天
  140. if (msgDate.year == today.year &&
  141. msgDate.month == today.month &&
  142. msgDate.day == today.day) {
  143. showTimeStr =
  144. DateUtils().getFormartData(timeSamp: sendTime, format: 'HH:mm');
  145. } else {
  146. showTimeStr =
  147. DateUtils().getFormartData(timeSamp: sendTime, format: 'MM/dd HH:mm');
  148. }
  149. return showTimeStr;
  150. }
  151. String getMsgTime() {
  152. String showTimeStr;
  153. var sendTime = widget.msg.time;
  154. var msgDate = DateTime.fromMillisecondsSinceEpoch(sendTime);
  155. if (widget.lastMsgTime == null) {
  156. showTimeStr = getTodayTime(msgDate);
  157. } else {
  158. if (sendTime - widget.lastMsgTime > 3 * 60 * 1000) {
  159. showTimeStr = getTodayTime(msgDate);
  160. }
  161. }
  162. return showTimeStr;
  163. }
  164. @override
  165. Widget build(BuildContext context) {
  166. var showTime = getMsgTime();
  167. return Container(
  168. width: Screen.width,
  169. color: Colors.white.withOpacity(0),
  170. margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 4),
  171. child: Column(
  172. children: <Widget>[
  173. showTime == null
  174. ? SizedBox()
  175. : Container(
  176. decoration: BoxDecoration(
  177. color: Color(0xFF2C2F36).withOpacity(0.25),
  178. borderRadius: BorderRadius.all(Radius.circular(9.5))),
  179. padding:
  180. EdgeInsets.only(left: 7, right: 7, top: 3, bottom: 2),
  181. child: Text(showTime,
  182. textScaleFactor: 1.0,
  183. style: TextStyle(fontSize: 10.0, color: Colors.white)),
  184. ),
  185. SizedBox(height: 10),
  186. _msgWidget()
  187. ],
  188. ),
  189. );
  190. }
  191. _msgWidget() {
  192. if (widget.msg.from == 0) {
  193. return _serverNotifyMsg();
  194. } else {
  195. if (widget.msg.from == UserData().basicInfo.userId) {
  196. return _getSentMessageLayout(context);
  197. } else {
  198. return _getReceivedMessageLayout(context);
  199. }
  200. }
  201. }
  202. _serverNotifyMsg() {
  203. var type = widget.msg.msgType;
  204. if (type == ChatType.GroupChatNoticeType.value) {
  205. var res = GroupChatNotice.fromBuffer(widget.msg.msgContent);
  206. var groupInfoModel = Provider.of<GroupInfoModel>(context);
  207. var optId = res.operatuId;
  208. var showStr = MsgHandler.getGroupNoticeMsg(res, groupInfoModel);
  209. return Container(
  210. alignment: Alignment.center,
  211. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  212. child: extendedText(showStr,
  213. color: Constants.GreyTextColor,
  214. fontSize: 12, onSpecialTextTap: (dynamic parameter) {
  215. // print('点击事件 $parameter');
  216. if (parameter.startsWith("\$")) {
  217. // showToast('点击: ${res.operateduId[0]}');
  218. List<GroupMemberModel> list = [];
  219. for (int k = 0; k < res.operateduId.length; k++) {
  220. list.add(GroupMemberModel.fromBaseInfo(res.operateduId[k]));
  221. }
  222. AppNavigator.push(
  223. context,
  224. InviteDetailPage(
  225. originalList: list,
  226. opt: GroupMemberModel.fromBaseInfo(optId),
  227. groupId: groupInfoModel.sessionId,
  228. ));
  229. }
  230. }),
  231. );
  232. }
  233. return Container();
  234. }
  235. _textGif(List<int> msgContent) {
  236. var msg = utf8.decode(msgContent);
  237. return Container(
  238. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  239. child: extendedText(
  240. msg,
  241. selectionEnabled: false,
  242. hideKeyboard: widget.hideKeyboard,
  243. fontSize: FontSize,
  244. color: Colors.black,
  245. ),
  246. );
  247. }
  248. double getQuoteContentWidth(QuoteMsg quoteMsg, String refName,
  249. String showTimeStr, String quoteContent) {
  250. double width;
  251. var timetWidth = CustomUI.getRealTextWidht(showTimeStr, 13);
  252. var nameWidhth = CustomUI.getRealTextWidht(refName, 13);
  253. width = timetWidth + (nameWidhth > 70 ? 70 : nameWidhth) + 59;
  254. var contentWidht = _getTextWidth(quoteContent, fontSize: 13);
  255. return min(max(width, contentWidht), maxWidth);
  256. }
  257. Widget getQuoteContent(QuoteMsg quoteMsg, String refName, String showTimeStr,
  258. String quoteContent, bool isSend) {
  259. var strColor;
  260. var bgColor;
  261. if (isSend) {
  262. strColor = const Color(0xFF24343C);
  263. bgColor = const Color(0xFFC4E0F5);
  264. } else {
  265. strColor = const Color(0xFF515151);
  266. bgColor = const Color(0xFFEBEBEC);
  267. }
  268. return Container(
  269. padding: EdgeInsets.only(left: 8, bottom: 8),
  270. margin: EdgeInsets.only(bottom: 5, top: 2),
  271. decoration:
  272. BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(7)),
  273. child: Column(
  274. crossAxisAlignment: CrossAxisAlignment.start,
  275. children: <Widget>[
  276. Row(children: <Widget>[
  277. Container(
  278. constraints: BoxConstraints(maxWidth: 70),
  279. child: Text(
  280. refName,
  281. style: TextStyle(fontSize: 13, color: strColor),
  282. overflow: TextOverflow.ellipsis,
  283. )),
  284. SizedBox(width: 10),
  285. Text(
  286. showTimeStr,
  287. style: TextStyle(fontSize: 13, color: strColor),
  288. ),
  289. Expanded(child: SizedBox()),
  290. InkWell(
  291. onTap: () {
  292. MessageMgr().emit('Jump to Msg', quoteMsg.sendTime.toInt());
  293. },
  294. child: Container(
  295. alignment: Alignment.center,
  296. height: 27,
  297. padding: EdgeInsets.only(
  298. left: 10,
  299. right: 10,
  300. ),
  301. child: Icon(
  302. IconData(0xe67e, fontFamily: Constants.IconFontFamily),
  303. size: 13,
  304. color: strColor,
  305. )))
  306. ]),
  307. Padding(
  308. padding: EdgeInsets.only(right: 8),
  309. child: Text(
  310. quoteContent,
  311. style: TextStyle(fontSize: 13, color: strColor),
  312. ),
  313. ),
  314. ],
  315. ),
  316. );
  317. }
  318. Map getQuoteItem(MsgModel msgModel) {
  319. QuoteMsg quoteMsg = QuoteMsg.fromBuffer(widget.msg.refMsgContent);
  320. var refName = Provider.of<RefNameProvider>(context)
  321. .getGroupRefName(msgModel.sessionId, quoteMsg.sendUserId);
  322. var msgDate =
  323. DateTime.fromMillisecondsSinceEpoch(quoteMsg.sendTime.toInt());
  324. var showTimeStr =
  325. getTodayTime(msgDate, sendTime: quoteMsg.sendTime.toInt());
  326. var tempIndex = quoteMsg.content.indexOf(':');
  327. var quoteContent = tempIndex == -1
  328. ? quoteMsg.content
  329. : quoteMsg.content.substring(quoteMsg.content.indexOf(':') + 2);
  330. return {
  331. "quoteMsg": quoteMsg,
  332. "refName": refName,
  333. "showTimeStr": showTimeStr,
  334. "quoteContent": quoteContent,
  335. };
  336. }
  337. _textMsg(MsgModel msgModel) {
  338. List<Widget> showMsg = [];
  339. var width = 0.0;
  340. var msg = utf8.decode(msgModel.msgContent);
  341. if (msg.contains('[ ')) {
  342. msg = msg.replaceAll('[ ', '[');
  343. }
  344. if (msg.contains(' ]')) {
  345. msg = msg.replaceAll(' ]', ']');
  346. }
  347. bool isUrl = false;
  348. if (textList[curTextType].contains('http')) {
  349. isUrl = true;
  350. }
  351. if (msgModel.refMsgContent != null && msgModel.refMsgContent.length > 0) {
  352. var quoteItem = getQuoteItem(msgModel);
  353. width = getQuoteContentWidth(quoteItem['quoteMsg'], quoteItem['refName'],
  354. quoteItem['showTimeStr'], quoteItem['quoteContent']);
  355. showMsg.add(getQuoteContent(quoteItem['quoteMsg'], quoteItem['refName'],
  356. quoteItem['showTimeStr'], quoteItem['quoteContent'], true));
  357. }
  358. var contentWidth = _getTextWidth(textList[curTextType]);
  359. width = min(max(contentWidth, width), maxWidth);
  360. if (textList[curTextType] != '') {
  361. showMsg.add(Container(
  362. constraints: BoxConstraints(maxWidth: maxWidth, minHeight: 22),
  363. alignment: Alignment.centerLeft,
  364. child: extendedText(
  365. textList[curTextType],
  366. color: isUrl ? Colors.blue : Constants.BlackTextColor,
  367. hideKeyboard: widget.hideKeyboard,
  368. fontSize: FontSize,
  369. )));
  370. }
  371. return Container(
  372. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  373. width: width + 20,
  374. child: Column(
  375. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  376. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  377. decoration: BoxDecoration(
  378. color: isLongPressed ? Colors.grey[300] : SendMsgBg,
  379. border: Border.all(color: Color(0xFFB9CBD7), width: 0.6),
  380. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  381. );
  382. }
  383. _soundMsg() {
  384. double time = widget.msg.extraInfo / 1000;
  385. if (time > 60) {
  386. time = 60.0;
  387. }
  388. bool isPlaying = false;
  389. var soundPath = widget.msg.localFile;
  390. isPlaying = SoundUtils().isPlaying(soundPath);
  391. var soundWidget = GestureDetector(
  392. child: Container(
  393. width: 120,
  394. child: Row(
  395. mainAxisAlignment: MainAxisAlignment.end,
  396. children: <Widget>[
  397. Container(
  398. alignment: Alignment.center,
  399. padding: EdgeInsets.only(bottom: 2),
  400. margin: EdgeInsets.only(right: 10),
  401. width: 25.5,
  402. height: 25.5,
  403. decoration: BoxDecoration(
  404. border:
  405. Border.all(color: const Color(0xFF1B92C7), width: 0.5),
  406. color: const Color(0xFF04A4FE),
  407. shape: BoxShape.circle),
  408. child: Icon(
  409. IconData(isPlaying ? 0xe652 : 0xe653,
  410. fontFamily: Constants.IconFontFamily),
  411. size: isPlaying ? 15 : 18,
  412. color: Colors.white,
  413. ),
  414. ),
  415. isPlaying
  416. ? Stack(
  417. children: <Widget>[
  418. Container(
  419. height: 18,
  420. width: 19,
  421. ),
  422. Positioned(
  423. bottom: 0,
  424. child: VideoAnim(
  425. begin: 18,
  426. start: 0.444,
  427. end: 4.5,
  428. )),
  429. Positioned(
  430. left: 7,
  431. bottom: 0,
  432. child: VideoAnim(
  433. begin: 4.5,
  434. end: 18,
  435. )),
  436. Positioned(
  437. left: 14,
  438. bottom: 0,
  439. child: VideoAnim(
  440. begin: 18,
  441. end: 4.5,
  442. ))
  443. ],
  444. )
  445. : Stack(
  446. children: <Widget>[
  447. Container(
  448. height: 18,
  449. width: 19,
  450. ),
  451. Positioned(
  452. bottom: 0, child: CustomUI.buildAudioContaniner(12)),
  453. Positioned(
  454. left: 7,
  455. bottom: 0,
  456. child: CustomUI.buildAudioContaniner(4.5)),
  457. Positioned(
  458. left: 14,
  459. bottom: 0,
  460. child: CustomUI.buildAudioContaniner(18))
  461. ],
  462. ),
  463. Expanded(child: SizedBox()),
  464. fixedText(time.toStringAsFixed(0), color: SendMsgText, fontSize: 16)
  465. ],
  466. ),
  467. padding: EdgeInsets.symmetric(horizontal: 15, vertical: 12),
  468. decoration: BoxDecoration(
  469. color: SendMsgBg,
  470. border: Border.all(color: Color(0xFFB9CBD7), width: 0.6),
  471. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  472. ),
  473. onTap: () async {
  474. print('播放状态 : $isPlaying');
  475. print('当前文件$soundPath ');
  476. if (isPlaying) {
  477. SoundUtils().pause();
  478. } else {
  479. SoundUtils().play(soundPath, onPlayed: () {
  480. if (mounted) {
  481. setState(() {});
  482. }
  483. }, complete: () {
  484. if (mounted) {
  485. setState(() {});
  486. }
  487. });
  488. }
  489. },
  490. );
  491. return UploadImgItem(
  492. msg: widget.msg,
  493. child: soundWidget,
  494. isShowProgress: false,
  495. );
  496. }
  497. Size _getImgSize() {
  498. double aspectRatio = widget.msg.extraInfo / 100;
  499. var maxWidth = Screen.width * 0.65;
  500. var maxHeight = Screen.height / 4;
  501. var width, height;
  502. if (maxWidth / maxHeight > aspectRatio) {
  503. height = maxHeight;
  504. width = maxHeight * aspectRatio;
  505. } else {
  506. width = maxWidth;
  507. height = maxWidth / aspectRatio;
  508. }
  509. return Size(width, height);
  510. }
  511. _imgMsg(List<int> imgData) {
  512. var imgSize = _getImgSize();
  513. ImageProvider provider = MemoryImage(widget.msg.localFile == null
  514. ? Uint8List.fromList(imgData)
  515. : File(widget.msg.localFile).readAsBytesSync());
  516. return GestureDetector(
  517. child: ClipRRect(
  518. child: UploadImgItem(
  519. msg: widget.msg,
  520. cancelToken: _cancelToken,
  521. child: Container(
  522. height: imgSize.height,
  523. width: imgSize.width,
  524. child: Image(
  525. fit: BoxFit.contain,
  526. image: provider,
  527. ),
  528. )),
  529. borderRadius: BorderRadius.circular(5),
  530. ),
  531. onTap: () async {
  532. showFullImg(context, widget.msg);
  533. });
  534. }
  535. _videoMsg(BuildContext context, List<int> thumbnail) {
  536. var imgSize = _getImgSize();
  537. return InkWell(
  538. child: ClipRRect(
  539. child: Stack(
  540. alignment: Alignment.center,
  541. children: <Widget>[
  542. UploadImgItem(
  543. msg: widget.msg,
  544. cancelToken: _cancelToken,
  545. child: Container(
  546. width: imgSize.width,
  547. height: imgSize.height,
  548. child: Image(
  549. fit: BoxFit.contain,
  550. image: MemoryImage(Uint8List.fromList(thumbnail)),
  551. ),
  552. ))
  553. ],
  554. ),
  555. borderRadius: BorderRadius.circular(5),
  556. ),
  557. onTap: () {
  558. showVideoPage(context, widget.msg.localFile);
  559. },
  560. );
  561. }
  562. _msgLayout(BuildContext context, MsgModel msg) {
  563. Widget item;
  564. switch (ChatType.valueOf(msg.msgType)) {
  565. case ChatType.TextChatType:
  566. item = _textMsg(msg);
  567. break;
  568. case ChatType.EmoticonType:
  569. item = _textGif(msg.msgContent);
  570. break;
  571. case ChatType.ImageChatType:
  572. item = _imgMsg(msg.msgContent);
  573. break;
  574. case ChatType.ShortVideoChatType:
  575. item = _videoMsg(context, msg.msgContent);
  576. break;
  577. case ChatType.ShortVoiceChatType:
  578. item = _soundMsg();
  579. break;
  580. case ChatType.RedWalletChatType:
  581. item = RedBagItem(
  582. Key(msg.time.toString()), UserData().basicInfo.userId, msg);
  583. break;
  584. case ChatType.PlaceChatType:
  585. item = PlaceItem(isMe: true, placeContent: msg.msgContent);
  586. break;
  587. case ChatType.FileChatType:
  588. item = _fileMsgItem();
  589. break;
  590. default:
  591. }
  592. return wrapItemWithMenu(item);
  593. }
  594. Widget _fileMsgItem() {
  595. return UploadImgItem(
  596. msg: widget.msg,
  597. cancelToken: _cancelToken,
  598. child: Container(
  599. height: 100,
  600. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  601. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  602. decoration: BoxDecoration(
  603. color: isLongPressed ? Colors.grey[300] : SendMsgBg,
  604. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  605. child: FileMsgItem(widget.msg)));
  606. }
  607. Widget wrapItemWithMenu(item) {
  608. List<Function> actionsFunc = [];
  609. List<String> actions = [
  610. I18n.of(context).reply,
  611. I18n.of(context).delete,
  612. ];
  613. actionsFunc.add(() {
  614. print('发送引用的消息');
  615. MessageMgr().emit('Reply Select Message', widget.msg);
  616. });
  617. actionsFunc.add(() {
  618. MessageMgr().emit('Delete Select Message', widget.msg);
  619. });
  620. ///转发
  621. if (widget.msg.msgType == ChatType.TextChatType.value ||
  622. widget.msg.msgType == ChatType.ImageChatType.value ||
  623. widget.msg.msgType == ChatType.ShortVideoChatType.value ||
  624. widget.msg.msgType == ChatType.PlaceChatType.value ||
  625. widget.msg.msgType == ChatType.EmoticonType.value ||
  626. widget.msg.msgType == ChatType.FileChatType.value) {
  627. actions.add(I18n.of(context).forward);
  628. actionsFunc.add(() {
  629. print('转发消息');
  630. if (widget.msg.msgType == ChatType.FileChatType.value &&
  631. widget.msg.localFile == null) {
  632. showToast('请先下载文件');
  633. return;
  634. }
  635. AppNavigator.pushForwardPage(context, widget.msg);
  636. });
  637. }
  638. if (widget.msg.msgType == ChatType.FileChatType.value &&
  639. widget.msg.localFile != null) {
  640. //分享文件
  641. actions.add(I18n.of(context).copy_download_url);
  642. actionsFunc.add(() async {
  643. //如果是文件增加复制下载地址
  644. String path = widget.msg.localFile;
  645. UploadUtil().copyFileUrl(widget.msg, context);
  646. String type = 'file';
  647. if (path.contains('mp4') || path.contains('mp3')) {
  648. type = 'video';
  649. } else if (path.contains('png') ||
  650. path.contains('jpg') ||
  651. path.contains('jpeg') ||
  652. path.contains('JPG') ||
  653. path.contains('PNG')) {
  654. type = 'image';
  655. }
  656. ShareExtend.share(FileCacheMgr.replacePath(path), type);
  657. });
  658. }
  659. if (widget.msg.msgType == ChatType.TextChatType.value) {
  660. actions.insert(0, I18n.of(context).copy);
  661. actionsFunc.insert(0, () {
  662. //复制当前的文字
  663. print('复制文字 ${textList[curTextType]}');
  664. ClipboardData clipboardData =
  665. ClipboardData(text: textList[curTextType]);
  666. Clipboard.setData(clipboardData);
  667. });
  668. }
  669. if (widget.msg.msgType == ChatType.ShortVoiceChatType.value) {
  670. var soundPlayMode =
  671. Provider.of<KeyboardIndexProvider>(context).soundPlayMode;
  672. actions.add(soundPlayMode
  673. ? I18n.of(context).handset_playback
  674. : I18n.of(context).speaker_play);
  675. actionsFunc.add(() {
  676. Provider.of<KeyboardIndexProvider>(context)
  677. .changeSoundPlayMode(!soundPlayMode);
  678. SoundUtils.instance.savePlayModeConfig(soundPlayMode);
  679. });
  680. }
  681. // String date2 = DateTime.fromMillisecondsSinceEpoch(widget.msg.time).toString();
  682. bool isUrl = false;
  683. if (widget.msg.msgType == ChatType.TextChatType.value) {
  684. if (textList[curTextType].contains('http')) {
  685. isUrl = true;
  686. }
  687. }
  688. return WPopupMenu(
  689. child: item,
  690. actions: actions,
  691. onTap: () async {
  692. MessageMgr().emit('Keyboard Hide');
  693. if (isUrl) {
  694. if (await canLaunch(textList[curTextType])) {
  695. launch(textList[curTextType]);
  696. }
  697. }
  698. },
  699. onLongPressStart: () {
  700. isLongPressed = true;
  701. setState(() {});
  702. },
  703. onLongPressEnd: () {
  704. isLongPressed = false;
  705. setState(() {});
  706. },
  707. onValueChanged: (int value) {
  708. print('选择的是$value个菜单');
  709. if (value >= 0 && value < actionsFunc.length) {
  710. actionsFunc[value]();
  711. }
  712. },
  713. );
  714. }
  715. Widget _getSentMessageLayout(BuildContext context) {
  716. bool hasHeadImg = true;
  717. if (UserData().basicInfo.headimgurl == null ||
  718. UserData().basicInfo.headimgurl.length == 0) {
  719. hasHeadImg = false;
  720. }
  721. return Row(
  722. crossAxisAlignment: CrossAxisAlignment.start,
  723. mainAxisAlignment: MainAxisAlignment.end,
  724. children: <Widget>[
  725. MsgStateWidget(widget.msg),
  726. SizedBox(width: 3),
  727. _msgLayout(context, widget.msg),
  728. SizedBox(width: 10),
  729. Column(
  730. crossAxisAlignment: CrossAxisAlignment.end,
  731. children: <Widget>[
  732. ClipRRect(
  733. borderRadius: BorderRadius.circular(8),
  734. child: hasHeadImg
  735. ? CachedNetworkImage(
  736. imageUrl: UserData().basicInfo.headimgurl,
  737. placeholder: (context, url) => Image.asset(
  738. Constants.DefaultHeadImgUrl,
  739. width: 40,
  740. height: 40,
  741. ),
  742. width: 40,
  743. height: 40,
  744. )
  745. : SizedBox(
  746. width: 40,
  747. height: 40,
  748. child: Image.asset(R.assetsImagesDefaultNorAvatar))),
  749. ],
  750. )
  751. ]);
  752. }
  753. _receiveJIF(MsgModel msg) {
  754. var text = utf8.decode(msg.msgContent);
  755. return Container(
  756. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  757. child: extendedText(text,
  758. selectionEnabled: false, hideKeyboard: widget.hideKeyboard));
  759. }
  760. double _getTextWidth(String text, {double fontSize: FontSize}) {
  761. var tp = TextPainter(
  762. text: TextSpan(style: TextStyle(fontSize: fontSize), text: text),
  763. textAlign: TextAlign.left,
  764. textDirection: TextDirection.ltr,
  765. textScaleFactor: 1,
  766. );
  767. tp.layout(maxWidth: Screen.width - 140);
  768. RegExp alterStr = RegExp(r'\[([0-9]+)\]');
  769. Iterable<Match> matches = alterStr.allMatches(text);
  770. // print('~~~~~~~~~~~~~~${matches.length}~~~~~~~~~~~~~~~');
  771. double delta = 0;
  772. for (Match m in matches) {
  773. // print('~~~~~~~~~~~~~~${m.group(1)}~~~~~~~~~~~~~~~');
  774. if (int.parse(m.group(1)) > 10) {
  775. delta += 20 + 4 - 25;
  776. } else {
  777. delta += 20 + 4 - 16.8;
  778. }
  779. }
  780. return tp.width + delta;
  781. }
  782. blueDot(bool isShow) {
  783. return Container(
  784. margin: EdgeInsets.only(right: 5),
  785. decoration: BoxDecoration(
  786. shape: BoxShape.circle,
  787. color: isShow ? Color(0xFFFF7E00) : Color(0xFFCFCFCF),
  788. ),
  789. width: 4,
  790. height: 4,
  791. );
  792. }
  793. _receiveText(MsgModel msg) {
  794. List<Widget> showMsg = [];
  795. double width = 0.0;
  796. if (textList.length > 0) {
  797. bool isUrl = false;
  798. if (textList[curTextType].contains('http')) {
  799. isUrl = true;
  800. }
  801. if (msg.refMsgContent != null && msg.refMsgContent.length > 0) {
  802. var quoteItem = getQuoteItem(msg);
  803. width = getQuoteContentWidth(
  804. quoteItem['quoteMsg'],
  805. quoteItem['refName'],
  806. quoteItem['showTimeStr'],
  807. quoteItem['quoteContent']);
  808. showMsg.add(getQuoteContent(quoteItem['quoteMsg'], quoteItem['refName'],
  809. quoteItem['showTimeStr'], quoteItem['quoteContent'], false));
  810. }
  811. var contentWidth = _getTextWidth(textList[curTextType]);
  812. width = min(max(contentWidth, width), maxWidth);
  813. showMsg.add(Container(
  814. constraints: BoxConstraints(maxWidth: maxWidth, minHeight: 22),
  815. alignment: Alignment.centerLeft,
  816. child: extendedText(
  817. textList[curTextType],
  818. color: isUrl ? Colors.blue : Constants.BlackTextColor,
  819. hideKeyboard: widget.hideKeyboard,
  820. fontSize: FontSize,
  821. )));
  822. }
  823. var minWidth = width + 20;
  824. if (msg.transTag != 0) {
  825. minWidth = 200;
  826. showMsg.add(Padding(
  827. padding: EdgeInsets.symmetric(vertical: 5),
  828. child: Divider(color: Color(0xFFECECEC), height: 1)));
  829. Widget tranWidget = _transProcessWidget(msg.transTag);
  830. showMsg.add(tranWidget);
  831. }
  832. Widget text = Stack(children: <Widget>[
  833. Container(
  834. width: width + 20,
  835. constraints:
  836. BoxConstraints(maxWidth: Screen.width - 120, minWidth: minWidth),
  837. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  838. child: Column(
  839. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  840. decoration: BoxDecoration(
  841. border: Border.all(color: ReciveBorderColor, width: 0.5),
  842. color: isLongPressed ? Colors.grey[300] : Colors.white,
  843. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  844. ),
  845. msg.transTag != 1 && msg.transTag != 0
  846. ? Positioned(
  847. right: 5,
  848. top: 5,
  849. child: Container(
  850. child: Row(children: <Widget>[
  851. blueDot(curTextType == 0),
  852. blueDot(curTextType == 1),
  853. blueDot(curTextType == 2),
  854. //blueDot(true),
  855. ])))
  856. : Container()
  857. ]);
  858. return InkWell(
  859. child: text,
  860. onTap: () {
  861. MessageMgr().emit('Keyboard Hide');
  862. if (msg.transTag == 1) {
  863. return;
  864. }
  865. if (msg.transTag == 2 || msg.transTag == 3) {
  866. setState(() {
  867. curTextType += 1;
  868. curTextType %= textList.length;
  869. });
  870. return;
  871. }
  872. },
  873. );
  874. }
  875. //用户评价人工翻译,差评
  876. rateTranslateResult() async {
  877. return await HttpUtil().rateTranslateResult(widget.msg, () {
  878. if (mounted) {
  879. setState(() {});
  880. }
  881. });
  882. }
  883. _translateItemWidget(int code, String title, Function onTap) {
  884. Color color = onTap == null ? Color(0xFFC5C5C5) : Color(0xFF6A6A6A);
  885. return InkWell(
  886. onTap: onTap,
  887. splashColor: Colors.red,
  888. child: Container(
  889. width: 80,
  890. child: Row(
  891. children: <Widget>[
  892. Icon(
  893. IconData(code, fontFamily: Constants.IconFontFamily),
  894. color: color,
  895. size: 20,
  896. ),
  897. SizedBox(width: 5),
  898. Expanded(
  899. child: SizedBox(
  900. child: Text(
  901. title,
  902. style: TextStyle(color: color, fontSize: 10),
  903. textScaleFactor: 1.0,
  904. maxLines: 1,
  905. overflow: TextOverflow.ellipsis,
  906. ),
  907. ))
  908. ],
  909. )));
  910. }
  911. bool isTranslating = false;
  912. _transProcessWidget(int transTag) {
  913. double width = 160;
  914. Widget userTranslateWidget;
  915. Widget machineTranslateWidget;
  916. if (transTag == 1) {
  917. //机器翻译中
  918. userTranslateWidget =
  919. _translateItemWidget(0xe670, I18n.of(context).man_retranslate, null);
  920. machineTranslateWidget =
  921. _translateItemWidget(0xe671, I18n.of(context).robotTranslate, null);
  922. } else if (transTag == 2) {
  923. //人工翻译中
  924. userTranslateWidget =
  925. _translateItemWidget(0xe670, I18n.of(context).ManTranslate, null);
  926. machineTranslateWidget = _translateItemWidget(
  927. 0xe671,
  928. I18n.of(context).robot_retranslate,
  929. textList.length <= 1
  930. ? null
  931. : () {
  932. setState(() {
  933. curTextType += 1;
  934. curTextType %= textList.length;
  935. });
  936. });
  937. } else if (transTag == 3) {
  938. //机器翻译完成
  939. userTranslateWidget = _translateItemWidget(
  940. 0xe670,
  941. I18n.of(context).man_retranslate,
  942. isTranslating
  943. ? null
  944. : () async {
  945. isTranslating = true;
  946. int money =
  947. Provider.of<MoneyChangeProvider>(context, listen: false)
  948. .money;
  949. int voucher =
  950. Provider.of<VoucherChangeProvider>(context, listen: false)
  951. .voucher;
  952. int needMoney = widget.msg.getNeedMoney();
  953. if (needMoney > voucher + money) {
  954. showToast('翻译券和H币不足');
  955. return;
  956. }
  957. var res = await HttpUtil().getPersonalTranslate(widget.msg);
  958. if (res) {
  959. print('请求人工翻译成功,进行扣费');
  960. setState(() {
  961. widget.msg.transTag = 2;
  962. //优先扣券
  963. if (voucher > 0) {
  964. int costQuan = min(voucher, needMoney);
  965. Provider.of<VoucherChangeProvider>(context,
  966. listen: false)
  967. .subVoucher(costQuan);
  968. }
  969. //不足的话再扣H币
  970. if (needMoney > voucher) {
  971. Provider.of<MoneyChangeProvider>(context, listen: false)
  972. .subMoney(needMoney - voucher);
  973. }
  974. });
  975. }
  976. });
  977. machineTranslateWidget = _translateItemWidget(
  978. 0xe671,
  979. I18n.of(context).robot_retranslate,
  980. textList.length <= 1
  981. ? null
  982. : () {
  983. setState(() {
  984. curTextType += 1;
  985. curTextType %= textList.length;
  986. });
  987. });
  988. } else if (transTag == 5 || transTag == 6) {
  989. //人工翻译失败
  990. userTranslateWidget = _translateItemWidget(0xe670, '人工翻译失败', null);
  991. machineTranslateWidget =
  992. _translateItemWidget(0xe671, I18n.of(context).robot_retranslate, () {
  993. setState(() {
  994. curTextType += 1;
  995. curTextType %= textList.length;
  996. });
  997. });
  998. } else if (transTag == 4 || transTag == 10) {
  999. //4人工翻译完成,未评论 10人工翻译完成已评论
  1000. userTranslateWidget = InkWell(
  1001. onTap: () {
  1002. setState(() {
  1003. curTextType = 0;
  1004. });
  1005. },
  1006. child: Container(
  1007. width: width / 2,
  1008. child: Row(
  1009. children: <Widget>[
  1010. InkWell(
  1011. child: Icon(
  1012. IconData(0xe641, fontFamily: Constants.IconFontFamily),
  1013. size: 18,
  1014. color:
  1015. transTag == 10 ? Colors.grey : Color(0xFF087FF3)),
  1016. onTap: transTag == 10
  1017. ? null
  1018. : () {
  1019. CustomUI.showIosDialog(
  1020. context, I18n.of(context).bad_ev, () async {
  1021. bool isSuccess = await rateTranslateResult();
  1022. if (isSuccess) {
  1023. Navigator.of(context).pop(true);
  1024. showToast(I18n.of(context).success);
  1025. } else {
  1026. showToast(I18n.of(context).fail);
  1027. Navigator.of(context).pop();
  1028. }
  1029. }, () {
  1030. Navigator.of(context).pop();
  1031. });
  1032. },
  1033. ),
  1034. SizedBox(width: 5),
  1035. Expanded(
  1036. child: SizedBox(
  1037. child: Text(
  1038. I18n.of(context).over,
  1039. style: TextStyle(color: Color(0xFF087FF3), fontSize: 10),
  1040. textScaleFactor: 1.0,
  1041. maxLines: 1,
  1042. overflow: TextOverflow.ellipsis,
  1043. ),
  1044. ))
  1045. ],
  1046. )));
  1047. machineTranslateWidget =
  1048. _translateItemWidget(0xe675, I18n.of(context).see_original, () {
  1049. setState(() {
  1050. curTextType = textList.length - 1;
  1051. });
  1052. });
  1053. }
  1054. return Container(
  1055. height: 26,
  1056. alignment: Alignment.center,
  1057. child: Row(
  1058. children: <Widget>[
  1059. userTranslateWidget,
  1060. Expanded(
  1061. child: SizedBox(child: VerticalDivider(), height: 20, width: 2)),
  1062. machineTranslateWidget
  1063. ],
  1064. ),
  1065. );
  1066. }
  1067. _receiveImg(BuildContext context, List<int> imgData, {String downloadData}) {
  1068. // ImageProvider provider = MemoryImage(Uint8List.fromList(imgData));
  1069. ImageProvider provider = MemoryImage(widget.msg.localFile == null
  1070. ? Uint8List.fromList(imgData)
  1071. : File(widget.msg.localFile).readAsBytesSync());
  1072. var imgSize = _getImgSize();
  1073. return DownloadItem(
  1074. isAutoDown: false,
  1075. msg: widget.msg,
  1076. onFinishTap: () {
  1077. widget.hideKeyboard();
  1078. showFullImg(context, widget.msg);
  1079. },
  1080. child: Container(
  1081. alignment: Alignment.centerLeft,
  1082. width: imgSize.width,
  1083. height: imgSize.height,
  1084. child: ClipRRect(
  1085. child: Image(
  1086. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  1087. ),
  1088. borderRadius: BorderRadius.circular(5),
  1089. ),
  1090. ),
  1091. );
  1092. }
  1093. _receiveVideo(BuildContext context, List<int> imgData,
  1094. {String downloadData}) {
  1095. ImageProvider provider = MemoryImage(Uint8List.fromList(imgData));
  1096. var imgSize = _getImgSize();
  1097. return InkWell(
  1098. onTap: () {
  1099. if (widget.msg.localFile != null) {
  1100. showVideoPage(context, widget.msg.localFile);
  1101. }
  1102. },
  1103. child: DownloadItem(
  1104. isAutoDown: false,
  1105. msg: widget.msg,
  1106. child: Container(
  1107. width: imgSize.width,
  1108. height: imgSize.height,
  1109. child: ClipRRect(
  1110. child: Image(
  1111. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  1112. ),
  1113. borderRadius: BorderRadius.circular(5),
  1114. ),
  1115. ),
  1116. ));
  1117. }
  1118. showVideoPage(BuildContext context, String filePath) {
  1119. widget.hideKeyboard();
  1120. Navigator.push(context,
  1121. MaterialPageRoute<void>(builder: (BuildContext context) {
  1122. return VideoPage(videoPath: filePath);
  1123. }));
  1124. }
  1125. _receiveSound(BuildContext context, List<int> soundData) {
  1126. print(' build 收到的语音消息');
  1127. MsgModel msg = widget.msg;
  1128. var time = widget.msg.extraInfo / 1000;
  1129. if (time > 60) {
  1130. time = 60.0;
  1131. }
  1132. bool isPlaying = false;
  1133. if (curSoundUrl != null) {
  1134. isPlaying = SoundUtils().isPlaying(curSoundUrl);
  1135. }
  1136. var soundWidget = InkWell(
  1137. onTap: () async {
  1138. bool isLocal = true;
  1139. if (msg.localFile != null) {
  1140. isLocal = true;
  1141. curSoundUrl = msg.localFile;
  1142. } else {
  1143. isLocal = false;
  1144. var sessionId = msg.sessionId;
  1145. curSoundUrl = UploadUtil()
  1146. .getFullUrl(msg.extraFile, sessionId, msg.channelType);
  1147. }
  1148. print('当前文件$curSoundUrl 本地?$isLocal');
  1149. if (isPlaying) {
  1150. await SoundUtils().pause();
  1151. } else {
  1152. print('开始播放');
  1153. if (widget.msg.soundListened == 0) {
  1154. widget.msg.updateSoundListened();
  1155. }
  1156. await SoundUtils().play(curSoundUrl, isLocal: isLocal, onPlayed: () {
  1157. if (mounted) {
  1158. this.setState(() {});
  1159. }
  1160. }, complete: () {
  1161. if (mounted) {
  1162. this.setState(() {});
  1163. }
  1164. });
  1165. }
  1166. },
  1167. child: Container(
  1168. width: 130,
  1169. child: Row(children: <Widget>[
  1170. Container(
  1171. alignment: Alignment.center,
  1172. padding: EdgeInsets.only(bottom: 2),
  1173. margin: EdgeInsets.only(right: 10),
  1174. width: 25.5,
  1175. height: 25.5,
  1176. decoration: BoxDecoration(
  1177. border:
  1178. Border.all(color: const Color(0xFF1B92C7), width: 0.5),
  1179. color: const Color(0xFF04A4FE),
  1180. shape: BoxShape.circle),
  1181. child: Icon(
  1182. IconData(isPlaying ? 0xe652 : 0xe653,
  1183. fontFamily: Constants.IconFontFamily),
  1184. size: isPlaying ? 15 : 18,
  1185. color: Colors.white,
  1186. ),
  1187. ),
  1188. isPlaying
  1189. ? Stack(
  1190. children: <Widget>[
  1191. Container(
  1192. height: 18,
  1193. width: 19,
  1194. ),
  1195. Positioned(
  1196. bottom: 0,
  1197. child: VideoAnim(
  1198. begin: 18,
  1199. start: 0.444,
  1200. end: 4.5,
  1201. )),
  1202. Positioned(
  1203. left: 7,
  1204. bottom: 0,
  1205. child: VideoAnim(
  1206. begin: 4.5,
  1207. end: 18,
  1208. )),
  1209. Positioned(
  1210. left: 14,
  1211. bottom: 0,
  1212. child: VideoAnim(
  1213. begin: 18,
  1214. end: 4.5,
  1215. ))
  1216. ],
  1217. )
  1218. : Stack(
  1219. children: <Widget>[
  1220. Container(
  1221. height: 18,
  1222. width: 19,
  1223. ),
  1224. Positioned(
  1225. bottom: 0, child: CustomUI.buildAudioContaniner(12)),
  1226. Positioned(
  1227. left: 7,
  1228. bottom: 0,
  1229. child: CustomUI.buildAudioContaniner(4.5)),
  1230. Positioned(
  1231. left: 14,
  1232. bottom: 0,
  1233. child: CustomUI.buildAudioContaniner(18))
  1234. ],
  1235. ),
  1236. Expanded(child: SizedBox()),
  1237. fixedText(time.toStringAsFixed(0),
  1238. color: Constants.BlackTextColor, fontSize: 16)
  1239. ])),
  1240. );
  1241. List<Widget> showMsg = [];
  1242. showMsg.add(DownloadItem(
  1243. msg: widget.msg,
  1244. child: soundWidget,
  1245. isShowProgress: false,
  1246. ));
  1247. double width = 130;
  1248. double minWidth = 0;
  1249. if (textList.length > 0) {
  1250. width = _getTextWidth(textList[curTextType]) + 20;
  1251. width = min(width, Screen.width - 120);
  1252. minWidth = max(width, 150);
  1253. showMsg.add(Padding(
  1254. padding: EdgeInsets.symmetric(vertical: 5),
  1255. child: Divider(
  1256. height: 1,
  1257. )));
  1258. showMsg.add(Container(
  1259. child: extendedText(
  1260. textList[curTextType],
  1261. color: Constants.BlackTextColor,
  1262. hideKeyboard: widget.hideKeyboard,
  1263. fontSize: FontSize,
  1264. ),
  1265. alignment: Alignment.centerLeft,
  1266. constraints:
  1267. BoxConstraints(maxWidth: Screen.width - 120, minHeight: 22),
  1268. ));
  1269. }
  1270. if (msg.transTag != 0) {
  1271. minWidth = 200;
  1272. showMsg.add(Divider(color: Color(0xFFECECEC), height: 3));
  1273. Widget tranWidget = _transProcessWidget(msg.transTag);
  1274. showMsg.add(tranWidget);
  1275. }
  1276. return Container(
  1277. width: width + 20,
  1278. constraints:
  1279. msg.transTag != 0 ? BoxConstraints(minWidth: minWidth) : null,
  1280. child: Column(
  1281. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  1282. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  1283. decoration: BoxDecoration(
  1284. color: Colors.white,
  1285. border: Border.all(color: ReciveBorderColor, width: 0.5),
  1286. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  1287. );
  1288. }
  1289. void showFullImg(BuildContext context, MsgModel msg) {
  1290. print('显示图片');
  1291. Navigator.push(context,
  1292. MaterialPageRoute<void>(builder: (BuildContext context) {
  1293. return PhotoPage(msg: msg);
  1294. }));
  1295. }
  1296. _reveiveMsg(BuildContext context) {
  1297. Widget item;
  1298. switch (ChatType.valueOf(widget.msg.msgType)) {
  1299. case ChatType.TextChatType:
  1300. item = _receiveText(widget.msg);
  1301. break;
  1302. case ChatType.EmoticonType:
  1303. item = _receiveJIF(widget.msg);
  1304. break;
  1305. case ChatType.ImageChatType:
  1306. if (widget.msg.extraFile != null) {
  1307. item = _receiveImg(context, widget.msg.msgContent,
  1308. downloadData: widget.msg.extraFile);
  1309. } else {
  1310. item = _receiveImg(context, widget.msg.msgContent);
  1311. }
  1312. break;
  1313. case ChatType.ShortVideoChatType:
  1314. item = _receiveVideo(context, widget.msg.msgContent,
  1315. downloadData: widget.msg.extraFile);
  1316. break;
  1317. case ChatType.ShortVoiceChatType:
  1318. item = _receiveSound(context, widget.msg.msgContent);
  1319. break;
  1320. case ChatType.PlaceChatType:
  1321. item = PlaceItem(isMe: false, placeContent: widget.msg.msgContent);
  1322. break;
  1323. case ChatType.FileChatType:
  1324. item = _receiveFileMsgItem();
  1325. break;
  1326. default:
  1327. }
  1328. return wrapItemWithMenu(item);
  1329. }
  1330. Widget _receiveFileMsgItem() {
  1331. return DownloadItem(
  1332. isAutoDown: false,
  1333. msg: widget.msg,
  1334. onComplete: () {
  1335. if (mounted) {
  1336. setState(() {});
  1337. }
  1338. },
  1339. child: Container(
  1340. height: 100,
  1341. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  1342. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  1343. decoration: BoxDecoration(
  1344. color: Colors.white,
  1345. border: Border.all(color: ReciveBorderColor, width: 0.5),
  1346. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  1347. child: FileMsgItem(widget.msg)));
  1348. }
  1349. Widget _getReceivedMessageLayout(BuildContext context) {
  1350. bool hasHeadImg = true;
  1351. var memberModel = widget.memberModel;
  1352. if (memberModel == null) {
  1353. return Container();
  1354. }
  1355. if (memberModel.avtar == null || memberModel.avtar.length == 0) {
  1356. hasHeadImg = false;
  1357. }
  1358. GroupInfoModel infoModel = Provider.of<GroupInfoModel>(context);
  1359. var refName = Provider.of<RefNameProvider>(context)
  1360. .getGroupRefName(infoModel.sessionId, memberModel.memberId);
  1361. bool isShowSoundSate =
  1362. widget.msg.msgType == ChatType.ShortVoiceChatType.value &&
  1363. widget.msg.soundListened == 0;
  1364. return Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
  1365. Container(
  1366. margin: const EdgeInsets.only(right: 8.0),
  1367. child: GestureDetector(
  1368. child: ClipRRect(
  1369. borderRadius: BorderRadius.circular(8),
  1370. child: hasHeadImg
  1371. ? CachedNetworkImage(
  1372. imageUrl: memberModel.avtar,
  1373. placeholder: (context, url) => Image.asset(
  1374. Constants.DefaultHeadImgUrl,
  1375. width: 40,
  1376. height: 40,
  1377. ),
  1378. width: 40,
  1379. height: 40,
  1380. )
  1381. : SizedBox(
  1382. width: 40,
  1383. height: 40,
  1384. child: Image.asset(R.assetsImagesDefaultNorAvatar))),
  1385. onTap: () {
  1386. AppNavigator.pushProfileInfoPage(context, memberModel.memberId,
  1387. fromWhere: 2,
  1388. addMode: !FriendListMgr().isMyFriend(memberModel.memberId)
  1389. ? 1
  1390. : 0);
  1391. },
  1392. onLongPress: () {
  1393. print('long press user');
  1394. MessageMgr().emit('Alter User Message', memberModel);
  1395. },
  1396. )),
  1397. infoModel.isShowName > 0
  1398. ? Column(
  1399. crossAxisAlignment: CrossAxisAlignment.start,
  1400. children: <Widget>[
  1401. Container(
  1402. padding: EdgeInsets.only(left: 9),
  1403. child: fixedText(refName, fontSize: 12, color: Colors.grey),
  1404. ),
  1405. SizedBox(height: 2),
  1406. _reveiveMsg(context),
  1407. ],
  1408. )
  1409. : _reveiveMsg(context),
  1410. isShowSoundSate
  1411. ? Container(
  1412. margin: EdgeInsets.only(
  1413. left: 8, top: infoModel.isShowName > 0 ? 38 : 20),
  1414. child: CircleAvatar(
  1415. radius: 3.5,
  1416. backgroundColor: Colors.red,
  1417. ))
  1418. : Container()
  1419. ]);
  1420. }
  1421. }