Hibok
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

936 linhas
33 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'dart:typed_data';
  4. import 'package:chat/chat/keyboard_icon.dart';
  5. import 'package:chat/chat/record_view.dart';
  6. import 'package:chat/chat/util_keyboard.dart';
  7. import 'package:chat/data/UserData.dart';
  8. import 'package:chat/data/constants.dart';
  9. import 'package:chat/generated/i18n.dart';
  10. import 'package:chat/home/alter_select_view.dart';
  11. import 'package:chat/models/ChatMsg.dart';
  12. import 'package:chat/models/group_info_model.dart';
  13. import 'package:chat/models/keyboard_provider.dart';
  14. import 'package:chat/proto/all.pbserver.dart';
  15. import 'package:chat/utils/CustomUI.dart';
  16. import 'package:chat/utils/MessageMgr.dart';
  17. import 'package:chat/utils/file_cache_mgr.dart';
  18. import 'package:chat/utils/group_member_model.dart';
  19. import 'package:chat/utils/image_util.dart';
  20. import 'package:chat/utils/keyboard_utils.dart';
  21. import 'package:chat/utils/msgHandler.dart';
  22. import 'package:chat/utils/screen.dart';
  23. import 'package:chat/utils/sound_util.dart';
  24. import 'package:flutter/material.dart';
  25. import 'package:flutter/services.dart';
  26. import 'package:flutter_screenutil/flutter_screenutil.dart';
  27. import 'package:multi_image_picker/multi_image_picker.dart';
  28. import 'package:oktoast/oktoast.dart';
  29. import 'package:provider/provider.dart';
  30. import 'package:shared_preferences/shared_preferences.dart';
  31. import 'emoji_gif_text.dart';
  32. import 'emoji_text.dart';
  33. class InputBar extends StatefulWidget {
  34. final Function sendMsg;
  35. final bool isTranslateHK;
  36. InputBar({this.sendMsg, this.isTranslateHK = false});
  37. @override
  38. InputBarState createState() => InputBarState();
  39. }
  40. class InputBarState extends State<InputBar>
  41. with SingleTickerProviderStateMixin {
  42. final TextEditingController _textCtrl = TextEditingController();
  43. FocusNode editFocus = NoKeyboardEditableTextFocusNode();
  44. bool _isComposingMessage = false;
  45. PageController pageController;
  46. double keyboardHeight;
  47. KeyboardBloc _bloc = KeyboardBloc();
  48. GlobalKey _key = GlobalKey();
  49. int currentStickersPage = 0;
  50. //回复相关
  51. MsgModel refMsg;
  52. //@成员数组
  53. List<GroupMemberModel> alterMemberList = [];
  54. int lastTxtLen = 0;
  55. @override
  56. void initState() {
  57. super.initState();
  58. print('~~~~~~~~~~inputbar initState~~~~~~~~~~~');
  59. pageController = new PageController();
  60. getKeyboardHeight();
  61. _bloc.start();
  62. //处理引用事件
  63. MessageMgr().on('Reply Select Message', replySelectMsg);
  64. //处理@事件
  65. MessageMgr().on('Alter User Message', alterOtherMember);
  66. }
  67. @override
  68. void didChangeDependencies() {
  69. super.didChangeDependencies();
  70. editFocus?.requestFocus();
  71. }
  72. //获取键盘高度,之前保存的
  73. getKeyboardHeight() async {
  74. var sp = await SharedPreferences.getInstance();
  75. keyboardHeight = sp.getDouble(Constants.KeyboardHeight);
  76. if (keyboardHeight == null || keyboardHeight < 100) {
  77. keyboardHeight = 280;
  78. }
  79. }
  80. @override
  81. void dispose() {
  82. _key = null;
  83. _textCtrl.dispose();
  84. _bloc.dispose();
  85. editFocus.dispose();
  86. editFocus = null;
  87. MessageMgr().off('Reply Select Message', replySelectMsg);
  88. MessageMgr().off('Alter User Message', alterOtherMember);
  89. super.dispose();
  90. }
  91. //引用某句话
  92. replySelectMsg(args) async {
  93. refMsg = args;
  94. print('处理引用消息');
  95. setState(() {
  96. showKeyBoard();
  97. });
  98. }
  99. //快捷@某成员
  100. alterOtherMember(memberInfo) {
  101. if (!alterMemberList.contains(memberInfo)) {
  102. alterMemberList.add(memberInfo);
  103. }
  104. print('选中的成员~~~~~~~~~~~~ ${memberInfo.refName}');
  105. String textStr;
  106. if (_textCtrl.text.length > 0) {
  107. textStr = '${_textCtrl.text} @${memberInfo.refName} ';
  108. } else {
  109. textStr = '@${memberInfo.refName} ';
  110. }
  111. _textCtrl.value = TextEditingValue(
  112. text: textStr,
  113. selection:
  114. TextSelection.fromPosition(TextPosition(offset: textStr.length)));
  115. setState(() {
  116. showKeyBoard();
  117. _isComposingMessage = _textCtrl.text.length > 0;
  118. });
  119. }
  120. //显示键盘
  121. showKeyBoard() {
  122. Provider.of<KeyboardIndexProvider>(context, listen: false)
  123. .changeSelectIndex(0);
  124. editFocus.requestFocus();
  125. Provider.of<KeyboardIndexProvider>(context, listen: false)
  126. .changeReadOnlyKey(false);
  127. // SystemChannels.textInput.invokeMethod('TextInput.show');
  128. }
  129. _getRefShortText(){
  130. if (refMsg == null) {
  131. return null;
  132. }
  133. String desc = '';
  134. if (refMsg == null || refMsg.msgType == null) {
  135. return '';
  136. }
  137. switch (ChatType.valueOf(refMsg.msgType)) {
  138. case ChatType.TextChatType:
  139. desc = utf8.decode(refMsg.msgContent);
  140. break;
  141. case ChatType.EmoticonType:
  142. desc = '[${I18n.of(context).emoji}]';
  143. break;
  144. case ChatType.ImageChatType:
  145. desc = '[${I18n.of(context).picture}]';
  146. break;
  147. case ChatType.ShortVideoChatType:
  148. desc = '[${I18n.of(context).video}]';
  149. break;
  150. case ChatType.PlaceChatType:
  151. desc = '[${I18n.of(context).locate}]';
  152. break;
  153. case ChatType.ShortVoiceChatType:
  154. desc = '[${I18n.of(context).voice}]';
  155. break;
  156. case ChatType.GiftChatType:
  157. GiftChat giftChat = GiftChat.fromBuffer(refMsg.msgContent);
  158. if (giftChat.tuId == UserData().basicInfo.userId) {
  159. desc = I18n.of(context).you_get;
  160. } else {
  161. desc = I18n.of(context).you_give;
  162. }
  163. break;
  164. case ChatType.RedWalletChatType:
  165. desc = '[${I18n.of(context).red_money}]';
  166. break;
  167. case ChatType.GroupChatNoticeType:
  168. desc = '[${I18n.of(context).msg_notice}]';
  169. break;
  170. case ChatType.FileChatType:
  171. desc = '[${I18n.of(context).file}]';
  172. break;
  173. default:
  174. }
  175. if (refMsg.channelType == ChatChannelType.Group.value) {
  176. GroupInfoModel groupInfoModel = Provider.of<GroupInfoModel>(context);
  177. var member = groupInfoModel.getMember(refMsg.from);
  178. return '${member.refName}: $desc';
  179. //}
  180. //else if (refMsg.channelType == ChatChannelType.Session.value) {
  181. // UserInfo info = await HttpUtil().getFriendInfo(refMsg.from);
  182. // var refName = Provider.of<RefNameProvider>(context)
  183. // .getRefName(info.userId, info.nickName);
  184. // return '$refName: $desc';
  185. } else {
  186. return desc;
  187. }
  188. }
  189. bool isReceiver = false;
  190. @override
  191. Widget build(BuildContext context) {
  192. int curKeyboardIndex =
  193. Provider.of<KeyboardIndexProvider>(context).curKeyboardIndex;
  194. bool readOnly =
  195. Provider.of<KeyboardIndexProvider>(context, listen: false).readOnly;
  196. bool isGroup = Provider.of<bool>(context);
  197. Widget centerWidget;
  198. Widget input = Container(
  199. child: TextField(
  200. // textSelectionControls: ExtendedMaterialTextSelectionControls(),
  201. keyboardAppearance: Brightness.light,
  202. onChanged: (String messageText) {
  203. if (_textCtrl.text.length > 0) {
  204. var last = messageText.substring(_textCtrl.text.length - 1);
  205. if (last == '@' && _textCtrl.text.length > lastTxtLen) {
  206. editFocus.unfocus();
  207. _openAlterSelectPage();
  208. }
  209. }
  210. lastTxtLen = _textCtrl.text.length;
  211. setState(() {
  212. _isComposingMessage = _textCtrl.text.length > 0;
  213. });
  214. },
  215. key: _key,
  216. readOnly: readOnly,
  217. autofocus: true,
  218. cursorColor: Constants.BlueTextColor,
  219. style: TextStyle(
  220. fontSize: ScreenUtil().setSp(16),
  221. textBaseline: TextBaseline.alphabetic),
  222. maxLines: 3,
  223. minLines: 1,
  224. // specialTextSpanBuilder: MySpecialTextSpanBuilder(
  225. // showAtBackground: true,
  226. // ),
  227. controller: _textCtrl,
  228. textInputAction: TextInputAction.newline,
  229. inputFormatters: <TextInputFormatter>[
  230. LengthLimitingTextInputFormatter(600) //限制长度
  231. ],
  232. onSubmitted: _textMessageSubmitted,
  233. focusNode: editFocus,
  234. onTap: () {
  235. showKeyBoard();
  236. Provider.of<KeyboardIndexProvider>(context, listen: false)
  237. .changeReadOnlyKey(false);
  238. },
  239. decoration: InputDecoration(
  240. hintText: I18n.of(context).input_content,
  241. hintStyle: TextStyle(color: const Color(0xffBDBDBD), fontSize: 16),
  242. border: null,
  243. hintMaxLines: 1,
  244. contentPadding: EdgeInsets.only(left: 5, right: 5, top: 8, bottom: 8),
  245. ),
  246. ),
  247. );
  248. if (refMsg == null) {
  249. centerWidget = input;
  250. } else {
  251. var desc = _getRefShortText();
  252. centerWidget = Column(
  253. crossAxisAlignment: CrossAxisAlignment.start,
  254. children: <Widget>[
  255. Container(
  256. child: Row(mainAxisSize: MainAxisSize.min, children: [
  257. Container(
  258. child: Text(
  259. desc,
  260. maxLines: 1,
  261. overflow: TextOverflow.ellipsis,
  262. textScaleFactor: 1.0,
  263. ),
  264. constraints: BoxConstraints(maxWidth: Screen.width - 220),
  265. ),
  266. InkWell(
  267. child: Container(
  268. child: Icon(Icons.close, size: 10),
  269. padding: EdgeInsets.fromLTRB(10, 5, 5, 5),
  270. ),
  271. onTap: () {
  272. refMsg = null;
  273. setState(() {});
  274. },
  275. )
  276. ]),
  277. padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
  278. decoration: BoxDecoration(
  279. color: Colors.grey[200],
  280. border: Border.all(color: Colors.grey),
  281. borderRadius: BorderRadius.circular(10))),
  282. input
  283. ],
  284. );
  285. }
  286. return GestureDetector(
  287. onTap: () {
  288. print('放置触控隐藏键盘');
  289. },
  290. child: Container(
  291. width: Screen.width,
  292. color: Colors.white,
  293. child: Column(
  294. children: <Widget>[
  295. Container(
  296. padding: EdgeInsets.symmetric(horizontal: 7, vertical: 7),
  297. alignment: Alignment.topCenter,
  298. decoration: BoxDecoration(
  299. color: Colors.white,
  300. border: Border(top: BorderSide(color: Color(0xFFDFDFDF)))),
  301. child: Row(
  302. children: <Widget>[
  303. //emoji图标
  304. KeyboardIcon(
  305. iconCode: 0xe64d,
  306. selectIconCode: 0xe651,
  307. isSelect: curKeyboardIndex == 1,
  308. size: 27,
  309. onTap: () {
  310. isReceiver = !isReceiver;
  311. SoundUtils.instance.setReceiver(isReceiver);
  312. //
  313. if (curKeyboardIndex == 1) {
  314. Provider.of<KeyboardIndexProvider>(context,
  315. listen: false)
  316. .changeReadOnlyKey(false);
  317. showKeyBoard();
  318. } else {
  319. Provider.of<KeyboardIndexProvider>(context,
  320. listen: false)
  321. .changeSelectIndex(1);
  322. currentStickersPage = 0;
  323. }
  324. }),
  325. SizedBox(width: 10),
  326. //输入框
  327. Expanded(child: centerWidget), SizedBox(width: 10),
  328. _isComposingMessage
  329. ? InkWell(
  330. child: Padding(
  331. padding: EdgeInsets.fromLTRB(50, 2, 10, 2),
  332. child: Icon(
  333. Icons.send,
  334. color: Color(0xFF087FF3),
  335. size: 28,
  336. )),
  337. onTap: _sendTextMessage)
  338. : Container(
  339. child: Row(
  340. children: <Widget>[
  341. KeyboardIcon(
  342. iconCode: 0xe64b,
  343. isSelect: curKeyboardIndex == 2,
  344. size: 28,
  345. onTap: () {
  346. if (curKeyboardIndex == 2) {
  347. showKeyBoard();
  348. } else {
  349. Provider.of<KeyboardIndexProvider>(
  350. context,
  351. listen: false)
  352. .changeSelectIndex(2);
  353. }
  354. }),
  355. SizedBox(width: 10),
  356. KeyboardIcon(
  357. iconCode: 0xe64f,
  358. isSelect: curKeyboardIndex == 3,
  359. size: 28,
  360. onTap: () {
  361. if (curKeyboardIndex == 3) {
  362. showKeyBoard();
  363. } else {
  364. Provider.of<KeyboardIndexProvider>(
  365. context,
  366. listen: false)
  367. .changeSelectIndex(3);
  368. }
  369. }),
  370. SizedBox(width: 10),
  371. KeyboardIcon(
  372. iconCode: 0xe60c,
  373. isSelect: curKeyboardIndex == 4,
  374. size: 26,
  375. onTap: () {
  376. _openPhotoView();
  377. }),
  378. ],
  379. ),
  380. )
  381. ],
  382. ),
  383. ),
  384. Divider(height: 1, color: const Color(0xffE0E0E0)),
  385. StreamBuilder(
  386. stream: _bloc.stream,
  387. builder: (BuildContext context, AsyncSnapshot snapshot) {
  388. double keyHeight = MediaQuery.of(context).viewInsets.bottom;
  389. if (keyHeight > 10) {
  390. keyboardHeight = keyHeight;
  391. UserData().setKeyboardHeight(keyHeight);
  392. }
  393. return Container(
  394. width: double.infinity,
  395. color: Colors.white,
  396. height: curKeyboardIndex == -1 || curKeyboardIndex == 4
  397. ? 0
  398. : keyboardHeight,
  399. child: curKeyboardIndex >= 1 && curKeyboardIndex < 4
  400. ? IndexedStack(
  401. children: <Widget>[
  402. Container(
  403. color: Colors.white,
  404. width: double.infinity,
  405. height: double.infinity,
  406. child: InkWell(
  407. onTap: () {},
  408. child: Container(
  409. height: keyboardHeight,
  410. child: Column(
  411. children: <Widget>[
  412. Container(
  413. height: keyboardHeight - 50,
  414. child: PageView.custom(
  415. controller: pageController,
  416. onPageChanged: (page) {
  417. setState(() {
  418. currentStickersPage =
  419. page;
  420. });
  421. },
  422. childrenDelegate:
  423. new SliverChildBuilderDelegate(
  424. (context, index) {
  425. return index == 0
  426. ? buildEmojiGird()
  427. : buildGifGird();
  428. },
  429. childCount: 2,
  430. )),
  431. ),
  432. Divider(
  433. height: 1,
  434. color: Color(0xffE0E0E0),
  435. ),
  436. Container(
  437. height: 49,
  438. child: Row(
  439. children: <Widget>[
  440. InkWell(
  441. child: Container(
  442. width: 59,
  443. padding:
  444. EdgeInsets.all(10),
  445. color:
  446. currentStickersPage ==
  447. 0
  448. ? const Color(
  449. 0xFFEDEDED)
  450. : Colors.white,
  451. child: Image.asset(
  452. 'assets/images/chat/emoji.png'),
  453. ),
  454. onTap: () {
  455. pageController
  456. .jumpToPage(0);
  457. },
  458. ),
  459. InkWell(
  460. child: Container(
  461. padding:
  462. EdgeInsets.all(8),
  463. width: 59,
  464. color:
  465. currentStickersPage ==
  466. 1
  467. ? const Color(
  468. 0xFFEDEDED)
  469. : Colors.white,
  470. child: Image.asset(
  471. 'assets/images/chat/onion.png'),
  472. ),
  473. onTap: () {
  474. pageController
  475. .jumpToPage(1);
  476. },
  477. )
  478. ],
  479. ),
  480. ),
  481. ],
  482. ),
  483. ),
  484. )),
  485. UtilKeyboard(
  486. keyboardHeight: keyboardHeight,
  487. isGroup: isGroup,
  488. sendMsg: widget.sendMsg),
  489. RecordView(
  490. keyboardHeight: keyboardHeight,
  491. sendMsg: _sendSoundMsg),
  492. ],
  493. index: curKeyboardIndex - 1,
  494. )
  495. : Container());
  496. },
  497. )
  498. ],
  499. )),
  500. );
  501. }
  502. Widget buildEmojiGird() {
  503. return Container(
  504. height: keyboardHeight,
  505. child: Stack(
  506. children: <Widget>[
  507. Padding(
  508. padding: EdgeInsets.fromLTRB(5, 8, 50, 0),
  509. child: GridView.builder(
  510. controller: new ScrollController(keepScrollOffset: false),
  511. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  512. crossAxisCount: 7, crossAxisSpacing: 0, mainAxisSpacing: 0),
  513. itemBuilder: (context, index) {
  514. return GestureDetector(
  515. child: Container(
  516. padding: EdgeInsets.all(7),
  517. child: Image.asset(EmojiUitl
  518. .instance.emojiMap[EmojiText.flag + "${index + 1}]"]),
  519. ),
  520. behavior: HitTestBehavior.translucent,
  521. onTap: () {
  522. insertText(EmojiText.flag + "${index + 1}]", _textCtrl);
  523. setState(() {
  524. _isComposingMessage = _textCtrl.text.length > 0;
  525. });
  526. },
  527. );
  528. },
  529. itemCount: EmojiUitl.instance.emojiMap.length,
  530. padding: EdgeInsets.all(5.0),
  531. ),
  532. ),
  533. Positioned.fill(
  534. child: Container(
  535. padding: EdgeInsets.fromLTRB(0, 0, 0, 14),
  536. alignment: Alignment(1, 1),
  537. child: InkWell(
  538. onTap: () {
  539. deleteText(_textCtrl);
  540. setState(() {
  541. _isComposingMessage = _textCtrl.text.length > 0;
  542. });
  543. },
  544. child: Container(
  545. decoration: BoxDecoration(
  546. color: Color(0x4d0E0E10),
  547. borderRadius: BorderRadius.only(
  548. topLeft: Radius.circular(4.0),
  549. bottomLeft: Radius.circular(4))),
  550. width: 37,
  551. height: 28,
  552. child: Icon(
  553. IconData(
  554. 0xe679,
  555. fontFamily: 'iconfont',
  556. ),
  557. size: 16,
  558. color: Colors.white,
  559. ),
  560. ),
  561. ),
  562. )),
  563. ],
  564. ),
  565. );
  566. }
  567. Widget buildGifGird() {
  568. return Container(
  569. padding: EdgeInsets.fromLTRB(8, 6, 8, 0),
  570. height: keyboardHeight,
  571. child: GridView.builder(
  572. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  573. crossAxisCount: 5, crossAxisSpacing: 12.0, mainAxisSpacing: 12.0),
  574. itemBuilder: (context, index) {
  575. return GestureDetector(
  576. child: Image.asset(EmojiGifUitl
  577. .instance.emojiMap[EmojiGifText.flag + "_${index + 1}>"]),
  578. behavior: HitTestBehavior.translucent,
  579. onTap: () {
  580. _sendMessage(EmojiGifText.flag + "_${index + 1}>", true);
  581. },
  582. );
  583. },
  584. itemCount: EmojiGifUitl.instance.emojiMap.length,
  585. padding: EdgeInsets.all(5.0),
  586. ),
  587. );
  588. }
  589. void _openAlterSelectPage() async {
  590. bool isGroup = Provider.of<bool>(context);
  591. if (isGroup) {
  592. GroupInfoModel groupInfoModel = Provider.of<GroupInfoModel>(context);
  593. editFocus.unfocus();
  594. Provider.of<KeyboardIndexProvider>(context, listen: false)
  595. .changeReadOnlyKey(true);
  596. GroupMemberModel member =
  597. await AlterSelectPage.pickAlterUser(context, groupInfoModel);
  598. if (member != null) {
  599. print('选中的成员~~~~~~~~~~~~ ${member.refName}');
  600. _textCtrl.text = '${_textCtrl.text}${member.refName} ';
  601. alterMemberList.add(member);
  602. }
  603. editFocus.requestFocus();
  604. Provider.of<KeyboardIndexProvider>(context, listen: false)
  605. .changeReadOnlyKey(false);
  606. }
  607. }
  608. List<int> getAlterUsers(String messageText) {
  609. List<String> alterUserList = [];
  610. RegExp alterStr = RegExp(r'@+(\S+)');
  611. Iterable<Match> matches = alterStr.allMatches(messageText);
  612. print('~~~~~~~~~~~~~~${matches.length}~~~~~~~~~~~~~~~');
  613. for (Match m in matches) {
  614. print('~~~~~~~~~~~~~~${m.group(1)}~~~~~~~~~~~~~~~');
  615. alterUserList.add(m.group(1));
  616. }
  617. List<int> finalAlterList = [];
  618. for (var member in alterMemberList) {
  619. if (alterUserList.contains(member.refName)) {
  620. finalAlterList.add(member.memberId);
  621. print('有效成员 ${member.refName}');
  622. }
  623. }
  624. return finalAlterList;
  625. }
  626. void _openPhotoView() async {
  627. // Provider.of<KeyboardIndexProvider>(context, listen: false)
  628. // .changeSelectIndex(4);
  629. List<Asset> resultList = List<Asset>();
  630. try {
  631. resultList = await MultiImagePicker.pickImages(
  632. maxImages: 9,
  633. enableCamera: false,
  634. selectedAssets: [],
  635. cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
  636. materialOptions: MaterialOptions(
  637. actionBarColor: "#50A7F9",
  638. actionBarTitle: "Hibok",
  639. allViewTitle: "",
  640. useDetailsView: false,
  641. selectCircleStrokeColor: "#000000",
  642. ),
  643. );
  644. if (resultList != null && resultList.length > 0) {
  645. for (var i = 0; i < resultList.length; i++) {
  646. Asset photoEntity = resultList[i];
  647. ByteData byteData = await photoEntity.getByteData();
  648. File file = await FileCacheMgr().writeFile(
  649. 'temp-photo-${DateTime.now().millisecondsSinceEpoch}-${photoEntity.name}',
  650. byteData.buffer.asInt8List(0));
  651. _sendPhotoFile(file);
  652. }
  653. }
  654. } on Exception catch (e) {
  655. print(e.toString());
  656. }
  657. // var photos = await PhotoPicker.pickAsset(
  658. // context: context,
  659. // themeColor: Color(0xFFF0F0F0),
  660. // textColor: Color(0xFF3F3F3F),
  661. // pickType: PickType.onlyImage);
  662. //
  663. // if (photos != null && photos.length > 0) {
  664. // for (var i = 0; i < photos.length; i++) {
  665. // AssetEntity photoEntity = photos[i];
  666. // var file = await photoEntity.file;
  667. // _sendPhotoFile(file);
  668. // }
  669. // }
  670. ///防止再点键盘不谈
  671. }
  672. void _sendSoundMsg(String soundPath, int duration) {
  673. bool isGroup = Provider.of<bool>(context);
  674. int friendId = 0;
  675. if (!isGroup) {
  676. friendId = Provider.of<int>(context);
  677. }
  678. print('群聊模式 $isGroup');
  679. var msg = MsgHandler.createSendMsg(
  680. ChatType.ShortVoiceChatType, Uint8List(0),
  681. localFile: soundPath,
  682. friendId: friendId,
  683. extra: duration,
  684. channelType: isGroup ? ChatChannelType.Group : ChatChannelType.Session);
  685. widget.sendMsg(msg);
  686. }
  687. void _sendPhotoFile(File imgFile) async {
  688. var imgSize = await imgFile.length();
  689. print('图片大小:${imgSize / 1024}KB');
  690. var sendImg;
  691. if (imgSize > 33 * 1024 * 1024) {
  692. showToast(I18n.of(context).video_more_big);
  693. return;
  694. }
  695. bool isNeedUpload = false;
  696. if (imgSize > ImgSizeLimit) {
  697. print('图片大于 $ImgSizeLimit,压缩');
  698. //发送压缩图 WidgetUtil.getCompressImg(path,quality: 80,percentage: 80);
  699. sendImg = await WidgetUtil.getCompressImg(imgFile.absolute.path);
  700. isNeedUpload = true;
  701. } else {
  702. sendImg = imgFile.readAsBytesSync();
  703. }
  704. var rect = await WidgetUtil.getImageWH(
  705. image: Image.memory(Uint8List.fromList(sendImg)));
  706. print('图片大小 Rect : $rect');
  707. int aspectRatio = rect.width * 100 ~/ rect.height;
  708. bool isGroup = Provider.of<bool>(context);
  709. print('群聊输入模式 $isGroup');
  710. int friendId = 0;
  711. if (!isGroup) {
  712. friendId = Provider.of<int>(context);
  713. }
  714. var msg = MsgHandler.createSendMsg(ChatType.ImageChatType, sendImg,
  715. localFile: isNeedUpload ? imgFile.absolute.path : null,
  716. extra: aspectRatio,
  717. friendId: friendId,
  718. channelType: isGroup ? ChatChannelType.Group : ChatChannelType.Session);
  719. widget.sendMsg(msg);
  720. }
  721. Future<Null> _textMessageSubmitted(String text) async {
  722. if (!checkMessage()) {
  723. _textCtrl.clear();
  724. alterMemberList.clear();
  725. lastTxtLen = 0;
  726. return;
  727. }
  728. _textCtrl.clear();
  729. _sendMessage(text);
  730. }
  731. _sendTextMessage() {
  732. if (!checkMessage()) {
  733. return;
  734. }
  735. var sendStr = _textCtrl.text;
  736. _textCtrl.clear();
  737. _sendMessage(sendStr);
  738. }
  739. bool checkMessage() {
  740. if (_textCtrl.text.length == 0) {
  741. showToast(I18n.of(context).msg_not);
  742. return false;
  743. }
  744. return true;
  745. }
  746. _sendMessage(String messageText, [bool isGift = false]) {
  747. bool isGroup = Provider.of<bool>(context);
  748. int friendId = 0;
  749. List<int> alterUsers;
  750. if (!isGroup) {
  751. friendId = Provider.of<int>(context);
  752. } else {
  753. if (!isGift) {
  754. //检查@的人员
  755. alterUsers = getAlterUsers(messageText);
  756. }
  757. }
  758. ChatChannelType channelType =
  759. isGroup ? ChatChannelType.Group : ChatChannelType.Session;
  760. if (widget.isTranslateHK) {
  761. channelType = ChatChannelType.TransHK;
  762. print('聊天是 TransHK');
  763. }
  764. MsgModel msg = MsgHandler.createSendMsg(
  765. isGift ? ChatType.EmoticonType : ChatType.TextChatType, messageText,
  766. friendId: friendId,
  767. refMsg: refMsg,
  768. refShortTxt: _getRefShortText(),
  769. altUsers: alterUsers,
  770. channelType: channelType);
  771. widget.sendMsg(msg);
  772. if (!isGift) {
  773. if (refMsg != null) {
  774. refMsg = null;
  775. }
  776. alterMemberList.clear();
  777. lastTxtLen = 0;
  778. }
  779. setState(() {
  780. _isComposingMessage = _textCtrl.text.length > 0;
  781. });
  782. }
  783. ///emoji插入表情-处理换行
  784. static void insertText(String text, TextEditingController _textCtrl) {
  785. var value = _textCtrl.value;
  786. var start = value.selection.baseOffset;
  787. var end = value.selection.extentOffset;
  788. if (value.selection.isValid) {
  789. String newText = "";
  790. if (value.selection.isCollapsed) {
  791. if (end > 0) {
  792. newText += value.text.substring(0, end);
  793. }
  794. newText += text;
  795. if (value.text.length > end) {
  796. newText += value.text.substring(end, value.text.length);
  797. }
  798. } else {
  799. newText = value.text.replaceRange(start, end, text);
  800. end = start;
  801. }
  802. _textCtrl.value = value.copyWith(
  803. text: newText,
  804. selection: value.selection.copyWith(
  805. baseOffset: end + text.length, extentOffset: end + text.length));
  806. } else {
  807. _textCtrl.value = TextEditingValue(
  808. text: text,
  809. selection:
  810. TextSelection.fromPosition(TextPosition(offset: text.length)));
  811. }
  812. }
  813. static void deleteText(TextEditingController _textController) {
  814. var value = _textController.value;
  815. var selection = value.selection;
  816. var text = value.text;
  817. String newText = '';
  818. if (selection.baseOffset != selection.extentOffset) {
  819. newText = selection.textBefore(text) + selection.textAfter(text);
  820. _textController.value = TextEditingValue(
  821. text: newText,
  822. selection: selection.copyWith(
  823. baseOffset: selection.baseOffset,
  824. extentOffset: selection.baseOffset));
  825. } else {
  826. int offsetLength = 1;
  827. String text = _textController.text;
  828. if (text.substring(text.length - 1, text.length) == ']') {
  829. ///删除表情
  830. int end = _textController.text.lastIndexOf('[');
  831. offsetLength = text.length - end;
  832. }
  833. newText = text.substring(0, selection.baseOffset - offsetLength) +
  834. selection.textAfter(text);
  835. _textController.value = TextEditingValue(
  836. text: newText,
  837. selection: selection.copyWith(
  838. baseOffset: selection.baseOffset - offsetLength,
  839. extentOffset: selection.baseOffset - offsetLength));
  840. }
  841. }
  842. }