Hibok
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

858 lines
27 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:chat/data/translate_hk_data_mgr.dart';
  4. import 'package:chat/home/audio_chat_view.dart';
  5. import 'package:chat/utils/screen.dart';
  6. import 'package:chat/utils/upload_util.dart';
  7. import 'package:fixnum/fixnum.dart';
  8. import 'package:cached_network_image/cached_network_image.dart';
  9. import 'package:chat/chat/translate_state.dart';
  10. import 'package:chat/data/UserData.dart';
  11. import 'package:chat/data/chat_data_mgr.dart';
  12. import 'package:chat/data/constants.dart';
  13. import 'package:chat/generated/i18n.dart';
  14. import 'package:chat/home/add_friend.dart';
  15. import 'package:chat/models/ChatMsg.dart';
  16. import 'package:chat/models/UserInfo.dart';
  17. import 'package:chat/models/keyboard_provider.dart';
  18. import 'package:chat/models/ref_name_provider.dart';
  19. import 'package:chat/models/voucher_change.dart';
  20. import 'package:chat/proto/all.pbserver.dart';
  21. import 'package:chat/utils/CustomUI.dart';
  22. import 'package:chat/utils/HttpUtil.dart';
  23. import 'package:chat/utils/MessageMgr.dart';
  24. import 'package:chat/utils/TokenMgr.dart';
  25. import 'package:chat/utils/analyze_utils.dart';
  26. import 'package:chat/utils/app_navigator.dart';
  27. import 'package:chat/utils/blacklist_mgr.dart';
  28. import 'package:chat/utils/count_down_button.dart';
  29. import 'package:chat/utils/friend_list_mgr.dart';
  30. import 'package:chat/utils/msgHandler.dart';
  31. import 'package:chat/utils/net_state_widget.dart';
  32. import 'package:chat/utils/sound_util.dart';
  33. import 'package:chat/utils/sp_utils.dart';
  34. import 'package:chat/utils/sql_util.dart';
  35. import 'package:dio/dio.dart';
  36. import 'package:extended_text/extended_text.dart';
  37. import 'package:flutter/cupertino.dart';
  38. import 'package:flutter/material.dart';
  39. import 'package:flutter/services.dart';
  40. import 'package:oktoast/oktoast.dart';
  41. import 'package:permission_handler/permission_handler.dart';
  42. import 'package:provider/provider.dart';
  43. import 'package:scroll_to_index/scroll_to_index.dart';
  44. //import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
  45. import '../r.dart';
  46. import 'ChatPageItem.dart';
  47. import 'input_bar.dart';
  48. import 'package:chat/utils/PopUpMenu.dart' as myPop;
  49. import 'package:chat/models/money_change.dart';
  50. class ChatPage extends StatefulWidget {
  51. final int friendId;
  52. final int enterType; // 0默认 1图片
  53. final dynamic enterContent;
  54. final bool isTranslateButler;
  55. ChatPage(
  56. {Key key,
  57. this.friendId,
  58. this.enterType = 0,
  59. this.enterContent,
  60. this.isTranslateButler = false})
  61. : super(key: key);
  62. _ChatPageState createState() => _ChatPageState();
  63. }
  64. class _ChatPageState extends State<ChatPage> {
  65. MessageMgr msgMgr = MessageMgr();
  66. UserInfo friendInfo;
  67. List<MsgModel> msgList;
  68. KeyboardIndexProvider _keyboardIndexProvider = KeyboardIndexProvider();
  69. TextEditingController nickNameController = new TextEditingController();
  70. //统计聊天时长
  71. int startTime;
  72. bool isTranslateButler;
  73. bool isTranslateButlerFinish=false;///翻译管家是否已经结束
  74. int jumpTime;
  75. AutoScrollController controller;
  76. bool hasChatPermission=false;
  77. @override
  78. void dispose() {
  79. var endTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  80. AnalyzeUtils.commitChatDuration(startTime, endTime);
  81. MessageMgr().off('Send CoinBag', _sendCoin);
  82. msgMgr.off('New Chat Message', receiveMsg);
  83. msgMgr.off('Keyboard Hide', dealWithKeyboardHide);
  84. msgMgr.off('Delete Select Message', _deleteItem);
  85. msgMgr.off(MessageMgr.TRANSLATE_HK_END_CHAT, translateHKChatEnd);
  86. msgMgr.off('Jump to Msg', jumpToMsg);
  87. MsgHandler.curActiveSession = 0;
  88. SoundUtils().stop();
  89. nickNameController.dispose();
  90. super.dispose();
  91. }
  92. jumpToMsg(time) async {
  93. hideKeyBoard();
  94. int jumIndex = 0;
  95. jumpTime = time;
  96. for (int i = 0; i < msgList.length; i++) {
  97. if (time == msgList[i].time) {
  98. jumIndex = i;
  99. break;
  100. }
  101. }
  102. if (jumIndex < 0) {
  103. jumIndex = 0;
  104. }
  105. if (jumIndex > msgList.length - 1) {
  106. jumIndex = msgList.length - 1;
  107. }
  108. await controller.scrollToIndex(jumIndex,
  109. preferPosition: AutoScrollPosition.middle);
  110. controller.highlight(jumIndex,
  111. highlightDuration: new Duration(milliseconds: 100));
  112. }
  113. @override
  114. void initState() {
  115. super.initState();
  116. print('init chatpage');
  117. getDefaultSetting();
  118. getUserInfo();
  119. controller = AutoScrollController(
  120. viewportBoundaryGetter: () =>
  121. Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
  122. axis: Axis.vertical);
  123. isTranslateButler = widget.isTranslateButler;
  124. ///todo 这里再判断是否还在服务时间
  125. startTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  126. msgList = ChatDataMgr().getRecord();
  127. msgMgr.on('New Chat Message', receiveMsg);
  128. msgMgr.on('Keyboard Hide', dealWithKeyboardHide);
  129. msgMgr.on('Send CoinBag', _sendCoin);
  130. msgMgr.on('Delete Select Message', _deleteItem);
  131. msgMgr.on(MessageMgr.TRANSLATE_HK_END_CHAT, translateHKChatEnd);
  132. msgMgr.on('Jump to Msg', jumpToMsg);
  133. WidgetsBinding.instance.addPostFrameCallback((_) {
  134. checkPermission();
  135. });
  136. }
  137. checkPermission()async{
  138. if(isTranslateButler){
  139. if (await CustomUI.showPermissionSetting(context,
  140. PermissionGroup.microphone, I18n.of(context).video_permission)) {
  141. setState(() {
  142. hasChatPermission=true;
  143. dx=Screen.width - zoomButtonSizeWidth;
  144. dy = (Screen.height - zoomButtonSizeHeight)/2;
  145. });
  146. } else {
  147. hasChatPermission=false;
  148. showToast(I18n.of(context).need_record);
  149. }
  150. }
  151. }
  152. translateHKChatEnd(args){
  153. setState(() {
  154. isShowZoomButton=false;
  155. isTranslateButlerFinish=true;
  156. TranslateHKMgr().order=null;
  157. });
  158. }
  159. void _sendFile(File file) async {
  160. // File file = await FilePicker.getFile();
  161. int fileSize = file.lengthSync();
  162. print('选择的文件 ${file.path} 大小 $fileSize');
  163. if (fileSize > 33 * 1024 * 1024) {
  164. showToast(I18n.of(context).max_file.replaceFirst('/s1', 33.toString()));
  165. return;
  166. }
  167. var fileName = file.path.split('/').last;
  168. print('fileName $fileName');
  169. var ext = '';
  170. var extList = fileName.split('.');
  171. if (extList.length > 1) {
  172. ext = extList.last;
  173. }
  174. print('ext $ext');
  175. var fileMsg = FileChat.create();
  176. fileMsg.type = ext;
  177. fileMsg.size = fileSize;
  178. fileMsg.name = fileName;
  179. var msg = MsgHandler.createSendMsg(
  180. ChatType.FileChatType, fileMsg.writeToBuffer(),
  181. friendId: widget.friendId,
  182. localFile: file.path,
  183. channelType: ChatChannelType.Session);
  184. sendMsg(msg);
  185. }
  186. _sendCoin(args) async {
  187. var res = await _checkCoinBeforeSend(args['amount']);
  188. if (res != null) {
  189. args['redNo'] = res['redNo'];
  190. args['friendId'] = widget.friendId;
  191. var msg = MsgHandler.createCoinBagMsg(args);
  192. sendMsg(msg);
  193. }
  194. }
  195. //校验
  196. _checkCoinBeforeSend(int amount) async {
  197. Map data = {
  198. "userId": UserData().basicInfo.userId,
  199. "rUserId": friendInfo.userId,
  200. "price": amount,
  201. };
  202. data['sign'] = TokenMgr().getSign(data);
  203. Response res = await HttpUtil().post('red/packet/send', data: data);
  204. if (res == null) {
  205. return null;
  206. }
  207. Map resData = res.data;
  208. if (resData['code'] == 0) {
  209. print(resData['data']);
  210. return resData['data'];
  211. }
  212. return null;
  213. }
  214. void getDefaultSetting() async {
  215. bool soundPlayMode =
  216. (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false;
  217. _keyboardIndexProvider.init(soundPlayMode);
  218. }
  219. dealWithKeyboardHide(args) {
  220. if (_keyboardIndexProvider.curKeyboardIndex == 0) {
  221. hideKeyBoard();
  222. }
  223. }
  224. getUserInfo() async {
  225. //先从本地获取用户信息
  226. friendInfo = await HttpUtil().getFriendInfo(widget.friendId, true);
  227. //如果是新的聊天,向服务器发送创建会话消息
  228. if (msgList.length == 0) {
  229. MsgHandler.getActiveSesstion(
  230. [friendInfo.userId, UserData().basicInfo.userId]);
  231. }
  232. if (mounted) {
  233. setState(() {});
  234. }
  235. WidgetsBinding.instance.addPostFrameCallback((_) {
  236. if (widget.enterType == 1) {
  237. print('接收到的:${widget.enterContent}');
  238. _sendFile(File(widget.enterContent));
  239. } else if (widget.enterType == 2 || widget.enterType == 3) {
  240. //转发消息
  241. MsgModel originMsg = widget.enterContent;
  242. MsgModel msg = MsgHandler.createSendMsg(
  243. ChatType.valueOf(originMsg.msgType), originMsg.msgContent);
  244. msg.extraInfo = originMsg.extraInfo;
  245. if (originMsg.extraFile == null ||
  246. originMsg.extraFile.contains('http')) {
  247. msg.extraFile = originMsg.extraFile;
  248. } else {
  249. msg.extraFile = UploadUtil().getFullUrl(
  250. originMsg.extraFile, originMsg.sessionId, originMsg.channelType);
  251. }
  252. msg.localFile = originMsg.localFile;
  253. msg.friendId = widget.friendId;
  254. if (msg.localFile != null) {
  255. msg.state = MsgState.Uploaded;
  256. }
  257. sendMsg(msg);
  258. }
  259. });
  260. }
  261. @override
  262. Widget build(BuildContext context) {
  263. print('build chatpage');
  264. if (friendInfo == null) {
  265. return Scaffold(
  266. backgroundColor: const Color(0xFFE2E9F1),
  267. body: SafeArea(
  268. child: Center(
  269. child: CircularProgressIndicator(),
  270. ),
  271. ),
  272. );
  273. }
  274. List<Widget> actions = [];
  275. int voucher = Provider.of<VoucherChangeProvider>(context).voucher;
  276. actions.add(Row(
  277. children: <Widget>[
  278. CustomUI.buildImageLabel("assets/images/voucher.png", voucher,
  279. imgOpc: 0.5, imgHeight: 13),
  280. CustomUI.buildImageLabel(
  281. R.assetsImagesCoin, Provider.of<MoneyChangeProvider>(context).money,
  282. isLeft: false)
  283. ],
  284. ));
  285. actions.add(TranslateSateWidget(friendId: friendInfo.userId));
  286. actions.add(Container(
  287. margin: EdgeInsets.only(top: 1),
  288. child: myPop.PopupMenuButton(
  289. icon: Icon(
  290. Icons.more_horiz,
  291. size: 24,
  292. ),
  293. offset: Offset(0, 100),
  294. onSelected: (int index) {
  295. if (index == 2) {
  296. AppNavigator.pushInformUserPage(
  297. context, friendInfo.sex == 1, friendInfo.userId);
  298. } else if (index == 1) {
  299. Navigator.of(context).push(
  300. new MaterialPageRoute(
  301. builder: (context) {
  302. return AddFriendPage(
  303. userId: friendInfo.userId,
  304. pageType: SendMessagePageType.Remark,
  305. originalName: Provider.of<RefNameProvider>(context)
  306. .getRefName(
  307. friendInfo.userId, friendInfo.nickName));
  308. },
  309. ),
  310. );
  311. }
  312. },
  313. itemBuilder: (BuildContext context) {
  314. return <myPop.PopupMenuItem<int>>[
  315. myPop.PopupMenuItem(
  316. child: Container(
  317. alignment: Alignment.center,
  318. color: Colors.white,
  319. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  320. child: Text(I18n.of(context).anonymous_report,
  321. textScaleFactor: 1.0,
  322. maxLines: 1,
  323. style: TextStyle(
  324. color: Color(AppColors.AppBarColor), fontSize: 12)),
  325. ),
  326. value: 2,
  327. ),
  328. myPop.PopupMenuItem(
  329. child: Container(
  330. decoration: BoxDecoration(
  331. border: Border(
  332. top: BorderSide(width: 1, color: Colors.grey[300])),
  333. color: Colors.white,
  334. ),
  335. alignment: Alignment.center,
  336. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  337. child: Text(I18n.of(context).Remark,
  338. textScaleFactor: 1.0,
  339. maxLines: 1,
  340. style: TextStyle(
  341. color: Color(AppColors.AppBarColor), fontSize: 12)),
  342. ),
  343. value: 1,
  344. ),
  345. ];
  346. })));
  347. return WillPopScope(
  348. child: Stack(
  349. children: <Widget>[
  350. MultiProvider(
  351. providers: [
  352. ChangeNotifierProvider(create: (_) => _keyboardIndexProvider),
  353. Provider<bool>.value(value: false),
  354. Provider<int>.value(value: widget.friendId),
  355. ],
  356. child: GestureDetector(
  357. onTap: hideKeyBoard,
  358. child: ExtendedTextSelectionPointerHandler(
  359. ///选择文字,消除弹窗
  360. builder: (states) {
  361. return Listener(
  362. child: Scaffold(
  363. resizeToAvoidBottomInset: false,
  364. backgroundColor: const Color(0xFFE2E9F1),
  365. appBar: AppBar(
  366. title: Text(
  367. '${Provider.of<RefNameProvider>(context).getRefName(friendInfo.userId, friendInfo.nickName)}',
  368. textScaleFactor: 1.0,
  369. style: TextStyle(
  370. color: Constants.BlackTextColor,
  371. fontSize: 16.47),
  372. ),
  373. leading: CustomUI.buildCustomLeading(context,onTap:(){
  374. goBackCheck();
  375. }),
  376. titleSpacing: -10,
  377. centerTitle: false,
  378. elevation: 1,
  379. actions: actions),
  380. body: SafeArea(
  381. child: Column(
  382. children: <Widget>[
  383. NetStateWidget(),
  384. (isTranslateButler)
  385. ? _buildTranslationButler()
  386. : Container(),
  387. Expanded(child: _buildMessageList()),
  388. InputBar(sendMsg: sendMsg,isTranslateHK: isTranslateButler,),
  389. ],
  390. ))),
  391. behavior: HitTestBehavior.translucent,
  392. onPointerDown: (value) {
  393. for (var state in states) {
  394. if (!state.containsPosition(value.position)) {
  395. //clear other selection
  396. state.clearSelection();
  397. }
  398. }
  399. },
  400. onPointerMove: (value) {
  401. //clear other selection
  402. for (var state in states) {
  403. if (!state.containsPosition(value.position)) {
  404. //clear other selection
  405. state.clearSelection();
  406. }
  407. }
  408. },
  409. );
  410. },
  411. ))),
  412. isTranslateButler ? getAudioChatView() : Container(),
  413. isTranslateButler ? zoomAudioButton() : Container()
  414. ],
  415. ),
  416. onWillPop: () {
  417. if(isTranslateButler && !isTranslateButlerFinish){
  418. if(UserData().isTranslateUser){
  419. showToast(I18n.of(context).translation_butler_order_close_tips);
  420. }else{
  421. CustomUI.buildTowConfirm(
  422. context,
  423. I18n.of(context).translation_butler_order_close_tips2,
  424. I18n.of(context).confirm,
  425. (){
  426. setState(() {
  427. isTranslateButlerFinish=true;
  428. });
  429. MsgHandler.sendEndTransHKOrderReq();
  430. Navigator.of(context).pop();
  431. },
  432. I18n.of(context).cancel,
  433. (){ Navigator.of(context).pop();});
  434. }
  435. }else{
  436. Navigator.of(context).pop(); }
  437. return Future.value(false);
  438. });
  439. }
  440. goBackCheck(){
  441. if(isTranslateButler && !isTranslateButlerFinish){
  442. if(UserData().isTranslateUser){
  443. showToast('翻译服务未结束不能主动结束');
  444. }else{
  445. CustomUI.buildTowConfirm(
  446. context,
  447. '是否提前结束翻译管家服务?',
  448. I18n.of(context).confirm,
  449. (){
  450. setState(() {
  451. isTranslateButlerFinish=true;
  452. });
  453. MsgHandler.sendEndTransHKOrderReq();
  454. Navigator.of(context).pop();
  455. },
  456. '取消',
  457. (){ Navigator.of(context).pop();});
  458. }
  459. }else{
  460. Navigator.of(context).pop();
  461. }
  462. }
  463. Widget getAudioChatView() {
  464. if (friendInfo == null || isTranslateButlerFinish || !hasChatPermission) {
  465. return Container();
  466. }
  467. return isActive
  468. ? Offstage(
  469. offstage: !isShowAudio,
  470. child: AudioChatPage(
  471. userInfo: friendInfo,
  472. isTranslateButler: true,
  473. isReplay: !TranslateHKMgr().isUser,
  474. translateButlerCloseCallBack: audioChatPageCallBack,
  475. ),
  476. ):Container()
  477. ;
  478. }
  479. audioChatPageCallBack(int args) {
  480. switch (args) {
  481. case 1:
  482. setState(() {
  483. isShowAudio = !isShowAudio;
  484. isShowZoomButton = true;
  485. });
  486. break;
  487. case 2:
  488. setState(() {
  489. isActive = false;
  490. });
  491. break;
  492. }
  493. }
  494. bool isShowAudio = false;
  495. ///控住连麦窗口是否显示
  496. bool isActive = true;
  497. ///控制连麦是否关闭
  498. bool isShowZoomButton = true;
  499. GlobalKey mykey = GlobalKey();
  500. double dx = 0 , dy = 0;
  501. double zoomButtonSizeWidth = 60;
  502. double zoomButtonSizeHeight = 74;
  503. void dragEvent(DragUpdateDetails details) {
  504. final RenderObject box = context.findRenderObject();
  505. // 获得自定义Widget的大小,用来计算Widget的中心锚点
  506. dx = details.globalPosition.dx - mykey.currentContext.size.width / 2;
  507. dy = details.globalPosition.dy - mykey.currentContext.size.height / 2;
  508. // print('dx $dx dy $dy screen width:${Screen.width}');
  509. if (dx > Screen.width - zoomButtonSizeWidth) {
  510. dx = Screen.width - zoomButtonSizeWidth;
  511. } else if (dx < 0) {
  512. dx = 0;
  513. }
  514. if (dy > Screen.height - zoomButtonSizeHeight) {
  515. dy = Screen.height - zoomButtonSizeHeight;
  516. } else if (dy < 0) {
  517. dy = 0;
  518. }
  519. setState(() {});
  520. }
  521. Widget zoomAudioButton() {
  522. if (friendInfo == null || !isShowZoomButton || !hasChatPermission) {
  523. return Container();
  524. }
  525. Widget button = Container(
  526. key: mykey,
  527. width: zoomButtonSizeWidth,
  528. height: zoomButtonSizeHeight,
  529. child: Card(child: Align(child: Icon(
  530. IconData(0xe67d, fontFamily: Constants.IconFontFamily),
  531. color: Color(0xFF008AFF),
  532. size: 35.0,
  533. ),alignment: Alignment.center,),),
  534. );
  535. return GestureDetector(
  536. onHorizontalDragUpdate: dragEvent,
  537. onVerticalDragUpdate: dragEvent,
  538. onTap: (){
  539. setState(() {
  540. isShowAudio = !isShowAudio;
  541. isShowZoomButton = !isShowZoomButton;
  542. });
  543. },
  544. child: Container(
  545. child: Transform.translate(
  546. offset: Offset(dx, dy),
  547. child: Align(
  548. alignment: Alignment.topLeft,
  549. child: button,
  550. ),
  551. ),
  552. ),
  553. );
  554. }
  555. Widget _buildTranslationButler() {
  556. bool hasHeadImg = true;
  557. if (friendInfo.headimgurl == null || friendInfo.headimgurl.length == 0) {
  558. hasHeadImg = false;
  559. }
  560. /// 5H币/1分钟
  561. String coinTIme = I18n.of(context).translation_butler_coin_time;
  562. coinTIme = coinTIme.replaceAll('/s1', '5');
  563. coinTIme = coinTIme.replaceAll('/s2', '1');
  564. return Container(
  565. padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
  566. color: Colors.white,
  567. child: Row(
  568. children: <Widget>[
  569. ClipRRect(
  570. borderRadius: BorderRadius.circular(8),
  571. child: hasHeadImg
  572. ? CachedNetworkImage(
  573. imageUrl: friendInfo.headimgurl,
  574. placeholder: (context, url) => Image.asset(
  575. Constants.DefaultHeadImgUrl,
  576. width: 54,
  577. height: 54,
  578. ),
  579. width: 54,
  580. height: 54,
  581. )
  582. : SizedBox(
  583. width: 54,
  584. height: 54,
  585. child: Image.asset(R.assetsImagesDefaultNorAvatar))),
  586. Padding(
  587. padding: EdgeInsets.only(left: 10),
  588. child: Container(
  589. child: Column(
  590. crossAxisAlignment: CrossAxisAlignment.start,
  591. children: <Widget>[
  592. Text(
  593. I18n.of(context).translation_butler,
  594. textScaleFactor: 1.0,
  595. overflow: TextOverflow.ellipsis,
  596. maxLines: 1,
  597. style: TextStyle(
  598. color: AppColors.NewAppbarTextColor, fontSize: 15),
  599. ),
  600. SizedBox(
  601. height: 5,
  602. ),
  603. Text(
  604. coinTIme,
  605. textScaleFactor: 1.0,
  606. maxLines: 1,
  607. style: TextStyle(color: Color(0xFF797979), fontSize: 13),
  608. )
  609. ],
  610. ),
  611. constraints: BoxConstraints(maxWidth: 135),
  612. ),
  613. ),
  614. (TranslateHKMgr().isUser&& !isTranslateButlerFinish)?Expanded(
  615. child: Container(
  616. constraints: BoxConstraints(maxWidth: 130),
  617. width: double.maxFinite,
  618. child: CountDownButton(
  619. I18n.of(context).translation_butler_end_service,
  620. () {
  621. // Navigator.of(context).pop();
  622. },
  623. countDownTime: 60*5,
  624. align: Alignment.centerRight,
  625. onPress: () {
  626. MsgHandler.sendEndTransHKOrderReq();
  627. // Navigator.of(context).pop();
  628. },
  629. ),
  630. // alignment: Alignment(1,0),
  631. )):Container(),
  632. ],
  633. ),
  634. );
  635. }
  636. Widget _buildMessageList() {
  637. return Container(
  638. alignment: Alignment.topCenter,
  639. child: msgList.length == 0
  640. ? Padding(
  641. padding: EdgeInsets.all(8),
  642. child: Text(
  643. I18n.of(context).chat_tips,
  644. textAlign: TextAlign.center,
  645. textScaleFactor: 1.0,
  646. style: TextStyle(color: Colors.grey, fontSize: 12),
  647. ))
  648. : NotificationListener(
  649. child: Scrollbar(
  650. child: ListView.builder(
  651. controller: controller,
  652. physics: new ClampingScrollPhysics(),
  653. itemCount: msgList.length,
  654. itemBuilder: _buildItem,
  655. padding: EdgeInsets.symmetric(vertical: 8),
  656. reverse: true,
  657. shrinkWrap: true,
  658. )),
  659. onNotification: (notification) {
  660. if (notification is ScrollNotification) {}
  661. return true;
  662. },
  663. ),
  664. );
  665. }
  666. hideKeyBoard() {
  667. _keyboardIndexProvider.changeSelectIndex(-1);
  668. }
  669. readOnly() {
  670. _keyboardIndexProvider.changeReadOnlyKey(true);
  671. }
  672. sendMsg(MsgModel msg) async {
  673. // if(widget.isTranslateButler){ ///翻译管家聊天通道
  674. // msg.channelType = ChatChannelType.TransHK.value;
  675. // }
  676. print('对方是否拉黑你 ${friendInfo.isBlackened}');
  677. if (BlacklistMgr.isBlack(friendInfo.userId)) {
  678. return;
  679. }
  680. print('chat page session id:${msg.sessionId}');
  681. if (!friendInfo.isCanStrangerNews &&
  682. !FriendListMgr().isMyFriend(friendInfo.userId)) {
  683. showToast(I18n.of(context).stranger_close_tips);
  684. return;
  685. }
  686. MsgHandler.insertMsgToDB(msg);
  687. MsgHandler.sendChatMsg(msg);
  688. if (mounted) {
  689. setState(() {});
  690. await controller.scrollToIndex(0,
  691. preferPosition: AutoScrollPosition.begin);
  692. }
  693. }
  694. MsgModel msg;
  695. int count = 0;
  696. testBig(MsgModel msg) async {
  697. for (int k = 0; k < 100; k++) {
  698. msg.msgContent = utf8.encode('测试$count');
  699. Int64 time = Int64((DateTime.now()).millisecondsSinceEpoch);
  700. msg.time = time.toInt();
  701. MsgHandler.insertMsgToDB(msg);
  702. MsgHandler.sendChatMsg(msg);
  703. await Future.delayed(Duration(milliseconds: 500), () {});
  704. count++;
  705. }
  706. count = 0;
  707. print('发送完毕');
  708. showToast('发送完毕');
  709. }
  710. void receiveMsg(args) {
  711. if (mounted) {
  712. setState(() {});
  713. }
  714. }
  715. _deleteItem(msg) {
  716. MessageMgr().emit('Cancel Request', msg);
  717. print('#### 开始删除--');
  718. msgList.remove(msg);
  719. setState(() {});
  720. SqlUtil().deleteSigleRecordWith(msg.sessionId, msg.time);
  721. }
  722. Widget _wrapScrollTag({int index, Widget child}) => AutoScrollTag(
  723. key: ValueKey(index),
  724. controller: controller,
  725. index: index,
  726. child: child,
  727. highlightColor: Colors.white.withOpacity(1),
  728. );
  729. Widget _buildItem(BuildContext context, int index) {
  730. var lastMsgTime;
  731. if (index < msgList.length - 1) {
  732. lastMsgTime = msgList[index + 1].time;
  733. }
  734. MsgModel msg = msgList[index];
  735. return _wrapScrollTag(
  736. index: index,
  737. child: ChatPageItem(
  738. key: Key(msg.time.toString()),
  739. msg: msg,
  740. hideKeyboard: readOnly,
  741. friendInfo: friendInfo,
  742. lastMsgTime: lastMsgTime));
  743. }
  744. }