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

1631 righe
53 KiB

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