Hibok
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

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