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.
 
 
 
 
 
 

517 lines
16 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:chat/chat/group_chat_item.dart';
  4. import 'package:chat/data/UserData.dart';
  5. import 'package:chat/data/chat_data_mgr.dart';
  6. import 'package:chat/data/constants.dart';
  7. import 'package:chat/data/group_data_mgr.dart';
  8. import 'package:chat/generated/i18n.dart';
  9. import 'package:chat/home/group_setting.dart';
  10. import 'package:chat/models/ChatMsg.dart';
  11. import 'package:chat/models/group_info_model.dart';
  12. import 'package:chat/models/keyboard_provider.dart';
  13. import 'package:chat/models/money_change.dart';
  14. import 'package:chat/models/voucher_change.dart';
  15. import 'package:chat/proto/all.pbserver.dart';
  16. import 'package:chat/utils/CustomUI.dart';
  17. import 'package:chat/utils/MessageMgr.dart';
  18. import 'package:chat/utils/analyze_utils.dart';
  19. import 'package:chat/utils/msgHandler.dart';
  20. import 'package:chat/utils/net_state_widget.dart';
  21. import 'package:chat/utils/screen.dart';
  22. import 'package:chat/utils/sound_util.dart';
  23. import 'package:chat/utils/sp_utils.dart';
  24. import 'package:chat/utils/sql_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 '../r.dart';
  31. import 'input_bar.dart';
  32. import 'package:chat/models/ref_name_provider.dart';
  33. class GroupChatPage extends StatefulWidget {
  34. final GroupInfoModel groupInfoModel;
  35. final int enterType; // 0默认 1图片
  36. final dynamic enterContent;
  37. GroupChatPage(
  38. {Key key, this.groupInfoModel, this.enterType = 0, this.enterContent})
  39. : super(key: key);
  40. _GroupChatPageState createState() => _GroupChatPageState();
  41. }
  42. class _GroupChatPageState extends State<GroupChatPage> {
  43. ScrollController _scrollCtrl = ScrollController();
  44. MessageMgr msgMgr = MessageMgr();
  45. List<MsgModel> msgList;
  46. KeyboardIndexProvider _keyboardIndexProvider = KeyboardIndexProvider();
  47. TextEditingController nickNameController = new TextEditingController();
  48. //统计聊天时长
  49. int startTime;
  50. //子元素的对应偏移量
  51. Map itemOffsetMap = {};
  52. @override
  53. void dispose() {
  54. var endTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  55. AnalyzeUtils.commitChatDuration(startTime, endTime);
  56. msgMgr.off('New Chat Message', receiveMsg);
  57. msgMgr.off('Keyboard Hide', dealWithKeyboardHide);
  58. msgMgr.off('Update Group Info', updateGroupInfo);
  59. msgMgr.off('Delete Select Message', _deleteItem);
  60. MsgHandler.curActiveSession = 0;
  61. SoundUtils().stop();
  62. _scrollCtrl.dispose();
  63. super.dispose();
  64. }
  65. @override
  66. void initState() {
  67. super.initState();
  68. print('init group chat page ${widget.groupInfoModel.sessionId}');
  69. getDefaultSetting();
  70. startTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  71. MsgHandler.updateActiveSesstion(widget.groupInfoModel.sessionId,
  72. isGroup: true);
  73. msgList = ChatDataMgr().getGroupRecord();
  74. for(int k=0;k<msgList.length;k++){
  75. MsgModel msg = msgList[k];
  76. print('msgList ${msg.msgType} ${msg.from}' );
  77. }
  78. msgMgr.on('New Chat Message', receiveMsg);
  79. msgMgr.on('Keyboard Hide', dealWithKeyboardHide);
  80. msgMgr.on('Update Group Info', updateGroupInfo);
  81. msgMgr.on('Delete Select Message', _deleteItem);
  82. WidgetsBinding.instance.addPostFrameCallback((_) {
  83. if (widget.enterType == 1) {
  84. print('接收到的:${widget.enterContent}');
  85. _sendFile(File(widget.enterContent));
  86. }
  87. });
  88. }
  89. void _sendFile( File file) async {
  90. // File file = await FilePicker.getFile();
  91. int fileSize = file.lengthSync();
  92. print('选择的文件 ${file.path} 大小 $fileSize');
  93. if (fileSize > 33 * 1024 * 1024) {
  94. showToast('文件大于33M');
  95. return;
  96. }
  97. var fileName = file.path.split('/').last;
  98. print('fileName $fileName');
  99. var ext = '';
  100. var extList = fileName.split('.');
  101. if (extList.length > 1) {
  102. ext = extList.last;
  103. }
  104. print('ext $ext');
  105. var fileMsg = FileChat.create();
  106. fileMsg.type = ext;
  107. fileMsg.size = fileSize;
  108. fileMsg.name = fileName;
  109. var msg = MsgHandler.createSendMsg(ChatType.FileChatType, fileMsg.writeToBuffer(),
  110. friendId: 0,
  111. localFile: file.path,
  112. channelType:
  113. ChatChannelType.Group);
  114. sendMsg(msg);
  115. }
  116. updateGroupInfo(args) {
  117. print('更新群信息');
  118. if (mounted) {
  119. setState(() {});
  120. }
  121. }
  122. void getDefaultSetting() async {
  123. bool soundPlayMode =
  124. (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false;
  125. _keyboardIndexProvider.init(soundPlayMode);
  126. }
  127. dealWithKeyboardHide(args) {
  128. if (_keyboardIndexProvider.curKeyboardIndex == 0) {
  129. readOnly();
  130. }
  131. }
  132. @override
  133. Widget build(BuildContext context) {
  134. List<Widget> actions = [];
  135. actions.add(Row(
  136. children: <Widget>[
  137. CustomUI.buildImageLabel("assets/images/voucher.png",
  138. Provider.of<VoucherChangeProvider>(context).voucher,
  139. imgOpc: 0.5, imgHeight: 13),
  140. CustomUI.buildImageLabel(
  141. R.assetsImagesCoin, Provider.of<MoneyChangeProvider>(context).money,
  142. isLeft: false)
  143. ],
  144. ));
  145. actions.add(widget.groupInfoModel.isInGroup
  146. ? IconButton(
  147. icon: Icon(Icons.more_horiz),
  148. iconSize: 22,
  149. onPressed: () {
  150. //进入群管理界面
  151. hideKeyBoard();
  152. Navigator.of(context).push(
  153. new MaterialPageRoute(
  154. builder: (context) {
  155. return GroupSetting(
  156. groupInfoModel: widget.groupInfoModel,
  157. );
  158. },
  159. ),
  160. );
  161. },
  162. )
  163. : IconButton(
  164. icon: Icon(Icons.delete),
  165. iconSize: 22,
  166. color: Colors.redAccent,
  167. onPressed: () {
  168. //进入群管理界面
  169. quiteGroup();
  170. },
  171. ));
  172. Map refMap = Provider.of<RefNameProvider>(context).refMap;
  173. return MultiProvider(
  174. providers: [
  175. ChangeNotifierProvider(create: (_) => _keyboardIndexProvider),
  176. Provider<bool>.value(value: true),
  177. Provider<GroupInfoModel>.value(value: widget.groupInfoModel),
  178. ],
  179. child: GestureDetector(
  180. onTap: hideKeyBoard,
  181. child: ExtendedTextSelectionPointerHandler(
  182. ///选择文字,消除弹窗
  183. builder: (states) {
  184. return Listener(
  185. child: Scaffold(
  186. resizeToAvoidBottomInset: false,
  187. backgroundColor: const Color(0xFFE2E9F1),
  188. appBar: AppBar(
  189. backgroundColor: AppColors.NewAppbarBgColor,
  190. title: Text(
  191. widget.groupInfoModel.getGroupName(refMap),
  192. textScaleFactor: 1.0,
  193. style: TextStyle(
  194. color: Constants.BlackTextColor,
  195. fontSize: 16.47),
  196. ),
  197. leading: CustomUI.buildCustomLeading(context),
  198. titleSpacing: -10,
  199. elevation: 1,
  200. centerTitle: false,
  201. actions: actions),
  202. body: SafeArea(
  203. child: Column(
  204. children: <Widget>[
  205. NetStateWidget(),
  206. Expanded(child: _buildMessageList()),
  207. InputBar(sendMsg: sendMsg),
  208. ],
  209. ))),
  210. behavior: HitTestBehavior.translucent,
  211. onPointerDown: (value) {
  212. for (var state in states) {
  213. if (!state.containsPosition(value.position)) {
  214. //clear other selection
  215. state.clearSelection();
  216. }
  217. }
  218. },
  219. onPointerMove: (value) {
  220. //clear other selection
  221. for (var state in states) {
  222. if (!state.containsPosition(value.position)) {
  223. //clear other selection
  224. state.clearSelection();
  225. }
  226. }
  227. },
  228. );
  229. },
  230. )));
  231. }
  232. //更新各个子元素的偏移位置
  233. _updateMsgItemOffset() {
  234. if (msgList.length == 0) {
  235. return;
  236. }
  237. var myId = UserData().basicInfo.userId;
  238. double offset = 0;
  239. for (var i = 0; i < msgList.length; i++) {
  240. MsgModel msg = msgList[i];
  241. double itemHeight = 40;
  242. switch (ChatType.valueOf(msg.msgType)) {
  243. case ChatType.TextChatType:
  244. if (msg.from == myId) {
  245. var text = utf8.decode(msg.msgContent);
  246. itemHeight = 21 + _getTextHeight(text);
  247. } else {}
  248. break;
  249. case ChatType.ShortVoiceChatType:
  250. if (msg.from == myId) {
  251. itemHeight = 22.5 + 24;
  252. } else {}
  253. break;
  254. case ChatType.ImageChatType:
  255. itemHeight = _getImgHeight(msg);
  256. break;
  257. case ChatType.ShortVideoChatType:
  258. itemHeight = _getImgHeight(msg);
  259. break;
  260. case ChatType.EmoticonType:
  261. itemHeight = 40;
  262. break;
  263. case ChatType.RedWalletChatType:
  264. print('红包消息');
  265. itemHeight = 70;
  266. break;
  267. case ChatType.PlaceChatType:
  268. itemHeight = 100 + 40.0;
  269. break;
  270. case ChatType.GroupChatNoticeType:
  271. itemHeight = 40;
  272. break;
  273. case ChatType.GiftChatType:
  274. itemHeight = 40;
  275. break;
  276. case ChatType.FileChatType:
  277. itemHeight = 80;
  278. break;
  279. default:
  280. }
  281. itemOffsetMap[i] = offset;
  282. offset += itemHeight;
  283. }
  284. }
  285. double _getTextHeight(String text) {
  286. var tp = TextPainter(
  287. text: TextSpan(style: TextStyle(fontSize: 15), text: text),
  288. textAlign: TextAlign.left,
  289. textDirection: TextDirection.ltr,
  290. textScaleFactor: 1,
  291. );
  292. tp.layout(maxWidth: Screen.width - 140);
  293. return tp.height;
  294. }
  295. double _getImgHeight(MsgModel msg) {
  296. double aspectRatio = msg.extraInfo / 100;
  297. var maxWidth = Screen.width * 0.65;
  298. var maxHeight = Screen.height / 4;
  299. double height;
  300. if (maxWidth / maxHeight > aspectRatio) {
  301. height = maxHeight;
  302. } else {
  303. height = maxWidth / aspectRatio;
  304. }
  305. return height;
  306. }
  307. Widget _buildMessageList() {
  308. return Container(
  309. alignment: Alignment.topCenter,
  310. child: msgList.length == 0
  311. ? Padding(
  312. padding: EdgeInsets.all(10),
  313. child: Text(
  314. I18n.of(context).chat_tips,
  315. textAlign: TextAlign.center,
  316. textScaleFactor: 1.0,
  317. style: TextStyle(color: Colors.grey),
  318. ))
  319. : Scrollbar(child: ListView.builder(
  320. reverse: true,
  321. shrinkWrap: true,
  322. itemCount: msgList.length,
  323. controller: _scrollCtrl,
  324. padding: EdgeInsets.all(8.0),
  325. itemBuilder: _buildItem,
  326. )),
  327. );
  328. }
  329. hideKeyBoard() {
  330. _keyboardIndexProvider.changeSelectIndex(-1);
  331. }
  332. readOnly() {
  333. _keyboardIndexProvider.changeReadOnlyKey(true);
  334. }
  335. sendMsg(MsgModel msg) {
  336. if (!widget.groupInfoModel.isInGroup) {
  337. //如果不在该群
  338. showToast(I18n.of(context).not_in_group);
  339. return;
  340. }
  341. MsgHandler.insertMsgToDB(msg);
  342. MsgHandler.sendChatMsg(msg);
  343. if (mounted) {
  344. setState(() {});
  345. }
  346. if (_scrollCtrl.hasClients) {
  347. _scrollCtrl.animateTo(0,
  348. duration: new Duration(milliseconds: 500), curve: Curves.ease);
  349. }
  350. }
  351. void receiveMsg(args) {
  352. if (args != widget.groupInfoModel.sessionId) {
  353. return;
  354. }
  355. if (mounted) {
  356. setState(() {});
  357. if (_scrollCtrl.hasClients) {
  358. _scrollCtrl.animateTo(0,
  359. duration: new Duration(milliseconds: 500), curve: Curves.ease);
  360. }
  361. }
  362. }
  363. _deleteItem(msg) {
  364. MessageMgr().emit('Cancel Request', msg);
  365. print('#### 开始删除--');
  366. msgList.remove(msg);
  367. setState(() {});
  368. SqlUtil().deleteSigleRecordWith(msg.sessionId, msg.time);
  369. }
  370. Widget _buildItem(BuildContext context, int index) {
  371. var lastMsgTime;
  372. if (index < msgList.length - 1) {
  373. lastMsgTime = msgList[index + 1].time;
  374. }
  375. MsgModel msg = msgList[index];
  376. if (msg.from == 0) {
  377. return GroupChatPageItem(
  378. key: Key(msg.time.toString()), msg: msg, lastMsgTime: lastMsgTime);
  379. } else {
  380. return GroupChatPageItem(
  381. key: Key(msg.time.toString()),
  382. msg: msg,
  383. memberModel: widget.groupInfoModel.getMember(msg.from),
  384. hideKeyboard: readOnly,
  385. lastMsgTime: lastMsgTime);
  386. }
  387. }
  388. quiteGroup() {
  389. var function = () {
  390. GroupInfoMgr().deleteGroup(widget.groupInfoModel.sessionId);
  391. MessageMgr().emit('Quit Group', widget.groupInfoModel.sessionId);
  392. Navigator.of(context).popUntil(ModalRoute.withName('/main'));
  393. MsgHandler.quitGroup(widget.groupInfoModel.sessionId);
  394. };
  395. showModalBottomSheet(
  396. context: context,
  397. backgroundColor: Colors.transparent,
  398. builder: (BuildContext context) {
  399. return Container(
  400. decoration: BoxDecoration(
  401. color: Colors.white,
  402. borderRadius: BorderRadius.only(
  403. topLeft: Radius.circular(13), topRight: Radius.circular(13))),
  404. height: 225,
  405. child: Column(
  406. crossAxisAlignment: CrossAxisAlignment.center,
  407. mainAxisAlignment: MainAxisAlignment.center,
  408. children: <Widget>[
  409. Padding(
  410. padding: EdgeInsets.fromLTRB(15, 15, 15, 13),
  411. child: Text(
  412. I18n.of(context).quit_group_tips,
  413. style: TextStyle(fontSize: 12, color: Color(0xff777777)),
  414. ),
  415. ),
  416. Divider(
  417. color: Color(0xffE5E5E5),
  418. ),
  419. InkWell(
  420. onTap: function,
  421. child: Container(
  422. height: 60,
  423. alignment: Alignment.center,
  424. child: Text(I18n.of(context).determine,
  425. style: TextStyle(
  426. fontSize: 18, color: Constants.ConfrimButtonColor)),
  427. ),
  428. ),
  429. Container(
  430. color: Color(0xffF2F2F2),
  431. height: 4,
  432. ),
  433. InkWell(
  434. onTap: () {
  435. Navigator.of(context).pop();
  436. },
  437. child: Container(
  438. height: 60,
  439. alignment: Alignment.center,
  440. child: Text(I18n.of(context).cancel,
  441. style: TextStyle(fontSize: 18, color: Color(0xff4B4B4B))),
  442. ),
  443. )
  444. ],
  445. ),
  446. );
  447. },
  448. );
  449. }
  450. }