Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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