Hibok
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1622 lines
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. Widget userTranslateWidget;
  918. Widget machineTranslateWidget;
  919. if (transTag == 1) {
  920. //机器翻译中
  921. userTranslateWidget =
  922. _translateItemWidget(0xe670, I18n.of(context).man_retranslate, null);
  923. machineTranslateWidget =
  924. _translateItemWidget(0xe671, I18n.of(context).robotTranslate, null);
  925. } else if (transTag == 2) {
  926. //人工翻译中
  927. userTranslateWidget =
  928. _translateItemWidget(0xe670, I18n.of(context).ManTranslate, null);
  929. machineTranslateWidget =
  930. _translateItemWidget(0xe671, I18n.of(context).robot_retranslate, () {
  931. setState(() {
  932. curTextType += 1;
  933. curTextType %= textList.length;
  934. });
  935. });
  936. } else if (transTag == 3) {
  937. //机器翻译完成
  938. userTranslateWidget = _translateItemWidget(
  939. 0xe670,
  940. I18n.of(context).man_retranslate,
  941. isTranslating
  942. ? null
  943. : () async {
  944. isTranslating = true;
  945. int money =
  946. Provider.of<MoneyChangeProvider>(context, listen: false)
  947. .money;
  948. int voucher =
  949. Provider.of<VoucherChangeProvider>(context, listen: false)
  950. .voucher;
  951. int needMoney = widget.msg.getNeedMoney();
  952. if (needMoney > voucher + money) {
  953. showToast('翻译券和H币不足');
  954. return;
  955. }
  956. var res = await HttpUtil().getPersonalTranslate(widget.msg);
  957. if (res) {
  958. print('请求人工翻译成功,进行扣费');
  959. setState(() {
  960. widget.msg.transTag = 2;
  961. SqlUtil().updateUserTranslateState(widget.msg.sessionId,
  962. widget.msg.time, widget.msg.transTag);
  963. //优先扣券
  964. if (voucher > 0) {
  965. int costQuan = min(voucher, needMoney);
  966. Provider.of<VoucherChangeProvider>(context,
  967. listen: false)
  968. .subVoucher(costQuan);
  969. }
  970. //不足的话再扣H币
  971. if (needMoney > voucher) {
  972. Provider.of<MoneyChangeProvider>(context, listen: false)
  973. .subMoney(needMoney - voucher);
  974. }
  975. });
  976. }
  977. });
  978. machineTranslateWidget =
  979. _translateItemWidget(0xe671, I18n.of(context).robot_retranslate, () {
  980. setState(() {
  981. curTextType += 1;
  982. curTextType %= textList.length;
  983. });
  984. });
  985. } else if (transTag == 5 || transTag == 6) {
  986. //人工翻译失败
  987. userTranslateWidget = _translateItemWidget(0xe670, '人工翻译失败', null);
  988. machineTranslateWidget =
  989. _translateItemWidget(0xe671, I18n.of(context).robot_retranslate, () {
  990. setState(() {
  991. curTextType += 1;
  992. curTextType %= textList.length;
  993. });
  994. });
  995. } else if (transTag == 4 || transTag == 10) {
  996. //4人工翻译完成,未评论 10人工翻译完成已评论
  997. userTranslateWidget = InkWell(
  998. onTap: () {
  999. setState(() {
  1000. curTextType = 0;
  1001. });
  1002. },
  1003. child: Container(
  1004. width: width / 2,
  1005. child: Row(
  1006. children: <Widget>[
  1007. InkWell(
  1008. child: Icon(
  1009. IconData(0xe641, fontFamily: Constants.IconFontFamily),
  1010. size: 18,
  1011. color:
  1012. transTag == 10 ? Colors.grey : Color(0xFF087FF3)),
  1013. onTap: transTag == 10
  1014. ? null
  1015. : () {
  1016. CustomUI.showIosDialog(
  1017. context, I18n.of(context).bad_ev, () async {
  1018. bool isSuccess = await rateTranslateResult();
  1019. if (isSuccess) {
  1020. Navigator.of(context).pop(true);
  1021. showToast(I18n.of(context).success);
  1022. } else {
  1023. showToast(I18n.of(context).fail);
  1024. Navigator.of(context).pop();
  1025. }
  1026. }, () {
  1027. Navigator.of(context).pop();
  1028. });
  1029. },
  1030. ),
  1031. SizedBox(width: 5),
  1032. Expanded(
  1033. child: SizedBox(
  1034. child: Text(
  1035. I18n.of(context).over,
  1036. style: TextStyle(color: Color(0xFF087FF3), fontSize: 10),
  1037. textScaleFactor: 1.0,
  1038. maxLines: 1,
  1039. overflow: TextOverflow.ellipsis,
  1040. ),
  1041. ))
  1042. ],
  1043. )));
  1044. machineTranslateWidget =
  1045. _translateItemWidget(0xe675, I18n.of(context).see_original, () {
  1046. setState(() {
  1047. curTextType = textList.length - 1;
  1048. });
  1049. });
  1050. }
  1051. return Container(
  1052. height: 26,
  1053. alignment: Alignment.center,
  1054. child: Row(
  1055. children: <Widget>[
  1056. userTranslateWidget,
  1057. Expanded(
  1058. child: SizedBox(child: VerticalDivider(), height: 20, width: 2)),
  1059. machineTranslateWidget
  1060. ],
  1061. ),
  1062. );
  1063. }
  1064. _receiveImg(BuildContext context, List<int> imgData, {String downloadData}) {
  1065. ImageProvider provider = MemoryImage(widget.msg.localFile == null
  1066. ? Uint8List.fromList(imgData)
  1067. : File(widget.msg.localFile).readAsBytesSync());
  1068. var imgSize = _getImgSize();
  1069. return DownloadItem(
  1070. isAutoDown: false,
  1071. msg: widget.msg,
  1072. onFinishTap: () {
  1073. widget.hideKeyboard();
  1074. showFullImg(context, widget.msg);
  1075. },
  1076. child: Container(
  1077. width: imgSize.width,
  1078. height: imgSize.height,
  1079. child: ClipRRect(
  1080. child: Image(
  1081. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  1082. ),
  1083. borderRadius: BorderRadius.circular(5),
  1084. ),
  1085. ),
  1086. );
  1087. }
  1088. _receiveVideo(BuildContext context, List<int> imgData,
  1089. {String downloadData}) {
  1090. ImageProvider provider = MemoryImage(Uint8List.fromList(imgData));
  1091. var imgSize = _getImgSize();
  1092. return InkWell(
  1093. onTap: () {
  1094. if (widget.msg.localFile != null) {
  1095. showVideoPage(context, widget.msg.localFile);
  1096. }
  1097. },
  1098. child: DownloadItem(
  1099. isAutoDown: false,
  1100. msg: widget.msg,
  1101. child: Container(
  1102. width: imgSize.width,
  1103. height: imgSize.height,
  1104. child: ClipRRect(
  1105. child: Image(
  1106. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  1107. ),
  1108. borderRadius: BorderRadius.circular(5),
  1109. ),
  1110. ),
  1111. ));
  1112. }
  1113. showVideoPage(BuildContext context, String filePath) {
  1114. widget.hideKeyboard();
  1115. Navigator.push(context,
  1116. MaterialPageRoute<void>(builder: (BuildContext context) {
  1117. return VideoPage(videoPath: filePath);
  1118. }));
  1119. }
  1120. Widget _receiveFileMsgItem() {
  1121. return DownloadItem(
  1122. isAutoDown: false,
  1123. msg: widget.msg,
  1124. onComplete: () {
  1125. if (mounted) {
  1126. setState(() {});
  1127. }
  1128. },
  1129. child: Container(
  1130. height: 100,
  1131. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  1132. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  1133. decoration: BoxDecoration(
  1134. color: Colors.white,
  1135. border: Border.all(color: ReciveBorderColor, width: 0.5),
  1136. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  1137. child: FileMsgItem(widget.msg)));
  1138. }
  1139. _receiveSound(BuildContext context, List<int> soundData) {
  1140. print('收到语音消息');
  1141. MsgModel msg = widget.msg;
  1142. var time = widget.msg.extraInfo / 1000;
  1143. if (time > 60) {
  1144. time = 60.0;
  1145. }
  1146. bool isPlaying = false;
  1147. if (curSoundUrl != null) {
  1148. isPlaying = SoundUtils().isPlaying(curSoundUrl);
  1149. }
  1150. var soundWidget = InkWell(
  1151. onTap: () async {
  1152. bool isLocal = true;
  1153. if (msg.localFile != null) {
  1154. isLocal = true;
  1155. curSoundUrl = msg.localFile;
  1156. } else {
  1157. isLocal = false;
  1158. var sessionId = msg.sessionId;
  1159. curSoundUrl = UploadUtil()
  1160. .getFullUrl(msg.extraFile, sessionId, msg.channelType);
  1161. }
  1162. print('当前文件$curSoundUrl 本地?$isLocal');
  1163. if (isPlaying) {
  1164. await SoundUtils().pause();
  1165. } else {
  1166. print('开始播放');
  1167. if (widget.msg.soundListened == 0) {
  1168. widget.msg.updateSoundListened();
  1169. } //设置为已经播放
  1170. await SoundUtils().play(curSoundUrl, isLocal: isLocal, onPlayed: () {
  1171. if (mounted) {
  1172. this.setState(() {});
  1173. }
  1174. }, complete: () {
  1175. if (mounted) {
  1176. this.setState(() {});
  1177. }
  1178. });
  1179. }
  1180. },
  1181. child: Container(
  1182. width: 130,
  1183. child: Row(children: <Widget>[
  1184. Container(
  1185. alignment: Alignment.center,
  1186. padding: EdgeInsets.only(bottom: 2),
  1187. margin: EdgeInsets.only(right: 10),
  1188. width: 25.5,
  1189. height: 25.5,
  1190. decoration: BoxDecoration(
  1191. border:
  1192. Border.all(color: const Color(0xFF1B92C7), width: 0.5),
  1193. color: const Color(0xFF04A4FE),
  1194. shape: BoxShape.circle),
  1195. child: Icon(
  1196. IconData(isPlaying ? 0xe652 : 0xe653,
  1197. fontFamily: Constants.IconFontFamily),
  1198. size: isPlaying ? 15 : 18,
  1199. color: Colors.white,
  1200. ),
  1201. ),
  1202. isPlaying
  1203. ? Stack(
  1204. children: <Widget>[
  1205. Container(
  1206. height: 18,
  1207. width: 19,
  1208. ),
  1209. Positioned(
  1210. bottom: 0,
  1211. child: VideoAnim(
  1212. begin: 18,
  1213. start: 0.444,
  1214. end: 4.5,
  1215. )),
  1216. Positioned(
  1217. left: 7,
  1218. bottom: 0,
  1219. child: VideoAnim(
  1220. begin: 4.5,
  1221. end: 18,
  1222. )),
  1223. Positioned(
  1224. left: 14,
  1225. bottom: 0,
  1226. child: VideoAnim(
  1227. begin: 18,
  1228. end: 4.5,
  1229. ))
  1230. ],
  1231. )
  1232. : Stack(
  1233. children: <Widget>[
  1234. Container(
  1235. height: 18,
  1236. width: 19,
  1237. ),
  1238. Positioned(
  1239. bottom: 0, child: CustomUI.buildAudioContaniner(12)),
  1240. Positioned(
  1241. left: 7,
  1242. bottom: 0,
  1243. child: CustomUI.buildAudioContaniner(4.5)),
  1244. Positioned(
  1245. left: 14,
  1246. bottom: 0,
  1247. child: CustomUI.buildAudioContaniner(18))
  1248. ],
  1249. ),
  1250. Expanded(child: SizedBox()),
  1251. fixedText(time.toStringAsFixed(0),
  1252. color: Constants.BlackTextColor, fontSize: 16)
  1253. ])),
  1254. );
  1255. List<Widget> showMsg = [];
  1256. showMsg.add(DownloadItem(
  1257. msg: widget.msg,
  1258. child: soundWidget,
  1259. isShowProgress: false,
  1260. ));
  1261. double width = 130;
  1262. double minWidth = 0;
  1263. if (textList.length > 0 && textList[curTextType].length > 0) {
  1264. print('textList哒哒哒哒哒 ${textList[curTextType]}');
  1265. width = _getTextWidth(textList[curTextType]) + 20;
  1266. width = min(width, Screen.width - 120);
  1267. if (width < 130) {
  1268. width = 130;
  1269. }
  1270. showMsg.add(Padding(
  1271. padding: EdgeInsets.symmetric(vertical: 5),
  1272. child: Divider(
  1273. height: 1,
  1274. )));
  1275. showMsg.add(Container(
  1276. child: extendedText(
  1277. textList[curTextType],
  1278. color: Constants.BlackTextColor,
  1279. hideKeyboard: widget.hideKeyboard,
  1280. fontSize: FontSize,
  1281. ),
  1282. alignment: Alignment.centerLeft,
  1283. constraints:
  1284. BoxConstraints(maxWidth: Screen.width - 120, minHeight: 22),
  1285. ));
  1286. }
  1287. if (msg.transTag != 0) {
  1288. minWidth = 200;
  1289. showMsg.add(Divider(color: Color(0xFFECECEC), height: 3));
  1290. Widget tranWidget = _transProcessWidget(msg.transTag);
  1291. showMsg.add(tranWidget);
  1292. }
  1293. return Container(
  1294. width: width + 20,
  1295. constraints:
  1296. BoxConstraints(maxWidth: Screen.width - 120, minWidth: minWidth),
  1297. child: Column(
  1298. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  1299. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  1300. decoration: BoxDecoration(
  1301. color: Colors.white,
  1302. border: Border.all(color: ReciveBorderColor, width: 0.5),
  1303. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  1304. );
  1305. }
  1306. void showFullImg(BuildContext context, MsgModel msg) {
  1307. print('显示图片');
  1308. Navigator.push(context,
  1309. MaterialPageRoute<void>(builder: (BuildContext context) {
  1310. return PhotoPage(msg: msg);
  1311. }));
  1312. }
  1313. blueDot(bool isShow) {
  1314. return Container(
  1315. margin: EdgeInsets.only(right: 5),
  1316. decoration: BoxDecoration(
  1317. shape: BoxShape.circle,
  1318. color: isShow ? Color(0xFFFF7E00) : Color(0xFFCFCFCF),
  1319. ),
  1320. width: 4,
  1321. height: 4,
  1322. );
  1323. }
  1324. _reveiveMsg(BuildContext context) {
  1325. Widget item;
  1326. switch (ChatType.valueOf(widget.msg.msgType)) {
  1327. case ChatType.TextChatType:
  1328. item = _receiveText(widget.msg);
  1329. break;
  1330. case ChatType.EmoticonType:
  1331. item = _receiveJIF(widget.msg);
  1332. break;
  1333. case ChatType.ImageChatType:
  1334. if (widget.msg.extraFile != null) {
  1335. item = _receiveImg(context, widget.msg.msgContent,
  1336. downloadData: widget.msg.extraFile);
  1337. } else {
  1338. item = _receiveImg(context, widget.msg.msgContent);
  1339. }
  1340. break;
  1341. case ChatType.ShortVideoChatType:
  1342. item = _receiveVideo(context, widget.msg.msgContent,
  1343. downloadData: widget.msg.extraFile);
  1344. break;
  1345. case ChatType.ShortVoiceChatType:
  1346. item = _receiveSound(context, widget.msg.msgContent);
  1347. break;
  1348. case ChatType.RedWalletChatType:
  1349. item = RedBagItem(
  1350. Key(widget.msg.time.toString()), friendInfo.userId, widget.msg);
  1351. break;
  1352. case ChatType.PlaceChatType:
  1353. item = PlaceItem(isMe: false, placeContent: widget.msg.msgContent);
  1354. break;
  1355. case ChatType.GiftChatType:
  1356. item = GiftMsgItem(widget.msg.msgContent, false);
  1357. break;
  1358. case ChatType.FileChatType:
  1359. item = _receiveFileMsgItem();
  1360. break;
  1361. default:
  1362. }
  1363. return wrapItemWithMenu(item);
  1364. }
  1365. Widget _getReceivedMessageLayout(BuildContext context) {
  1366. bool hasHeadImg = true;
  1367. if (friendInfo.headimgurl == null || friendInfo.headimgurl.length == 0) {
  1368. hasHeadImg = false;
  1369. }
  1370. bool isShowSoundSate =
  1371. widget.msg.msgType == ChatType.ShortVoiceChatType.value &&
  1372. widget.msg.soundListened == 0;
  1373. return Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
  1374. Container(
  1375. margin: const EdgeInsets.only(right: 8.0),
  1376. child: InkWell(
  1377. child: ClipRRect(
  1378. borderRadius: BorderRadius.circular(8),
  1379. child: hasHeadImg
  1380. ? CachedNetworkImage(
  1381. imageUrl: friendInfo.headimgurl,
  1382. placeholder: (context, url) => Image.asset(
  1383. Constants.DefaultHeadImgUrl,
  1384. width: 40,
  1385. height: 40,
  1386. ),
  1387. width: 40,
  1388. height: 40,
  1389. )
  1390. : SizedBox(
  1391. width: 40,
  1392. height: 40,
  1393. child: Image.asset(R.assetsImagesDefaultNorAvatar))),
  1394. onTap: () {
  1395. AppNavigator.pushProfileInfoPage(context, friendInfo.userId,
  1396. fromWhere: 0);
  1397. },
  1398. )),
  1399. _reveiveMsg(context),
  1400. isShowSoundSate
  1401. ? Container(
  1402. margin: EdgeInsets.only(left: 8),
  1403. width: 40,
  1404. height: 40,
  1405. alignment: Alignment.centerLeft,
  1406. child: CircleAvatar(
  1407. radius: 3.5,
  1408. backgroundColor: Colors.red,
  1409. ))
  1410. : Container()
  1411. ]);
  1412. }
  1413. Widget _buildServiceCard(bool isStart, Function callBack) {
  1414. String endStr = UserData().isTranslateUser
  1415. ? I18n.of(context).translation_butler_evaluation_tips2
  1416. : I18n.of(context).translation_butler_evaluation_tips;
  1417. return Container(
  1418. alignment: Alignment.center,
  1419. margin: EdgeInsets.all(10),
  1420. child: Card(
  1421. elevation: 2,
  1422. // 阴影
  1423. shape: RoundedRectangleBorder(
  1424. borderRadius: BorderRadius.circular(10),
  1425. // side: BorderSide(color: Colors.green,width: 25),
  1426. ),
  1427. child: Padding(
  1428. padding: EdgeInsets.only(left: 10, right: 10, top: 15, bottom: 15),
  1429. child: Row(
  1430. children: <Widget>[
  1431. Expanded(
  1432. child: Container(
  1433. child: Column(
  1434. crossAxisAlignment: CrossAxisAlignment.start,
  1435. children: <Widget>[
  1436. Text(
  1437. isStart
  1438. ? I18n.of(context).translation_butler_start_service
  1439. : I18n.of(context).translation_butler_service_end,
  1440. textScaleFactor: 1.0,
  1441. style: TextStyle(
  1442. color: AppColors.NewAppbarTextColor, fontSize: 15),
  1443. ),
  1444. Text(
  1445. isStart
  1446. ? I18n.of(context).translation_butler_start_tips
  1447. : endStr,
  1448. textScaleFactor: 1.0,
  1449. style: TextStyle(color: Color(0xFF797979), fontSize: 13),
  1450. ),
  1451. ],
  1452. ),
  1453. )),
  1454. (isStart || UserData().isTranslateUser)
  1455. ? Container()
  1456. : Expanded(
  1457. child: Container(
  1458. margin: EdgeInsets.only(left: 15),
  1459. height: 30,
  1460. child: RaisedButton(
  1461. color: Color(0xff3875E9),
  1462. shape: RoundedRectangleBorder(
  1463. borderRadius:
  1464. BorderRadius.all(Radius.circular(10))),
  1465. child: Text(
  1466. I18n.of(context).translation_butler_evaluation,
  1467. textScaleFactor: 1.0,
  1468. style: TextStyle(color: Colors.white, fontSize: 15),
  1469. ),
  1470. onPressed: () {
  1471. CustomUI.buildTranslationEvaluationDialog(context);
  1472. }),
  1473. )),
  1474. ],
  1475. ),
  1476. ),
  1477. ),
  1478. );
  1479. }
  1480. }