Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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