Hibok
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

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