Hibok
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

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