Hibok
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

611 řádky
20 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:chat/chat/group_chat_item.dart';
  4. import 'package:chat/data/chat_data_mgr.dart';
  5. import 'package:chat/data/constants.dart';
  6. import 'package:chat/data/group_data_mgr.dart';
  7. import 'package:chat/generated/i18n.dart';
  8. import 'package:chat/home/group_setting.dart';
  9. import 'package:chat/models/ChatMsg.dart';
  10. import 'package:chat/models/group_info_model.dart';
  11. import 'package:chat/models/keyboard_provider.dart';
  12. import 'package:chat/models/money_change.dart';
  13. import 'package:chat/models/voucher_change.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/analyze_utils.dart';
  18. import 'package:chat/utils/msgHandler.dart';
  19. import 'package:chat/utils/net_state_widget.dart';
  20. import 'package:chat/utils/screen.dart';
  21. import 'package:chat/utils/sound_util.dart';
  22. import 'package:chat/utils/sp_utils.dart';
  23. import 'package:chat/utils/sql_util.dart';
  24. import 'package:chat/utils/upload_util.dart';
  25. import 'package:extended_text/extended_text.dart';
  26. import 'package:flutter/cupertino.dart';
  27. import 'package:flutter/material.dart';
  28. import 'package:oktoast/oktoast.dart';
  29. import 'package:provider/provider.dart';
  30. import 'package:scroll_to_index/scroll_to_index.dart';
  31. import '../r.dart';
  32. import 'input_bar.dart';
  33. import 'package:chat/models/ref_name_provider.dart';
  34. import 'package:fixnum/fixnum.dart';
  35. class GroupChatPage extends StatefulWidget {
  36. final GroupInfoModel groupInfoModel;
  37. final int enterType; // 0默认 1图片
  38. final dynamic enterContent;
  39. GroupChatPage(
  40. {Key key, this.groupInfoModel, this.enterType = 0, this.enterContent})
  41. : super(key: key);
  42. _GroupChatPageState createState() => _GroupChatPageState();
  43. }
  44. class _GroupChatPageState extends State<GroupChatPage> {
  45. AutoScrollController controller;
  46. MessageMgr msgMgr = MessageMgr();
  47. List<MsgModel> msgList;
  48. KeyboardIndexProvider _keyboardIndexProvider = KeyboardIndexProvider();
  49. TextEditingController nickNameController = new TextEditingController();
  50. //统计聊天时长
  51. int startTime;
  52. //子元素的对应偏移量
  53. Map itemOffsetMap = {};
  54. //未读消息数目
  55. int unreadNums = 0;
  56. //最上一条未读消息id
  57. int unreadTime;
  58. //@消息索引
  59. int alterTime;
  60. String alterUserName = '';
  61. int jumpTime;
  62. @override
  63. void dispose() {
  64. var endTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  65. AnalyzeUtils.commitChatDuration(startTime, endTime);
  66. msgMgr.off('New Chat Message', receiveMsg);
  67. msgMgr.off('Keyboard Hide', dealWithKeyboardHide);
  68. msgMgr.off('Update Group Info', updateGroupInfo);
  69. msgMgr.off('Delete Select Message', _deleteItem);
  70. msgMgr.off('Jump to Msg', jumpToMsg);
  71. MsgHandler.curActiveSession = 0;
  72. SoundUtils().stop();
  73. super.dispose();
  74. }
  75. @override
  76. void initState() {
  77. super.initState();
  78. print('init group chat page ${widget.groupInfoModel.sessionId}');
  79. getDefaultSetting();
  80. controller = AutoScrollController(
  81. viewportBoundaryGetter: () =>
  82. Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
  83. axis: Axis.vertical);
  84. startTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  85. unreadNums = ChatDataMgr()
  86. .groupUnreadProvider
  87. .getUnreadCount(widget.groupInfoModel.sessionId);
  88. alterTime = ChatDataMgr()
  89. .groupUnreadProvider
  90. .getHavaAltertime(widget.groupInfoModel.sessionId);
  91. MsgHandler.updateActiveSesstion(widget.groupInfoModel.sessionId,
  92. isGroup: true);
  93. msgList = ChatDataMgr().getGroupRecord();
  94. if (unreadNums >= 10 && unreadNums <= msgList.length) {
  95. unreadTime = msgList[unreadNums - 1].time;
  96. }
  97. for (int i = 0; i < msgList.length; i++) {
  98. if (msgList[i].time == alterTime) {
  99. alterUserName =
  100. widget.groupInfoModel.getMember(msgList[i].friendId).nickName;
  101. break;
  102. }
  103. }
  104. msgMgr.on('New Chat Message', receiveMsg);
  105. msgMgr.on('Keyboard Hide', dealWithKeyboardHide);
  106. msgMgr.on('Update Group Info', updateGroupInfo);
  107. msgMgr.on('Delete Select Message', _deleteItem);
  108. msgMgr.on('Jump to Msg', jumpToMsg);
  109. WidgetsBinding.instance.addPostFrameCallback((_) {
  110. if (widget.enterType == 1) {
  111. print('接收到的:${widget.enterContent}');
  112. _sendFile(File(widget.enterContent));
  113. } else if (widget.enterType == 2) {
  114. //转发消息
  115. MsgModel originMsg = widget.enterContent;
  116. MsgModel msg = MsgHandler.createSendMsg(
  117. ChatType.valueOf(originMsg.msgType), originMsg.msgContent,
  118. channelType: ChatChannelType.Group);
  119. msg.extraInfo = originMsg.extraInfo;
  120. // msg.extraFile = originMsg.extraFile;
  121. if (originMsg.extraFile == null ||
  122. originMsg.extraFile.contains('http')) {
  123. msg.extraFile = originMsg.extraFile;
  124. } else {
  125. msg.extraFile = UploadUtil().getFullUrl(
  126. originMsg.extraFile, originMsg.sessionId, originMsg.channelType);
  127. }
  128. msg.localFile = originMsg.localFile;
  129. if (msg.localFile != null) {
  130. msg.state = MsgState.Uploaded;
  131. }
  132. sendMsg(msg);
  133. }
  134. });
  135. controller.addListener(() {
  136. if (alterTime != null || unreadTime != null) {
  137. for (int i = 0; i < msgList.length; i++) {
  138. if ((msgList[i].time == alterTime || msgList[i].time == unreadTime) &&
  139. controller.isIndexStateInLayoutRange(i)) {
  140. clearUnreadNews();
  141. }
  142. }
  143. }
  144. });
  145. }
  146. jumpToMsg(time) async {
  147. hideKeyBoard();
  148. int jumIndex = 0;
  149. jumpTime = time;
  150. for (int i = 0; i < msgList.length; i++) {
  151. if (time == msgList[i].time) {
  152. jumIndex = i;
  153. break;
  154. }
  155. }
  156. if (jumIndex < 0) {
  157. jumIndex = 0;
  158. }
  159. if (jumIndex > msgList.length - 1) {
  160. jumIndex = msgList.length - 1;
  161. }
  162. await controller.scrollToIndex(jumIndex,
  163. preferPosition: AutoScrollPosition.middle);
  164. controller.highlight(jumIndex,
  165. highlightDuration: new Duration(milliseconds: 100));
  166. }
  167. void _sendFile(File file) async {
  168. // File file = await FilePicker.getFile();
  169. int fileSize = file.lengthSync();
  170. print('选择的文件 ${file.path} 大小 $fileSize');
  171. if (fileSize > 33 * 1024 * 1024) {
  172. showToast('文件大于33M');
  173. return;
  174. }
  175. var fileName = file.path.split('/').last;
  176. print('fileName $fileName');
  177. var ext = '';
  178. var extList = fileName.split('.');
  179. if (extList.length > 1) {
  180. ext = extList.last;
  181. }
  182. print('ext $ext');
  183. var fileMsg = FileChat.create();
  184. fileMsg.type = ext;
  185. fileMsg.size = fileSize;
  186. fileMsg.name = fileName;
  187. var msg = MsgHandler.createSendMsg(
  188. ChatType.FileChatType, fileMsg.writeToBuffer(),
  189. friendId: 0, localFile: file.path, channelType: ChatChannelType.Group);
  190. sendMsg(msg);
  191. }
  192. updateGroupInfo(args) {
  193. print('更新群信息');
  194. if (mounted) {
  195. setState(() {});
  196. }
  197. }
  198. void getDefaultSetting() async {
  199. bool soundPlayMode =
  200. (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false;
  201. _keyboardIndexProvider.init(soundPlayMode);
  202. }
  203. dealWithKeyboardHide(args) {
  204. if (_keyboardIndexProvider.curKeyboardIndex == 0) {
  205. readOnly();
  206. }
  207. }
  208. clearUnreadNews() {
  209. unreadTime = null;
  210. setState(() {});
  211. }
  212. @override
  213. Widget build(BuildContext context) {
  214. List<Widget> actions = [];
  215. actions.add(Row(
  216. children: <Widget>[
  217. CustomUI.buildImageLabel("assets/images/voucher.png",
  218. Provider.of<VoucherChangeProvider>(context).voucher,
  219. imgOpc: 0.5, imgHeight: 13),
  220. CustomUI.buildImageLabel(
  221. R.assetsImagesCoin, Provider.of<MoneyChangeProvider>(context).money,
  222. isLeft: false)
  223. ],
  224. ));
  225. actions.add(widget.groupInfoModel.isInGroup
  226. ? IconButton(
  227. icon: Icon(Icons.more_horiz),
  228. iconSize: 22,
  229. onPressed: () {
  230. //进入群管理界面
  231. hideKeyBoard();
  232. Navigator.of(context).push(
  233. new MaterialPageRoute(
  234. builder: (context) {
  235. return GroupSetting(
  236. groupInfoModel: widget.groupInfoModel,
  237. );
  238. },
  239. ),
  240. );
  241. },
  242. )
  243. : IconButton(
  244. icon: Icon(Icons.delete),
  245. iconSize: 22,
  246. color: Colors.redAccent,
  247. onPressed: () {
  248. //进入群管理界面
  249. quiteGroup();
  250. },
  251. ));
  252. Map refMap = Provider.of<RefNameProvider>(context).refMap;
  253. bool isHaveUnreadNews = unreadTime != null;
  254. bool isAlterYou = alterTime != null;
  255. return MultiProvider(
  256. providers: [
  257. ChangeNotifierProvider(create: (_) => _keyboardIndexProvider),
  258. Provider<bool>.value(value: true),
  259. Provider<GroupInfoModel>.value(value: widget.groupInfoModel),
  260. ],
  261. child: GestureDetector(
  262. onTap:hideKeyBoard, child: ExtendedTextSelectionPointerHandler(
  263. ///选择文字,消除弹窗
  264. builder: (states) {
  265. return Listener(
  266. child: Scaffold(
  267. resizeToAvoidBottomInset: false,
  268. backgroundColor: const Color(0xFFE2E9F1),
  269. appBar: AppBar(
  270. backgroundColor: AppColors.NewAppbarBgColor,
  271. title: Text(
  272. widget.groupInfoModel.getGroupName(refMap),
  273. textScaleFactor: 1.0,
  274. style: TextStyle(
  275. color: Constants.BlackTextColor, fontSize: 16.47),
  276. ),
  277. leading: CustomUI.buildCustomLeading(context),
  278. titleSpacing: -10,
  279. elevation: 1,
  280. centerTitle: false,
  281. actions: actions),
  282. body: SafeArea(
  283. child: Stack(
  284. children: <Widget>[
  285. Column(
  286. children: <Widget>[
  287. NetStateWidget(),
  288. Expanded(child: _buildMessageList()),
  289. InputBar(sendMsg: sendMsg),
  290. ],
  291. ),
  292. isHaveUnreadNews
  293. ? Positioned(
  294. top: 32.5,
  295. right: 0,
  296. child: InkWell(
  297. onTap: () {
  298. jumpToMsg(
  299. isAlterYou ? alterTime : unreadTime);
  300. clearUnreadNews();
  301. },
  302. child: Container(
  303. alignment: Alignment.center,
  304. decoration: BoxDecoration(
  305. boxShadow: [
  306. BoxShadow(
  307. color: Color(0x33000000), //阴影颜色
  308. blurRadius: 7, //阴影大小
  309. )
  310. ],
  311. borderRadius: BorderRadius.only(
  312. topLeft: Radius.circular(80),
  313. bottomLeft: Radius.circular(80)),
  314. color: Colors.white,
  315. ),
  316. constraints: BoxConstraints(minWidth: 120),
  317. height: 39,
  318. child: Row(
  319. children: <Widget>[
  320. SizedBox(width: 16.5),
  321. Image.asset(
  322. 'assets/images/up.png',
  323. width: 12,
  324. ),
  325. SizedBox(width: 10),
  326. Container(
  327. child: Text(
  328. isAlterYou
  329. ? '$alterUserName'
  330. : I18n.of(context)
  331. .new_msg
  332. .replaceFirst(
  333. '/s1',
  334. unreadNums
  335. .toString()),
  336. style: TextStyle(
  337. color: Color(0xFF3875E9)),
  338. overflow: TextOverflow.ellipsis,
  339. ),
  340. constraints: BoxConstraints(
  341. maxWidth: isAlterYou
  342. ? 60
  343. : Screen.width)),
  344. Text(
  345. isAlterYou
  346. ? '@${I18n.of(context).you}'
  347. : '',
  348. style: TextStyle(
  349. color: Color(0xFF3875E9)),
  350. ),
  351. SizedBox(width: 14),
  352. ],
  353. ),
  354. )))
  355. : Container()
  356. ],
  357. ))),
  358. onPointerDown: (value) {
  359. for (var state in states) {
  360. if (!state.containsPosition(value.position)) {
  361. //clear other selection
  362. state.clearSelection();
  363. }
  364. }
  365. },
  366. onPointerMove: (value) {
  367. //clear other selection
  368. for (var state in states) {
  369. if (!state.containsPosition(value.position)) {
  370. //clear other selection
  371. state.clearSelection();
  372. }
  373. }
  374. },
  375. );
  376. },
  377. )));
  378. }
  379. Widget _buildMessageList() {
  380. return Container(
  381. alignment: Alignment.topCenter,
  382. child: msgList.length == 0
  383. ? Padding(
  384. padding: EdgeInsets.all(10),
  385. child: Text(
  386. I18n.of(context).chat_tips,
  387. textAlign: TextAlign.center,
  388. textScaleFactor: 1.0,
  389. style: TextStyle(color: Colors.grey),
  390. ))
  391. : Scrollbar(
  392. child: ListView.builder(
  393. controller: controller,
  394. physics: new ClampingScrollPhysics(),
  395. itemCount: msgList.length,
  396. itemBuilder: _buildItem,
  397. padding: EdgeInsets.symmetric(vertical: 8),
  398. reverse: true,
  399. shrinkWrap: true,
  400. )),
  401. );
  402. }
  403. Widget _wrapScrollTag({int index, Widget child}) => AutoScrollTag(
  404. key: ValueKey(index),
  405. controller: controller,
  406. index: index,
  407. child: child,
  408. highlightColor: Colors.white.withOpacity(1),
  409. );
  410. hideKeyBoard() {
  411. _keyboardIndexProvider.changeSelectIndex(-1);
  412. }
  413. readOnly() {
  414. _keyboardIndexProvider.changeReadOnlyKey(true);
  415. }
  416. sendMsg(MsgModel msg) async {
  417. if (!widget.groupInfoModel.isInGroup) {
  418. //如果不在该群
  419. showToast(I18n.of(context).not_in_group);
  420. return;
  421. }
  422. MsgHandler.insertMsgToDB(msg);
  423. MsgHandler.sendChatMsg(msg);
  424. if (mounted) {
  425. setState(() {});
  426. await controller.scrollToIndex(0,
  427. preferPosition: AutoScrollPosition.begin);
  428. }
  429. }
  430. MsgModel msg;
  431. int count = 0;
  432. testBig(MsgModel msg) async {
  433. for (int k = 0; k < 100; k++) {
  434. msg.msgContent = utf8.encode('测试$count');
  435. Int64 time = Int64((DateTime.now()).millisecondsSinceEpoch);
  436. msg.time = time.toInt();
  437. MsgHandler.insertMsgToDB(msg);
  438. MsgHandler.sendChatMsg(msg);
  439. await Future.delayed(Duration(milliseconds: 300), () {});
  440. count++;
  441. }
  442. count = 0;
  443. print('攻击完毕');
  444. showToast('攻击完毕');
  445. }
  446. void receiveMsg(args) {
  447. print("msgList.length: ${msgList.length}");
  448. if (args != widget.groupInfoModel.sessionId) {
  449. return;
  450. }
  451. if (mounted) {
  452. setState(() {});
  453. }
  454. }
  455. _deleteItem(msg) {
  456. MessageMgr().emit('Cancel Request', msg);
  457. print('#### 开始删除--');
  458. msgList.remove(msg);
  459. setState(() {});
  460. SqlUtil().deleteSigleRecordWith(msg.sessionId, msg.time);
  461. }
  462. Widget _buildItem(BuildContext context, int index) {
  463. var lastMsgTime;
  464. if (index < msgList.length - 1) {
  465. lastMsgTime = msgList[index+1].time;
  466. }
  467. MsgModel msg = msgList[index];
  468. var result;
  469. if (msg.from == 0) {
  470. result = GroupChatPageItem(
  471. key: Key(msg.time.toString()), msg: msg, lastMsgTime: lastMsgTime);
  472. } else {
  473. result = GroupChatPageItem(
  474. key: Key(msg.time.toString()),
  475. msg: msg,
  476. memberModel: widget.groupInfoModel.getMember(msg.from),
  477. hideKeyboard: readOnly,
  478. lastMsgTime: lastMsgTime);
  479. }
  480. return _wrapScrollTag(index: index, child: result);
  481. }
  482. quiteGroup() {
  483. var function = () {
  484. GroupInfoMgr().deleteGroup(widget.groupInfoModel.sessionId);
  485. MessageMgr().emit('Quit Group', widget.groupInfoModel.sessionId);
  486. Navigator.of(context).popUntil(ModalRoute.withName('/main'));
  487. MsgHandler.quitGroup(widget.groupInfoModel.sessionId);
  488. };
  489. showModalBottomSheet(
  490. context: context,
  491. backgroundColor: Colors.transparent,
  492. builder: (BuildContext context) {
  493. return Container(
  494. decoration: BoxDecoration(
  495. color: Colors.white,
  496. borderRadius: BorderRadius.only(
  497. topLeft: Radius.circular(13), topRight: Radius.circular(13))),
  498. height: 225,
  499. child: Column(
  500. crossAxisAlignment: CrossAxisAlignment.center,
  501. mainAxisAlignment: MainAxisAlignment.center,
  502. children: <Widget>[
  503. Padding(
  504. padding: EdgeInsets.fromLTRB(15, 15, 15, 13),
  505. child: Text(
  506. I18n.of(context).quit_group_tips,
  507. textScaleFactor: 1.0,
  508. style: TextStyle(fontSize: 12, color: Color(0xff777777)),
  509. ),
  510. ),
  511. Divider(
  512. color: Color(0xffE5E5E5),
  513. ),
  514. InkWell(
  515. onTap: function,
  516. child: Container(
  517. height: 60,
  518. alignment: Alignment.center,
  519. child: Text(I18n.of(context).determine,
  520. textScaleFactor: 1.0,
  521. style: TextStyle(
  522. fontSize: 18, color: Constants.ConfrimButtonColor)),
  523. ),
  524. ),
  525. Container(
  526. color: Color(0xffF2F2F2),
  527. height: 4,
  528. ),
  529. InkWell(
  530. onTap: () {
  531. Navigator.of(context).pop();
  532. },
  533. child: Container(
  534. height: 60,
  535. alignment: Alignment.center,
  536. child: Text(I18n.of(context).cancel,
  537. textScaleFactor: 1.0,
  538. style: TextStyle(fontSize: 18, color: Color(0xff4B4B4B))),
  539. ),
  540. )
  541. ],
  542. ),
  543. );
  544. },
  545. );
  546. }
  547. }