Hibok
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

1600 行
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 &&
  223. widget.msg.channelType == ChatChannelType.Session.value) {
  224. TransHKChatNotice res =
  225. TransHKChatNotice.fromBuffer(widget.msg.msgContent);
  226. if (res.noticeType == TransHKChatNoticeType.StartChat) {
  227. ///开始翻译服务
  228. return _buildServiceCard(true, () {});
  229. } else if (res.noticeType == TransHKChatNoticeType.EndChat) {
  230. ///翻译服务结束
  231. return _buildServiceCard(false, () {});
  232. } else {
  233. return Container();
  234. }
  235. // return Container();
  236. }
  237. // else{
  238. // if (type == ChatType.GroupChatNoticeType.value) {
  239. // var res = GroupChatNotice.fromBuffer(widget.msg.msgContent);
  240. //
  241. // var groupInfoModel = Provider.of<GroupInfoModel>(context);
  242. // var showStr = MsgHandler.getGroupNoticeMsg(res, groupInfoModel);
  243. // return Container(
  244. // alignment: Alignment.center,
  245. // constraints: BoxConstraints(maxWidth: Screen.width - 120),
  246. // child: Text(
  247. // showStr,
  248. // textScaleFactor: 1.0,
  249. // textAlign: TextAlign.center,
  250. // style: TextStyle(color: Constants.GreyTextColor, fontSize: 12),
  251. // ),
  252. // );
  253. // }
  254. // }
  255. }
  256. return Container();
  257. }
  258. _textGif(List<int> msgContent) {
  259. var msg = utf8.decode(msgContent);
  260. return Container(
  261. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  262. child: extendedText(
  263. msg,
  264. hideKeyboard: widget.hideKeyboard,
  265. fontSize: FontSize,
  266. color: Colors.black,
  267. ),
  268. );
  269. }
  270. _textMsg(List<int> msgContent) {
  271. List<Widget> showMsg = [];
  272. var width = 0.0;
  273. var msg = utf8.decode(msgContent);
  274. if (msg.contains('[ ')) {
  275. msg = msg.replaceAll('[ ', '[');
  276. }
  277. if (msg.contains(' ]')) {
  278. msg = msg.replaceAll(' ]', ']');
  279. }
  280. bool isUrl = false;
  281. if (textList[curTextType].contains('http')) {
  282. isUrl = true;
  283. }
  284. if (widget.msg.refMsgContent != null &&
  285. widget.msg.refMsgContent.length > 0) {
  286. QuoteMsg quoteMsg = QuoteMsg.fromBuffer(widget.msg.refMsgContent);
  287. var refName = Provider.of<RefNameProvider>(context)
  288. .getRefName(friendInfo.userId, friendInfo.nickName);
  289. var msgDate =
  290. DateTime.fromMillisecondsSinceEpoch(quoteMsg.sendTime.toInt());
  291. var showTimeStr = getTodayTime(msgDate, sendTime: quoteMsg.sendTime.toInt());
  292. var timetWidth = CustomUI.getRealTextWidht(showTimeStr, 13);
  293. var nameWidhth = CustomUI.getRealTextWidht(refName, 13);
  294. width = timetWidth + (nameWidhth > 70 ? 70 : nameWidhth) + 36;
  295. var tempIndex = quoteMsg.content.indexOf(':');
  296. var quoteContent = tempIndex == -1
  297. ? quoteMsg.content
  298. : quoteMsg.content.substring(quoteMsg.content.indexOf(':') + 2);
  299. var contentWidht = _getTextWidth(quoteContent, fontSize: 13);
  300. width = min(max(width, contentWidht), maxWidth);
  301. showMsg.add(InkWell(
  302. onTap: () {
  303. MessageMgr().emit('Jump to Msg', quoteMsg.sendTime.toInt());
  304. },
  305. child: Container(
  306. padding: EdgeInsets.all(8),
  307. margin: EdgeInsets.only(bottom: 5),
  308. decoration: BoxDecoration(
  309. color: Color(0xFFC4E0F5),
  310. borderRadius: BorderRadius.circular(5.5)),
  311. child: Column(
  312. crossAxisAlignment: CrossAxisAlignment.start,
  313. children: <Widget>[
  314. Row(children: <Widget>[
  315. Container(
  316. constraints: BoxConstraints(maxWidth: 70),
  317. child: Text(
  318. refName,
  319. style: TextStyle(fontSize: 13),
  320. overflow: TextOverflow.ellipsis,
  321. )),
  322. SizedBox(width: 20),
  323. Text(
  324. showTimeStr,
  325. style: TextStyle(fontSize: 13),
  326. )
  327. ]),
  328. SizedBox(height: 7.5),
  329. Text(
  330. quoteContent,
  331. style: TextStyle(fontSize: 13),
  332. )
  333. ],
  334. ),
  335. )));
  336. }
  337. var contentWidth = _getTextWidth(textList[curTextType]);
  338. if (contentWidth > width && contentWidth <= maxWidth) {
  339. width = contentWidth;
  340. }
  341. showMsg.add(Container(
  342. constraints: BoxConstraints(maxWidth: maxWidth, minHeight: 22),
  343. alignment: Alignment.centerLeft,
  344. child: extendedText(
  345. textList[curTextType],
  346. color: isUrl ? Colors.blue : Constants.BlackTextColor,
  347. hideKeyboard: widget.hideKeyboard,
  348. fontSize: FontSize,
  349. )));
  350. return Container(
  351. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  352. width: width + 20,
  353. child: Column(
  354. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  355. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  356. decoration: BoxDecoration(
  357. color: isLongPressed ? Colors.grey[300] : SendMsgBg,
  358. border: Border.all(color: Color(0xFFB9CBD7), width: 0.6),
  359. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  360. );
  361. }
  362. _soundMsg() {
  363. double time = widget.msg.extraInfo / 1000;
  364. if (time > 60) {
  365. time = 60.0;
  366. }
  367. bool isPlaying = false;
  368. var soundPath = widget.msg.localFile;
  369. isPlaying = SoundUtils().isPlaying(soundPath);
  370. var soundWidget = GestureDetector(
  371. child: Container(
  372. width: 120,
  373. child: Row(
  374. mainAxisAlignment: MainAxisAlignment.end,
  375. children: <Widget>[
  376. Container(
  377. alignment: Alignment.center,
  378. padding: EdgeInsets.only(bottom: 2),
  379. margin: EdgeInsets.only(right: 10),
  380. width: 25.5,
  381. height: 25.5,
  382. decoration: BoxDecoration(
  383. border:
  384. Border.all(color: const Color(0xFF1B92C7), width: 0.5),
  385. color: const Color(0xFF04A4FE),
  386. shape: BoxShape.circle),
  387. child: Icon(
  388. IconData(isPlaying ? 0xe652 : 0xe653,
  389. fontFamily: Constants.IconFontFamily),
  390. size: isPlaying ? 15 : 18,
  391. color: Colors.white,
  392. ),
  393. ),
  394. isPlaying
  395. ? Stack(
  396. children: <Widget>[
  397. Container(
  398. height: 18,
  399. width: 19,
  400. ),
  401. Positioned(
  402. bottom: 0,
  403. child: VideoAnim(
  404. begin: 18,
  405. start: 0.444,
  406. end: 4.5,
  407. )),
  408. Positioned(
  409. left: 7,
  410. bottom: 0,
  411. child: VideoAnim(
  412. begin: 4.5,
  413. end: 18,
  414. )),
  415. Positioned(
  416. left: 14,
  417. bottom: 0,
  418. child: VideoAnim(
  419. begin: 18,
  420. end: 4.5,
  421. ))
  422. ],
  423. )
  424. : Stack(
  425. children: <Widget>[
  426. Container(
  427. height: 18,
  428. width: 19,
  429. ),
  430. Positioned(
  431. bottom: 0, child: CustomUI.buildAudioContaniner(12)),
  432. Positioned(
  433. left: 7,
  434. bottom: 0,
  435. child: CustomUI.buildAudioContaniner(4.5)),
  436. Positioned(
  437. left: 14,
  438. bottom: 0,
  439. child: CustomUI.buildAudioContaniner(18))
  440. ],
  441. ),
  442. Expanded(child: SizedBox()),
  443. fixedText(time.toStringAsFixed(0),
  444. color: Constants.BlackTextColor, fontSize: 16)
  445. ],
  446. ),
  447. padding: EdgeInsets.symmetric(horizontal: 15, vertical: 12),
  448. decoration: BoxDecoration(
  449. color: SendMsgBg,
  450. border: Border.all(color: Color(0xFFB9CBD7), width: 0.6),
  451. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  452. ),
  453. onTap: () async {
  454. print('播放状态 : $isPlaying');
  455. print('当前文件$soundPath ');
  456. if (isPlaying) {
  457. SoundUtils().pause();
  458. } else {
  459. SoundUtils().play(soundPath, onPlayed: () {
  460. if (mounted) {
  461. setState(() {});
  462. }
  463. }, complete: () {
  464. if (mounted) {
  465. setState(() {});
  466. }
  467. });
  468. }
  469. },
  470. );
  471. return UploadImgItem(
  472. msg: widget.msg,
  473. child: soundWidget,
  474. isShowProgress: false,
  475. );
  476. }
  477. Size _getImgSize() {
  478. double aspectRatio = widget.msg.extraInfo / 100;
  479. var maxWidth = Screen.width * 0.65;
  480. var maxHeight = Screen.height / 4;
  481. var width, height;
  482. if (maxWidth / maxHeight > aspectRatio) {
  483. height = maxHeight;
  484. width = maxHeight * aspectRatio;
  485. } else {
  486. width = maxWidth;
  487. height = maxWidth / aspectRatio;
  488. }
  489. return Size(width, height);
  490. }
  491. _imgMsg(List<int> imgData) {
  492. var imgSize = _getImgSize();
  493. ImageProvider provider = MemoryImage(widget.msg.localFile == null
  494. ? Uint8List.fromList(imgData)
  495. : File(widget.msg.localFile).readAsBytesSync());
  496. return GestureDetector(
  497. child: ClipRRect(
  498. child: UploadImgItem(
  499. msg: widget.msg,
  500. cancelToken: _cancelToken,
  501. child: Container(
  502. height: imgSize.height,
  503. width: imgSize.width,
  504. child: Image(
  505. fit: BoxFit.contain,
  506. image: provider,
  507. ),
  508. )),
  509. borderRadius: BorderRadius.circular(5),
  510. ),
  511. onTap: () async {
  512. showFullImg(context, widget.msg);
  513. });
  514. }
  515. _videoMsg(BuildContext context, List<int> thumbnail) {
  516. var imgSize = _getImgSize();
  517. return InkWell(
  518. child: ClipRRect(
  519. child: Stack(
  520. alignment: Alignment.center,
  521. children: <Widget>[
  522. UploadImgItem(
  523. msg: widget.msg,
  524. cancelToken: _cancelToken,
  525. child: Container(
  526. width: imgSize.width,
  527. height: imgSize.height,
  528. child: Image(
  529. fit: BoxFit.contain,
  530. image: MemoryImage(Uint8List.fromList(thumbnail)),
  531. ),
  532. ))
  533. ],
  534. ),
  535. borderRadius: BorderRadius.circular(5),
  536. ),
  537. onTap: () {
  538. showVideoPage(context, widget.msg.localFile);
  539. },
  540. );
  541. }
  542. _msgLayout(BuildContext context, MsgModel msg) {
  543. Widget item;
  544. switch (ChatType.valueOf(msg.msgType)) {
  545. case ChatType.TextChatType:
  546. item = _textMsg(msg.msgContent);
  547. break;
  548. case ChatType.EmoticonType:
  549. item = _textGif(msg.msgContent);
  550. break;
  551. case ChatType.ImageChatType:
  552. item = _imgMsg(msg.msgContent);
  553. break;
  554. case ChatType.ShortVideoChatType:
  555. item = _videoMsg(context, msg.msgContent);
  556. break;
  557. case ChatType.ShortVoiceChatType:
  558. item = _soundMsg();
  559. break;
  560. case ChatType.RedWalletChatType:
  561. item = RedBagItem(
  562. Key(msg.time.toString()), UserData().basicInfo.userId, msg);
  563. break;
  564. case ChatType.PlaceChatType:
  565. item = PlaceItem(isMe: true, placeContent: msg.msgContent);
  566. break;
  567. case ChatType.GiftChatType:
  568. item = GiftMsgItem(msg.msgContent, true);
  569. break;
  570. case ChatType.FileChatType:
  571. item = _fileMsgItem();
  572. break;
  573. default:
  574. }
  575. return wrapItemWithMenu(item);
  576. }
  577. Widget _fileMsgItem() {
  578. return UploadImgItem(
  579. msg: widget.msg,
  580. cancelToken: _cancelToken,
  581. child: Container(
  582. height: 100,
  583. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  584. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  585. decoration: BoxDecoration(
  586. color: isLongPressed ? Colors.grey[300] : SendMsgBg,
  587. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  588. child: FileMsgItem(widget.msg)));
  589. }
  590. Widget _getSentMessageLayout(BuildContext context) {
  591. bool hasHeadImg = true;
  592. if (UserData().basicInfo.headimgurl == null ||
  593. UserData().basicInfo.headimgurl.length == 0) {
  594. hasHeadImg = false;
  595. }
  596. return Row(
  597. crossAxisAlignment: CrossAxisAlignment.start,
  598. mainAxisAlignment: MainAxisAlignment.end,
  599. children: <Widget>[
  600. MsgStateWidget(widget.msg),
  601. SizedBox(width: 3),
  602. _msgLayout(context, widget.msg),
  603. SizedBox(width: 10),
  604. Column(
  605. crossAxisAlignment: CrossAxisAlignment.end,
  606. children: <Widget>[
  607. ClipRRect(
  608. borderRadius: BorderRadius.circular(8),
  609. child: hasHeadImg
  610. ? CachedNetworkImage(
  611. imageUrl: UserData().basicInfo.headimgurl,
  612. placeholder: (context, url) => Image.asset(
  613. Constants.DefaultHeadImgUrl,
  614. width: 40,
  615. height: 40,
  616. ),
  617. width: 40,
  618. height: 40,
  619. )
  620. : SizedBox(
  621. width: 40,
  622. height: 40,
  623. child: Image.asset(R.assetsImagesDefaultNorAvatar))),
  624. ],
  625. )
  626. ]);
  627. }
  628. Widget wrapItemWithMenu(item) {
  629. List<Function> actionsFunc = [];
  630. List<String> actions = [
  631. I18n.of(context).delete,
  632. I18n.of(context).reply,
  633. ];
  634. actionsFunc.add(() {
  635. MessageMgr().emit('Delete Select Message', widget.msg);
  636. });
  637. actionsFunc.add(() {
  638. print('发送引用的消息');
  639. MessageMgr().emit('Reply Select Message', widget.msg);
  640. });
  641. ///转发
  642. if (widget.msg.msgType == ChatType.TextChatType.value ||
  643. widget.msg.msgType == ChatType.ImageChatType.value ||
  644. widget.msg.msgType == ChatType.ShortVideoChatType.value ||
  645. widget.msg.msgType == ChatType.PlaceChatType.value ||
  646. widget.msg.msgType == ChatType.EmoticonType.value ||
  647. widget.msg.msgType == ChatType.FileChatType.value) {
  648. actions.add(I18n.of(context).forward);
  649. actionsFunc.add(() {
  650. print('转发消息');
  651. if (widget.msg.msgType == ChatType.FileChatType.value &&
  652. widget.msg.localFile == null) {
  653. showToast('请先下载文件');
  654. return;
  655. }
  656. AppNavigator.pushForwardPage(context, widget.msg);
  657. });
  658. }
  659. if (widget.msg.msgType == ChatType.FileChatType.value &&
  660. widget.msg.localFile != null) {
  661. //分享文件
  662. actions.add(I18n.of(context).copy_download_url);
  663. actionsFunc.add(() async {
  664. UploadUtil().copyFileUrl(widget.msg, context);
  665. String path = widget.msg.localFile;
  666. String type = 'file';
  667. if (path.contains('mp4') || path.contains('mp3')) {
  668. type = 'video';
  669. } else if (path.contains('png') ||
  670. path.contains('jpg') ||
  671. path.contains('jpeg') ||
  672. path.contains('JPG') ||
  673. path.contains('PNG')) {
  674. type = 'image';
  675. }
  676. ShareExtend.share(FileCacheMgr.replacePath(path), type);
  677. });
  678. }
  679. if (widget.msg.msgType == ChatType.TextChatType.value) {
  680. actions.insert(0, I18n.of(context).copy);
  681. actionsFunc.insert(0, () {
  682. //复制当前的文字
  683. print('复制文字 ${textList[curTextType]}');
  684. ClipboardData clipboardData =
  685. ClipboardData(text: textList[curTextType]);
  686. Clipboard.setData(clipboardData);
  687. });
  688. }
  689. if (widget.msg.msgType == ChatType.ShortVoiceChatType.value) {
  690. var soundPlayMode =
  691. Provider.of<KeyboardIndexProvider>(context).soundPlayMode;
  692. actions.add(soundPlayMode
  693. ? I18n.of(context).handset_playback
  694. : I18n.of(context).speaker_play);
  695. actionsFunc.add(() {
  696. Provider.of<KeyboardIndexProvider>(context)
  697. .changeSoundPlayMode(!soundPlayMode);
  698. SoundUtils.instance.savePlayModeConfig(soundPlayMode);
  699. });
  700. }
  701. // String date2 = DateTime.fromMillisecondsSinceEpoch(widget.msg.time).toString();
  702. bool isUrl = false;
  703. if (widget.msg.msgType == ChatType.TextChatType.value) {
  704. if (textList[curTextType].contains('http')) {
  705. isUrl = true;
  706. }
  707. }
  708. return WPopupMenu(
  709. child: item,
  710. actions: actions,
  711. onTap: () async {
  712. if (isUrl) {
  713. if (await canLaunch(textList[curTextType])) {
  714. launch(textList[curTextType]);
  715. }
  716. }
  717. },
  718. onLongPressStart: () {
  719. isLongPressed = true;
  720. setState(() {});
  721. },
  722. onLongPressEnd: () {
  723. isLongPressed = false;
  724. setState(() {});
  725. },
  726. onValueChanged: (int value) {
  727. print('选择的是$value个菜单');
  728. if (value >= 0 && value < actionsFunc.length) {
  729. actionsFunc[value]();
  730. }
  731. },
  732. );
  733. }
  734. //用户评价人工翻译,差评
  735. rateTranslateResult() async {
  736. return await HttpUtil().rateTranslateResult(widget.msg, () {
  737. if (mounted) {
  738. setState(() {});
  739. }
  740. });
  741. }
  742. _receiveJIF(MsgModel msg) {
  743. var text = utf8.decode(msg.msgContent);
  744. return extendedText(text, hideKeyboard: widget.hideKeyboard);
  745. }
  746. double _getTextWidth(String text, {double fontSize: FontSize}) {
  747. var tp = TextPainter(
  748. text: TextSpan(style: TextStyle(fontSize: fontSize), text: text),
  749. textAlign: TextAlign.left,
  750. textDirection: TextDirection.ltr,
  751. textScaleFactor: 1,
  752. );
  753. tp.layout(maxWidth: Screen.width - 140);
  754. RegExp alterStr = RegExp(r'\[([0-9]+)\]');
  755. Iterable<Match> matches = alterStr.allMatches(text);
  756. // print('~~~~~~~~~~~~~~${matches.length}~~~~~~~~~~~~~~~');
  757. double delta = 0;
  758. for (Match m in matches) {
  759. // print('~~~~~~~~~~~~~~${m.group(1)}~~~~~~~~~~~~~~~');
  760. if (int.parse(m.group(1)) > 10) {
  761. delta += 20 + 4 - 25;
  762. } else {
  763. delta += 20 + 4 - 16.8;
  764. }
  765. }
  766. return tp.width + delta;
  767. }
  768. _receiveText(MsgModel msg) {
  769. List<Widget> showMsg = [];
  770. double width = 0.0;
  771. if (textList.length > 0) {
  772. bool isUrl = false;
  773. if (textList[curTextType].contains('http')) {
  774. isUrl = true;
  775. }
  776. if (msg.refMsgContent != null && msg.refMsgContent.length > 0) {
  777. QuoteMsg quoteMsg = QuoteMsg.fromBuffer(msg.refMsgContent);
  778. var refName = Provider.of<RefNameProvider>(context)
  779. .getGroupRefName(msg.sessionId, quoteMsg.sendUserId);
  780. var msgDate =
  781. DateTime.fromMillisecondsSinceEpoch(quoteMsg.sendTime.toInt());
  782. var showTimeStr = getTodayTime(msgDate, sendTime: quoteMsg.sendTime.toInt());
  783. var timetWidth = CustomUI.getRealTextWidht(showTimeStr, 13);
  784. var nameWidhth = CustomUI.getRealTextWidht(refName, 13);
  785. width = timetWidth + (nameWidhth > 70 ? 70 : nameWidhth) + 36;
  786. var quoteContent =
  787. quoteMsg.content.substring(quoteMsg.content.indexOf(':') + 2);
  788. var contentWidht = _getTextWidth(quoteContent, fontSize: 13);
  789. width = min(max(width, contentWidht), maxWidth);
  790. showMsg.add(InkWell(
  791. onTap: () {
  792. MessageMgr().emit('Jump to Msg', quoteMsg.sendTime.toInt());
  793. },
  794. child: Container(
  795. padding: EdgeInsets.all(8),
  796. margin: EdgeInsets.only(bottom: 5),
  797. decoration: BoxDecoration(
  798. color: Color(0xFFC4E0F5),
  799. borderRadius: BorderRadius.circular(5.5)),
  800. child: Column(
  801. crossAxisAlignment: CrossAxisAlignment.start,
  802. children: <Widget>[
  803. Row(children: <Widget>[
  804. Container(
  805. constraints: BoxConstraints(maxWidth: 70),
  806. child: Text(
  807. refName,
  808. style: TextStyle(fontSize: 13),
  809. overflow: TextOverflow.ellipsis,
  810. )),
  811. SizedBox(width: 20),
  812. Text(
  813. showTimeStr,
  814. style: TextStyle(fontSize: 13),
  815. )
  816. ]),
  817. SizedBox(height: 7.5),
  818. Text(
  819. quoteContent,
  820. style: TextStyle(fontSize: 13),
  821. )
  822. ],
  823. ),
  824. )));
  825. }
  826. var contentWidth = _getTextWidth(textList[curTextType]);
  827. if (contentWidth > width && contentWidth <= maxWidth) {
  828. width = contentWidth;
  829. }
  830. showMsg.add(InkWell(
  831. onTap: () {
  832. if (msg.transTag == 1) {
  833. return;
  834. }
  835. if (msg.transTag == 2 || msg.transTag == 3) {
  836. setState(() {
  837. curTextType += 1;
  838. curTextType %= textList.length;
  839. });
  840. return;
  841. }
  842. },
  843. child: Container(
  844. constraints: BoxConstraints(maxWidth: maxWidth, minHeight: 22),
  845. alignment: Alignment.centerLeft,
  846. child: extendedText(
  847. textList[curTextType],
  848. color: isUrl ? Colors.blue : Constants.BlackTextColor,
  849. hideKeyboard: widget.hideKeyboard,
  850. fontSize: FontSize,
  851. ))));
  852. }
  853. var minWidth = width + 20;
  854. if (msg.transTag != 0) {
  855. minWidth = 200;
  856. showMsg.add(Padding(
  857. padding: EdgeInsets.symmetric(vertical: 5),
  858. child: Divider(color: Color(0xFFECECEC), height: 1)));
  859. Widget tranWidget = _transProcessWidget(msg.transTag);
  860. showMsg.add(tranWidget);
  861. }
  862. Widget text = Stack(children: <Widget>[
  863. Container(
  864. width: width + 20,
  865. constraints:
  866. BoxConstraints(maxWidth: Screen.width - 120, minWidth: minWidth),
  867. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  868. child: Column(
  869. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  870. decoration: BoxDecoration(
  871. border: Border.all(color: ReciveBorderColor, width: 0.5),
  872. color: isLongPressed ? Colors.grey[300] : Colors.white,
  873. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  874. ),
  875. msg.transTag != 1 && msg.transTag != 0
  876. ? Positioned(
  877. right: 5,
  878. top: 5,
  879. child: Container(
  880. child: Row(children: <Widget>[
  881. blueDot(curTextType == 0),
  882. blueDot(curTextType == 1),
  883. blueDot(curTextType == 2),
  884. //blueDot(true),
  885. ])))
  886. : Container()
  887. ]);
  888. return text;
  889. }
  890. _translateItemWidget(int code, String title, Function onTap) {
  891. Color color = onTap == null ? Constants.GreyTextColor : Color(0xFF087FF3);
  892. return InkWell(
  893. onTap: onTap,
  894. splashColor: Colors.red,
  895. child: Container(
  896. width: 80,
  897. child: Row(
  898. children: <Widget>[
  899. Icon(IconData(code, fontFamily: Constants.IconFontFamily),
  900. color: color, size: 20),
  901. SizedBox(width: 5),
  902. Expanded(
  903. child: SizedBox(
  904. child: Text(
  905. title,
  906. style: TextStyle(color: color, fontSize: 10),
  907. textScaleFactor: 1.0,
  908. maxLines: 1,
  909. overflow: TextOverflow.ellipsis,
  910. ),
  911. ))
  912. ],
  913. )));
  914. }
  915. bool isTranslating = false;
  916. _transProcessWidget(int transTag) {
  917. double width = 160;
  918. Widget userTranslateWidget;
  919. Widget machineTranslateWidget;
  920. if (transTag == 1) {
  921. //机器翻译中
  922. userTranslateWidget =
  923. _translateItemWidget(0xe670, I18n.of(context).man_retranslate, null);
  924. machineTranslateWidget =
  925. _translateItemWidget(0xe671, I18n.of(context).robotTranslate, null);
  926. } else if (transTag == 2) {
  927. //人工翻译中
  928. userTranslateWidget =
  929. _translateItemWidget(0xe670, I18n.of(context).ManTranslate, null);
  930. machineTranslateWidget =
  931. _translateItemWidget(0xe671, I18n.of(context).robot_retranslate, () {
  932. setState(() {
  933. curTextType += 1;
  934. curTextType %= textList.length;
  935. });
  936. });
  937. } else if (transTag == 3) {
  938. //机器翻译完成
  939. userTranslateWidget = _translateItemWidget(
  940. 0xe670,
  941. I18n.of(context).man_retranslate,
  942. isTranslating
  943. ? null
  944. : () async {
  945. isTranslating = true;
  946. int money =
  947. Provider.of<MoneyChangeProvider>(context, listen: false)
  948. .money;
  949. int voucher =
  950. Provider.of<VoucherChangeProvider>(context, listen: false)
  951. .voucher;
  952. int needMoney = widget.msg.getNeedMoney();
  953. if (needMoney > voucher + money) {
  954. showToast('翻译券和H币不足');
  955. return;
  956. }
  957. var res = await HttpUtil().getPersonalTranslate(widget.msg);
  958. if (res) {
  959. print('请求人工翻译成功,进行扣费');
  960. setState(() {
  961. widget.msg.transTag = 2;
  962. //优先扣券
  963. if (voucher > 0) {
  964. int costQuan = min(voucher, needMoney);
  965. Provider.of<VoucherChangeProvider>(context,
  966. listen: false)
  967. .subVoucher(costQuan);
  968. }
  969. //不足的话再扣H币
  970. if (needMoney > voucher) {
  971. Provider.of<MoneyChangeProvider>(context, listen: false)
  972. .subMoney(needMoney - voucher);
  973. }
  974. });
  975. }
  976. });
  977. machineTranslateWidget =
  978. _translateItemWidget(0xe671, I18n.of(context).robot_retranslate, () {
  979. setState(() {
  980. curTextType += 1;
  981. curTextType %= textList.length;
  982. });
  983. });
  984. } else if (transTag == 4 || transTag == 10) {
  985. //4人工翻译完成,未评论 10人工翻译完成已评论
  986. userTranslateWidget = InkWell(
  987. onTap: () {
  988. setState(() {
  989. curTextType = 0;
  990. });
  991. },
  992. child: Container(
  993. width: width / 2,
  994. child: Row(
  995. children: <Widget>[
  996. InkWell(
  997. child: Icon(
  998. IconData(0xe641, fontFamily: Constants.IconFontFamily),
  999. size: 18,
  1000. color:
  1001. transTag == 10 ? Colors.grey : Color(0xFF087FF3)),
  1002. onTap: transTag == 10
  1003. ? null
  1004. : () {
  1005. CustomUI.showIosDialog(
  1006. context, I18n.of(context).bad_ev, () async {
  1007. bool isSuccess = await rateTranslateResult();
  1008. if (isSuccess) {
  1009. Navigator.of(context).pop(true);
  1010. showToast(I18n.of(context).success);
  1011. } else {
  1012. showToast(I18n.of(context).fail);
  1013. Navigator.of(context).pop();
  1014. }
  1015. }, () {
  1016. Navigator.of(context).pop();
  1017. });
  1018. },
  1019. ),
  1020. SizedBox(width: 5),
  1021. Expanded(
  1022. child: SizedBox(
  1023. child: Text(
  1024. I18n.of(context).over,
  1025. style: TextStyle(color: Color(0xFF087FF3), fontSize: 10),
  1026. textScaleFactor: 1.0,
  1027. maxLines: 1,
  1028. overflow: TextOverflow.ellipsis,
  1029. ),
  1030. ))
  1031. ],
  1032. )));
  1033. machineTranslateWidget =
  1034. _translateItemWidget(0xe675, I18n.of(context).see_original, () {
  1035. setState(() {
  1036. curTextType = textList.length - 1;
  1037. });
  1038. });
  1039. }
  1040. return Container(
  1041. height: 26,
  1042. alignment: Alignment.center,
  1043. child: Row(
  1044. children: <Widget>[
  1045. userTranslateWidget,
  1046. Expanded(
  1047. child: SizedBox(child: VerticalDivider(), height: 20, width: 2)),
  1048. machineTranslateWidget
  1049. ],
  1050. ),
  1051. );
  1052. }
  1053. _receiveImg(BuildContext context, List<int> imgData, {String downloadData}) {
  1054. ImageProvider provider = MemoryImage(widget.msg.localFile == null
  1055. ? Uint8List.fromList(imgData)
  1056. : File(widget.msg.localFile).readAsBytesSync());
  1057. var imgSize = _getImgSize();
  1058. return DownloadItem(
  1059. isAutoDown: false,
  1060. msg: widget.msg,
  1061. onFinishTap: () {
  1062. widget.hideKeyboard();
  1063. showFullImg(context, widget.msg);
  1064. },
  1065. child: Container(
  1066. width: imgSize.width,
  1067. height: imgSize.height,
  1068. child: ClipRRect(
  1069. child: Image(
  1070. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  1071. ),
  1072. borderRadius: BorderRadius.circular(5),
  1073. ),
  1074. ),
  1075. );
  1076. }
  1077. _receiveVideo(BuildContext context, List<int> imgData,
  1078. {String downloadData}) {
  1079. ImageProvider provider = MemoryImage(Uint8List.fromList(imgData));
  1080. var imgSize = _getImgSize();
  1081. return InkWell(
  1082. onTap: () {
  1083. if (widget.msg.localFile != null) {
  1084. showVideoPage(context, widget.msg.localFile);
  1085. }
  1086. },
  1087. child: DownloadItem(
  1088. isAutoDown: false,
  1089. msg: widget.msg,
  1090. child: Container(
  1091. width: imgSize.width,
  1092. height: imgSize.height,
  1093. child: ClipRRect(
  1094. child: Image(
  1095. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  1096. ),
  1097. borderRadius: BorderRadius.circular(5),
  1098. ),
  1099. ),
  1100. ));
  1101. }
  1102. showVideoPage(BuildContext context, String filePath) {
  1103. widget.hideKeyboard();
  1104. Navigator.push(context,
  1105. MaterialPageRoute<void>(builder: (BuildContext context) {
  1106. return VideoPage(videoPath: filePath);
  1107. }));
  1108. }
  1109. Widget _receiveFileMsgItem() {
  1110. return DownloadItem(
  1111. isAutoDown: false,
  1112. msg: widget.msg,
  1113. onComplete: () {
  1114. if (mounted) {
  1115. setState(() {});
  1116. }
  1117. },
  1118. child: Container(
  1119. height: 100,
  1120. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  1121. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  1122. decoration: BoxDecoration(
  1123. color: Colors.white,
  1124. border: Border.all(color: ReciveBorderColor, width: 0.5),
  1125. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  1126. child: FileMsgItem(widget.msg)));
  1127. }
  1128. _receiveSound(BuildContext context, List<int> soundData) {
  1129. print('收到语音消息');
  1130. MsgModel msg = widget.msg;
  1131. var time = widget.msg.extraInfo / 1000;
  1132. if (time > 60) {
  1133. time = 60.0;
  1134. }
  1135. bool isPlaying = false;
  1136. if (curSoundUrl != null) {
  1137. isPlaying = SoundUtils().isPlaying(curSoundUrl);
  1138. }
  1139. var soundWidget = InkWell(
  1140. onTap: () async {
  1141. bool isLocal = true;
  1142. if (msg.localFile != null) {
  1143. isLocal = true;
  1144. curSoundUrl = msg.localFile;
  1145. } else {
  1146. isLocal = false;
  1147. var sessionId = msg.sessionId;
  1148. curSoundUrl = UploadUtil()
  1149. .getFullUrl(msg.extraFile, sessionId, msg.channelType);
  1150. }
  1151. print('当前文件$curSoundUrl 本地?$isLocal');
  1152. if (isPlaying) {
  1153. await SoundUtils().pause();
  1154. } else {
  1155. print('开始播放');
  1156. if (widget.msg.soundListened == 0) {
  1157. widget.msg.updateSoundListened();
  1158. } //设置为已经播放
  1159. await SoundUtils().play(curSoundUrl, isLocal: isLocal, onPlayed: () {
  1160. if (mounted) {
  1161. this.setState(() {});
  1162. }
  1163. }, complete: () {
  1164. if (mounted) {
  1165. this.setState(() {});
  1166. }
  1167. });
  1168. }
  1169. },
  1170. child: Container(
  1171. width: 130,
  1172. child: Row(children: <Widget>[
  1173. Container(
  1174. alignment: Alignment.center,
  1175. padding: EdgeInsets.only(bottom: 2),
  1176. margin: EdgeInsets.only(right: 10),
  1177. width: 25.5,
  1178. height: 25.5,
  1179. decoration: BoxDecoration(
  1180. border:
  1181. Border.all(color: const Color(0xFF1B92C7), width: 0.5),
  1182. color: const Color(0xFF04A4FE),
  1183. shape: BoxShape.circle),
  1184. child: Icon(
  1185. IconData(isPlaying ? 0xe652 : 0xe653,
  1186. fontFamily: Constants.IconFontFamily),
  1187. size: isPlaying ? 15 : 18,
  1188. color: Colors.white,
  1189. ),
  1190. ),
  1191. isPlaying
  1192. ? Stack(
  1193. children: <Widget>[
  1194. Container(
  1195. height: 18,
  1196. width: 19,
  1197. ),
  1198. Positioned(
  1199. bottom: 0,
  1200. child: VideoAnim(
  1201. begin: 18,
  1202. start: 0.444,
  1203. end: 4.5,
  1204. )),
  1205. Positioned(
  1206. left: 7,
  1207. bottom: 0,
  1208. child: VideoAnim(
  1209. begin: 4.5,
  1210. end: 18,
  1211. )),
  1212. Positioned(
  1213. left: 14,
  1214. bottom: 0,
  1215. child: VideoAnim(
  1216. begin: 18,
  1217. end: 4.5,
  1218. ))
  1219. ],
  1220. )
  1221. : Stack(
  1222. children: <Widget>[
  1223. Container(
  1224. height: 18,
  1225. width: 19,
  1226. ),
  1227. Positioned(
  1228. bottom: 0, child: CustomUI.buildAudioContaniner(12)),
  1229. Positioned(
  1230. left: 7,
  1231. bottom: 0,
  1232. child: CustomUI.buildAudioContaniner(4.5)),
  1233. Positioned(
  1234. left: 14,
  1235. bottom: 0,
  1236. child: CustomUI.buildAudioContaniner(18))
  1237. ],
  1238. ),
  1239. Expanded(child: SizedBox()),
  1240. fixedText(time.toStringAsFixed(0),
  1241. color: Constants.BlackTextColor, fontSize: 16)
  1242. ])),
  1243. );
  1244. List<Widget> showMsg = [];
  1245. showMsg.add(DownloadItem(
  1246. msg: widget.msg,
  1247. child: soundWidget,
  1248. isShowProgress: false,
  1249. ));
  1250. double width = 130;
  1251. double minWidth = 0;
  1252. if (textList.length > 0) {
  1253. width = _getTextWidth(textList[curTextType]) + 20;
  1254. width = min(width, Screen.width - 120);
  1255. showMsg.add(Padding(
  1256. padding: EdgeInsets.symmetric(vertical: 5),
  1257. child: Divider(
  1258. height: 1,
  1259. )));
  1260. showMsg.add(Container(
  1261. child: extendedText(
  1262. textList[curTextType],
  1263. color: Constants.BlackTextColor,
  1264. hideKeyboard: widget.hideKeyboard,
  1265. fontSize: FontSize,
  1266. ),
  1267. alignment: Alignment.centerLeft,
  1268. constraints:
  1269. BoxConstraints(maxWidth: Screen.width - 120, minHeight: 22),
  1270. ));
  1271. }
  1272. if (msg.transTag != 0) {
  1273. minWidth = 200;
  1274. showMsg.add(Divider(color: Color(0xFFECECEC), height: 3));
  1275. Widget tranWidget = _transProcessWidget(msg.transTag);
  1276. showMsg.add(tranWidget);
  1277. }
  1278. return Container(
  1279. width: width + 20,
  1280. constraints:
  1281. BoxConstraints(maxWidth: Screen.width - 120, minWidth: minWidth),
  1282. child: Column(
  1283. crossAxisAlignment: CrossAxisAlignment.start, children: showMsg),
  1284. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  1285. decoration: BoxDecoration(
  1286. color: Colors.white,
  1287. border: Border.all(color: ReciveBorderColor, width: 0.5),
  1288. borderRadius: BorderRadius.all(Radius.circular(ChatRadius))),
  1289. );
  1290. }
  1291. void showFullImg(BuildContext context, MsgModel msg) {
  1292. print('显示图片');
  1293. Navigator.push(context,
  1294. MaterialPageRoute<void>(builder: (BuildContext context) {
  1295. return PhotoPage(msg: msg);
  1296. }));
  1297. }
  1298. blueDot(bool isShow) {
  1299. return Container(
  1300. margin: EdgeInsets.only(right: 5),
  1301. decoration: BoxDecoration(
  1302. shape: BoxShape.circle,
  1303. color: isShow ? Color(0xFFFF7E00) : Color(0xFFCFCFCF),
  1304. ),
  1305. width: 6,
  1306. height: 6,
  1307. );
  1308. }
  1309. _reveiveMsg(BuildContext context) {
  1310. Widget item;
  1311. switch (ChatType.valueOf(widget.msg.msgType)) {
  1312. case ChatType.TextChatType:
  1313. item = _receiveText(widget.msg);
  1314. break;
  1315. case ChatType.EmoticonType:
  1316. item = _receiveJIF(widget.msg);
  1317. break;
  1318. case ChatType.ImageChatType:
  1319. if (widget.msg.extraFile != null) {
  1320. item = _receiveImg(context, widget.msg.msgContent,
  1321. downloadData: widget.msg.extraFile);
  1322. } else {
  1323. item = _receiveImg(context, widget.msg.msgContent);
  1324. }
  1325. break;
  1326. case ChatType.ShortVideoChatType:
  1327. item = _receiveVideo(context, widget.msg.msgContent,
  1328. downloadData: widget.msg.extraFile);
  1329. break;
  1330. case ChatType.ShortVoiceChatType:
  1331. item = _receiveSound(context, widget.msg.msgContent);
  1332. break;
  1333. case ChatType.RedWalletChatType:
  1334. item = RedBagItem(
  1335. Key(widget.msg.time.toString()), friendInfo.userId, widget.msg);
  1336. break;
  1337. case ChatType.PlaceChatType:
  1338. item = PlaceItem(isMe: false, placeContent: widget.msg.msgContent);
  1339. break;
  1340. case ChatType.GiftChatType:
  1341. item = GiftMsgItem(widget.msg.msgContent, false);
  1342. break;
  1343. case ChatType.FileChatType:
  1344. item = _receiveFileMsgItem();
  1345. break;
  1346. default:
  1347. }
  1348. return wrapItemWithMenu(item);
  1349. }
  1350. Widget _getReceivedMessageLayout(BuildContext context) {
  1351. bool hasHeadImg = true;
  1352. if (friendInfo.headimgurl == null || friendInfo.headimgurl.length == 0) {
  1353. hasHeadImg = false;
  1354. }
  1355. bool isShowSoundSate =
  1356. widget.msg.msgType == ChatType.ShortVoiceChatType.value &&
  1357. widget.msg.soundListened == 0;
  1358. return Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
  1359. Container(
  1360. margin: const EdgeInsets.only(right: 8.0),
  1361. child: InkWell(
  1362. child: ClipRRect(
  1363. borderRadius: BorderRadius.circular(8),
  1364. child: hasHeadImg
  1365. ? CachedNetworkImage(
  1366. imageUrl: friendInfo.headimgurl,
  1367. placeholder: (context, url) => Image.asset(
  1368. Constants.DefaultHeadImgUrl,
  1369. width: 40,
  1370. height: 40,
  1371. ),
  1372. width: 40,
  1373. height: 40,
  1374. )
  1375. : SizedBox(
  1376. width: 40,
  1377. height: 40,
  1378. child: Image.asset(R.assetsImagesDefaultNorAvatar))),
  1379. onTap: () {
  1380. AppNavigator.pushProfileInfoPage(context, friendInfo.userId,
  1381. fromWhere: 0);
  1382. },
  1383. )),
  1384. _reveiveMsg(context),
  1385. isShowSoundSate
  1386. ? Container(
  1387. margin: EdgeInsets.only(left: 8),
  1388. width: 40,
  1389. height: 40,
  1390. alignment: Alignment.centerLeft,
  1391. child: CircleAvatar(
  1392. radius: 3.5,
  1393. backgroundColor: Colors.red,
  1394. ))
  1395. : Container()
  1396. ]);
  1397. }
  1398. Widget _buildServiceCard(bool isStart, Function callBack) {
  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. 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. : I18n.of(context).translation_butler_evaluation_tips,
  1428. textScaleFactor: 1.0,
  1429. style: TextStyle(
  1430. color: AppColors.NewAppbarTextColor, fontSize: 13),
  1431. )
  1432. ],
  1433. ),
  1434. isStart
  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. }