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

1613 righe
52 KiB

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