Hibok
您不能選擇超過 %s 個話題 話題必須以字母或數字為開頭,可包含連接號 ('-') 且最長為 35 個字
 
 
 
 
 
 

772 行
25 KiB

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