Hibok
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

1620 Zeilen
53 KiB

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