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

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