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

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