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

617 рядки
21 KiB

  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:chat/chat/record_view.dart';
  3. import 'package:chat/data/UserData.dart';
  4. import 'package:chat/data/constants.dart';
  5. import 'package:chat/data/robot_chat_mgr.dart';
  6. import 'package:chat/generated/i18n.dart';
  7. import 'package:chat/utils/CustomUI.dart';
  8. import 'package:chat/utils/keyboard_utils.dart';
  9. import 'package:chat/utils/net_state_widget.dart';
  10. import 'package:chat/utils/screen.dart';
  11. import 'package:chat/utils/sound_util.dart';
  12. import 'package:chat/utils/upload_util.dart';
  13. import 'package:chat/utils/video_anim.dart';
  14. import 'package:chat/utils/wpop/w_popup_menu.dart';
  15. import 'package:flutter/material.dart';
  16. import 'package:chat/utils/PopUpMenu.dart' as myPop;
  17. import 'package:flutter/services.dart';
  18. import 'package:flutter_screenutil/flutter_screenutil.dart';
  19. //import 'package:menu/menu.dart';
  20. import 'package:oktoast/oktoast.dart';
  21. import 'package:shared_preferences/shared_preferences.dart';
  22. import '../r.dart';
  23. const Color SendMsgBg = Color(0xFFD4F0FF);
  24. class TranslateRobotPage extends StatefulWidget {
  25. @override
  26. _TranslateRobotPageState createState() => _TranslateRobotPageState();
  27. }
  28. class _TranslateRobotPageState extends State<TranslateRobotPage> {
  29. final TextEditingController _textCtrl = TextEditingController();
  30. FocusNode editFocus = FocusNode();
  31. bool _isComposingMessage = false;
  32. int curToLang = 1;
  33. int curSourceLang = UserData().language;
  34. ScrollController _scrollCtrl = ScrollController();
  35. int curKeyboardIndex = -1;
  36. List langList;
  37. double keyboardHeight;
  38. KeyboardBloc _bloc = KeyboardBloc();
  39. @override
  40. void initState() {
  41. super.initState();
  42. getKeyboardHeight();
  43. _bloc.start();
  44. }
  45. @override
  46. void didChangeDependencies() {
  47. super.didChangeDependencies();
  48. langList = [
  49. I18n.of(context).english,
  50. I18n.of(context).Vietnamese,
  51. I18n.of(context).traditional_Chinese,
  52. I18n.of(context).Simplified_Chinese,
  53. I18n.of(context).Korean,
  54. I18n.of(context).Japanese,
  55. ];
  56. }
  57. @override
  58. void dispose() {
  59. editFocus?.unfocus();
  60. super.dispose();
  61. }
  62. getKeyboardHeight() async {
  63. var sp = await SharedPreferences.getInstance();
  64. keyboardHeight = sp.getDouble(Constants.KeyboardHeight);
  65. if (keyboardHeight == null || keyboardHeight < 100) {
  66. keyboardHeight = 280;
  67. }
  68. }
  69. hideKeyBoard() {
  70. setState(() {
  71. curKeyboardIndex = -1;
  72. });
  73. }
  74. _langPopMenu(bool isSource) {
  75. var curIndex = isSource ? curSourceLang : curToLang;
  76. return myPop.PopupMenuButton(
  77. child: Container(
  78. child: Row(
  79. children: <Widget>[
  80. fixedText(langList[curIndex], color: Colors.black, fontSize: 16),
  81. Icon(IconData(0xe63b, fontFamily: Constants.IconFontFamily),
  82. color: Colors.grey)
  83. ],
  84. ),
  85. ),
  86. offset: Offset(0, 100),
  87. onSelected: (int index) {
  88. if (curIndex != index) {
  89. if (isSource) {
  90. curSourceLang = index;
  91. } else {
  92. curToLang = index;
  93. }
  94. print('更换翻译语言');
  95. setState(() {});
  96. }
  97. },
  98. itemBuilder: (BuildContext context) {
  99. return List<myPop.PopupMenuItem<int>>.generate(langList.length,
  100. (int i) {
  101. return myPop.PopupMenuItem(
  102. child: Container(
  103. alignment: Alignment.center,
  104. color: Colors.white,
  105. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  106. child: Text(langList[i],
  107. textScaleFactor: 1.0,
  108. maxLines: 1,
  109. style: TextStyle(
  110. color: i == curIndex
  111. ? Colors.blueAccent
  112. : Color(AppColors.AppBarColor),
  113. fontSize: 14)),
  114. ),
  115. value: i,
  116. );
  117. });
  118. });
  119. }
  120. @override
  121. Widget build(BuildContext context) {
  122. return GestureDetector(
  123. onTap: hideKeyBoard,
  124. child: Scaffold(
  125. resizeToAvoidBottomInset: false,
  126. appBar: AppBar(
  127. title: Row(
  128. mainAxisSize: MainAxisSize.min,
  129. children: <Widget>[
  130. _langPopMenu(true),
  131. SizedBox(width: 10),
  132. InkWell(
  133. onTap: () {
  134. var temp = curSourceLang;
  135. curSourceLang = curToLang;
  136. curToLang = temp;
  137. setState(() {});
  138. },
  139. child: Container(
  140. child: Icon(
  141. IconData(0xe669, fontFamily: Constants.IconFontFamily),
  142. size: 12,
  143. ),
  144. padding: EdgeInsets.symmetric(vertical: 5, horizontal: 5),
  145. ),
  146. ),
  147. SizedBox(width: 20),
  148. _langPopMenu(false),
  149. ],
  150. ),
  151. centerTitle: true,
  152. leading: CustomUI.buildCustomLeading(context),
  153. ),
  154. body: SafeArea(
  155. child: Column(
  156. children: <Widget>[
  157. NetStateWidget(),
  158. Expanded(child: _buildMessageList()),
  159. _inputBar(),
  160. ],
  161. )),
  162. ));
  163. }
  164. Widget _buildMessageList() {
  165. return Container(
  166. alignment: Alignment.topCenter,
  167. child: ListView.builder(
  168. reverse: true,
  169. shrinkWrap: true,
  170. itemCount: RobotChatMgr().robotChatList.length,
  171. controller: _scrollCtrl,
  172. padding: EdgeInsets.all(8.0),
  173. itemBuilder: _buildItem,
  174. ),
  175. );
  176. }
  177. Widget _buildItem(BuildContext context, int index) {
  178. var chat = RobotChatMgr().robotChatList[index];
  179. List<Function> actionsFunc = [];
  180. List<String> actions = [I18n.of(context).delete];
  181. if (chat.type == 0) {
  182. actions.add(I18n.of(context).copy);
  183. }
  184. actionsFunc.add(() {
  185. RobotChatMgr().robotChatList.remove(chat);
  186. setState(() {});
  187. });
  188. actionsFunc.add(() {
  189. ClipboardData clipboardData = ClipboardData(text: chat.msgContent);
  190. Clipboard.setData(clipboardData);
  191. });
  192. return WPopupMenu(
  193. child: chat.isMe ? _buildMyMsg(chat) : _buildRobotMsg(chat),
  194. actions: actions,
  195. menuWidth: 200,
  196. onValueChanged: (int value) {
  197. print('选择的是$value个菜单');
  198. if (value >= 0 && value < actionsFunc.length) {
  199. actionsFunc[value]();
  200. }
  201. },
  202. );
  203. }
  204. Widget _buildMyMsg(RobotChatModel msg) {
  205. bool hasHeadImg = true;
  206. if (UserData().basicInfo.headimgurl == null ||
  207. UserData().basicInfo.headimgurl.length == 0) {
  208. hasHeadImg = false;
  209. }
  210. return Container(
  211. width: Screen.width,
  212. margin: const EdgeInsets.symmetric(vertical: 10.0),
  213. child: Row(
  214. crossAxisAlignment: CrossAxisAlignment.start,
  215. mainAxisAlignment: MainAxisAlignment.end,
  216. children: <Widget>[
  217. msg.type == 0 ? _textMsg(msg) : _soundMsg(msg),
  218. SizedBox(width: 10),
  219. Column(
  220. crossAxisAlignment: CrossAxisAlignment.end,
  221. children: <Widget>[
  222. ClipRRect(
  223. borderRadius: BorderRadius.circular(8),
  224. child: hasHeadImg
  225. ? CachedNetworkImage(
  226. imageUrl: UserData().basicInfo.headimgurl,
  227. width: 40,
  228. height: 40,
  229. )
  230. : SizedBox(
  231. width: 40,
  232. height: 40,
  233. child:
  234. Image.asset(R.assetsImagesDefaultNorAvatar))),
  235. ],
  236. )
  237. ]));
  238. }
  239. Widget _soundMsg(RobotChatModel msg) {
  240. double time = msg.extraInfo / 1000;
  241. if (time > 60) {
  242. time = 60.0;
  243. }
  244. bool isPlaying = false;
  245. var soundPath = msg.msgContent;
  246. isPlaying = SoundUtils().isPlaying(soundPath);
  247. return GestureDetector(
  248. child: Container(
  249. width: 120,
  250. child: Row(
  251. mainAxisAlignment: MainAxisAlignment.end,
  252. children: <Widget>[
  253. Container(
  254. alignment: Alignment.center,
  255. padding: EdgeInsets.only(bottom: 2),
  256. margin: EdgeInsets.only(right: 10),
  257. width: 25.5,
  258. height: 25.5,
  259. decoration: BoxDecoration(
  260. border:
  261. Border.all(color: const Color(0xFF1B92C7), width: 0.5),
  262. color: const Color(0xFF04A4FE),
  263. shape: BoxShape.circle),
  264. child: Icon(
  265. IconData(isPlaying ? 0xe652 : 0xe653,
  266. fontFamily: Constants.IconFontFamily),
  267. size: isPlaying ? 15 : 18,
  268. color: Colors.white,
  269. ),
  270. ),
  271. isPlaying
  272. ? Stack(
  273. children: <Widget>[
  274. Container(
  275. height: 18,
  276. width: 19,
  277. ),
  278. Positioned(
  279. bottom: 0,
  280. child: VideoAnim(
  281. begin: 18,
  282. start: 0.444,
  283. end: 4.5,
  284. )),
  285. Positioned(
  286. left: 7,
  287. bottom: 0,
  288. child: VideoAnim(
  289. begin: 4.5,
  290. end: 18,
  291. )),
  292. Positioned(
  293. left: 14,
  294. bottom: 0,
  295. child: VideoAnim(
  296. begin: 18,
  297. end: 4.5,
  298. ))
  299. ],
  300. )
  301. : Stack(
  302. children: <Widget>[
  303. Container(
  304. height: 18,
  305. width: 19,
  306. ),
  307. Positioned(
  308. bottom: 0, child: CustomUI.buildAudioContaniner(12)),
  309. Positioned(
  310. left: 7,
  311. bottom: 0,
  312. child: CustomUI.buildAudioContaniner(4.5)),
  313. Positioned(
  314. left: 14,
  315. bottom: 0,
  316. child: CustomUI.buildAudioContaniner(18))
  317. ],
  318. ),
  319. Expanded(child: SizedBox()),
  320. fixedText(time.toStringAsFixed(0),
  321. color: Constants.BlackTextColor, fontSize: 16)
  322. ],
  323. ),
  324. padding: EdgeInsets.symmetric(horizontal: 15, vertical: 12),
  325. decoration: BoxDecoration(
  326. color: SendMsgBg,
  327. border: Border.all(color: Color(0xFFB9CBD7), width: 0.6),
  328. borderRadius: BorderRadius.all(Radius.circular(8))),
  329. ),
  330. onTap: () async {
  331. print('播放状态 : $isPlaying');
  332. if (isPlaying) {
  333. SoundUtils().pause();
  334. } else {
  335. SoundUtils().play(soundPath, onPlayed: () {
  336. if (mounted) {
  337. setState(() {});
  338. }
  339. }, complete: () {
  340. if (mounted) {
  341. setState(() {});
  342. }
  343. });
  344. }
  345. },
  346. );
  347. }
  348. Widget _textMsg(RobotChatModel msg) {
  349. return Container(
  350. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  351. child: fixedText(msg.msgContent, fontSize: 15, color: Colors.black),
  352. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  353. decoration: BoxDecoration(
  354. color: SendMsgBg,
  355. border: Border.all(color: Color(0xFFB9CBD7), width: 0.6),
  356. borderRadius: BorderRadius.all(Radius.circular(8))),
  357. );
  358. }
  359. Widget _buildRobotMsg(RobotChatModel msg) {
  360. return Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
  361. Container(
  362. margin: const EdgeInsets.only(right: 8.0),
  363. child: ClipRRect(
  364. borderRadius: BorderRadius.circular(8),
  365. child: SizedBox(
  366. width: 40,
  367. height: 40,
  368. child: Image.asset(R.assetsImagesRobot))),
  369. ),
  370. ///todo
  371. Container(
  372. constraints: BoxConstraints(maxWidth: Screen.width - 120),
  373. padding: EdgeInsets.symmetric(horizontal: 9, vertical: 10.5),
  374. child: fixedText(msg.msgContent),
  375. decoration: BoxDecoration(
  376. border: Border.all(color: Color(0xFFDCDCDC), width: 0.5),
  377. color: Colors.white,
  378. borderRadius: BorderRadius.all(Radius.circular(8))),
  379. )
  380. ]);
  381. }
  382. //输入框
  383. _inputBar() {
  384. if (curKeyboardIndex != 0) {
  385. if (editFocus.hasFocus) {
  386. editFocus.unfocus();
  387. }
  388. } else {
  389. if (!editFocus.hasFocus) {
  390. FocusScope.of(context).requestFocus(editFocus);
  391. }
  392. }
  393. return GestureDetector(
  394. onTap: () {
  395. print('other keyboard');
  396. },
  397. child: Container(
  398. width: Screen.width,
  399. color: Colors.white,
  400. child: Column(
  401. children: <Widget>[
  402. Container(
  403. padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
  404. alignment: Alignment.topCenter,
  405. decoration: BoxDecoration(
  406. color: Colors.white,
  407. border:
  408. Border(top: BorderSide(color: Color(0xFFDFDFDF)))),
  409. child: Row(
  410. children: <Widget>[
  411. //输入框
  412. Flexible(
  413. child: Container(
  414. child: TextField(
  415. keyboardAppearance: Brightness.light,
  416. onChanged: (String messageText) {
  417. setState(() {
  418. _isComposingMessage = _textCtrl.text.length > 0;
  419. });
  420. },
  421. autofocus: false,
  422. style: TextStyle(
  423. fontSize: ScreenUtil().setSp(16),
  424. textBaseline: TextBaseline.alphabetic),
  425. maxLines: 3,
  426. minLines: 1,
  427. controller: _textCtrl,
  428. textInputAction: TextInputAction.newline,
  429. inputFormatters: <TextInputFormatter>[
  430. LengthLimitingTextInputFormatter(600) //限制长度
  431. ],
  432. onSubmitted: _textMessageSubmitted,
  433. focusNode: editFocus,
  434. onTap: () {
  435. setState(() {
  436. curKeyboardIndex = 0;
  437. });
  438. },
  439. decoration: InputDecoration(
  440. hintText: I18n.of(context).input_content,
  441. hintStyle: TextStyle(
  442. color: const Color(0xffBDBDBD), fontSize: 16),
  443. border: null,
  444. contentPadding: EdgeInsets.only(
  445. left: 5, right: 5, top: 8, bottom: 8),
  446. ),
  447. ),
  448. ),
  449. ),
  450. SizedBox(width: 10),
  451. _isComposingMessage
  452. ? InkWell(
  453. child: Padding(
  454. padding: EdgeInsets.symmetric(horizontal: 10),
  455. child: Icon(
  456. Icons.send,
  457. color: Color(0xFF087FF3),
  458. size: 28,
  459. )),
  460. onTap: _sendTextMessage)
  461. : InkWell(
  462. child: Padding(
  463. padding: EdgeInsets.symmetric(horizontal: 10),
  464. child: Icon(
  465. IconData(0xe64f,
  466. fontFamily: Constants.IconFontFamily),
  467. color: curKeyboardIndex == 1
  468. ? Color(0xFF0A80F3)
  469. : Color(0xFF797A7C),
  470. size: 28,
  471. )),
  472. onTap: () {
  473. if (curKeyboardIndex == 1) {
  474. setState(() {
  475. curKeyboardIndex = 0;
  476. });
  477. } else {
  478. setState(() {
  479. curKeyboardIndex = 1;
  480. });
  481. }
  482. })
  483. ],
  484. ),
  485. ),
  486. Divider(height: 1, color: const Color(0xffE0E0E0)),
  487. StreamBuilder(
  488. stream: _bloc.stream,
  489. builder: (BuildContext context, AsyncSnapshot snapshot) {
  490. double keyHeight = MediaQuery.of(context).viewInsets.bottom;
  491. if (keyHeight > 10) {
  492. keyboardHeight = keyHeight;
  493. UserData().setKeyboardHeight(keyHeight);
  494. }
  495. return Container(
  496. width: double.infinity,
  497. color: Colors.white,
  498. height: curKeyboardIndex == 0 || curKeyboardIndex == 1
  499. ? keyboardHeight
  500. : 0,
  501. child: RecordView(
  502. keyboardHeight: keyboardHeight,
  503. sendMsg: _sendSoundMsg));
  504. },
  505. )
  506. ],
  507. )));
  508. }
  509. showKeyBoard() {
  510. setState(() {
  511. curKeyboardIndex = 0;
  512. });
  513. editFocus.requestFocus();
  514. //SystemChannels.textInput.invokeMethod('TextInput.show');
  515. }
  516. _sendSoundMsg(String soundPath, int duration) {
  517. print('上传文件');
  518. RobotChatModel model = RobotChatModel(
  519. msgContent: soundPath, extraInfo: duration, isMe: true, type: 1);
  520. RobotChatMgr().addSource(model);
  521. setState(() {});
  522. UploadUtil()
  523. .commitTranslateSource(2, curSourceLang, curToLang, soundPath)
  524. .then((tranStr) {
  525. if (tranStr == null) {
  526. showToast(I18n.of(context).translate_fail);
  527. return;
  528. }
  529. RobotChatModel model = RobotChatModel(msgContent: tranStr, isMe: false);
  530. RobotChatMgr().addSource(model);
  531. if (mounted) {
  532. setState(() {});
  533. }
  534. });
  535. }
  536. _sendTextMessage() {
  537. if (!checkMessage()) {
  538. return;
  539. }
  540. var sendStr = _textCtrl.text;
  541. RobotChatModel model = RobotChatModel(msgContent: sendStr, isMe: true);
  542. RobotChatMgr().addSource(model);
  543. setState(() {});
  544. UploadUtil()
  545. .commitTranslateSource(1, curSourceLang, curToLang, sendStr)
  546. .then((tranStr) {
  547. if (tranStr == null) {
  548. showToast(I18n.of(context).translate_fail);
  549. return;
  550. }
  551. RobotChatModel model = RobotChatModel(msgContent: tranStr, isMe: false);
  552. RobotChatMgr().addSource(model);
  553. if (mounted) {
  554. setState(() {});
  555. }
  556. });
  557. _textCtrl.clear();
  558. setState(() {
  559. _isComposingMessage = false;
  560. });
  561. }
  562. bool checkMessage() {
  563. if (_textCtrl.text.length == 0) {
  564. showToast(I18n.of(context).msg_not);
  565. return false;
  566. }
  567. return true;
  568. }
  569. Future<Null> _textMessageSubmitted(String text) async {
  570. _sendTextMessage();
  571. }
  572. }