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

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