Hibok
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

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