Hibok
您不能選擇超過 %s 個話題 話題必須以字母或數字為開頭,可包含連接號 ('-') 且最長為 35 個字
 
 
 
 
 
 

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