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

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