import 'dart:convert'; import 'dart:io'; import 'package:chat/chat/group_chat_item.dart'; import 'package:chat/data/UserData.dart'; import 'package:chat/data/chat_data_mgr.dart'; import 'package:chat/data/constants.dart'; import 'package:chat/data/group_data_mgr.dart'; import 'package:chat/generated/i18n.dart'; import 'package:chat/home/group_setting.dart'; import 'package:chat/models/ChatMsg.dart'; import 'package:chat/models/group_info_model.dart'; import 'package:chat/models/keyboard_provider.dart'; import 'package:chat/models/money_change.dart'; import 'package:chat/models/voucher_change.dart'; import 'package:chat/proto/all.pbserver.dart'; import 'package:chat/utils/CustomUI.dart'; import 'package:chat/utils/MessageMgr.dart'; import 'package:chat/utils/analyze_utils.dart'; import 'package:chat/utils/msgHandler.dart'; import 'package:chat/utils/net_state_widget.dart'; import 'package:chat/utils/screen.dart'; import 'package:chat/utils/sound_util.dart'; import 'package:chat/utils/sp_utils.dart'; import 'package:chat/utils/sql_util.dart'; import 'package:chat/utils/upload_util.dart'; import 'package:extended_text/extended_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:oktoast/oktoast.dart'; import 'package:provider/provider.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../r.dart'; import 'input_bar.dart'; import 'package:chat/models/ref_name_provider.dart'; import 'package:fixnum/fixnum.dart'; class GroupChatPage extends StatefulWidget { final GroupInfoModel groupInfoModel; final int enterType; // 0默认 1图片 final dynamic enterContent; GroupChatPage( {Key key, this.groupInfoModel, this.enterType = 0, this.enterContent}) : super(key: key); _GroupChatPageState createState() => _GroupChatPageState(); } class _GroupChatPageState extends State { final ItemScrollController itemScrollController = ItemScrollController(); final ItemPositionsListener itemPositionListener = ItemPositionsListener.create(); MessageMgr msgMgr = MessageMgr(); List msgList; KeyboardIndexProvider _keyboardIndexProvider = KeyboardIndexProvider(); TextEditingController nickNameController = new TextEditingController(); //统计聊天时长 int startTime; //子元素的对应偏移量 Map itemOffsetMap = {}; //未读消息数目 int unreadNums = 0; //最上一条未读消息id int unreadTime; //@消息索引 int alterTime; String alterUserName = ''; @override void dispose() { var endTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; AnalyzeUtils.commitChatDuration(startTime, endTime); msgMgr.off('New Chat Message', receiveMsg); msgMgr.off('Keyboard Hide', dealWithKeyboardHide); msgMgr.off('Update Group Info', updateGroupInfo); msgMgr.off('Delete Select Message', _deleteItem); msgMgr.off('Jump to Msg', jumpToMsg); MsgHandler.curActiveSession = 0; SoundUtils().stop(); super.dispose(); } @override void initState() { super.initState(); print('init group chat page ${widget.groupInfoModel.sessionId}'); getDefaultSetting(); startTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; unreadNums = ChatDataMgr() .groupUnreadProvider .getUnreadCount(widget.groupInfoModel.sessionId); alterTime = ChatDataMgr() .groupUnreadProvider .getHavaAltertime(widget.groupInfoModel.sessionId); MsgHandler.updateActiveSesstion(widget.groupInfoModel.sessionId, isGroup: true); msgList = ChatDataMgr().getGroupRecord(); if (unreadNums >= 10) { unreadTime = msgList[unreadNums - 1].time; } for (int i = 0; i < msgList.length; i++) { if (msgList[i].time == alterTime) { alterUserName = widget.groupInfoModel.getMember(msgList[i].friendId).nickName; break; } } msgMgr.on('New Chat Message', receiveMsg); msgMgr.on('Keyboard Hide', dealWithKeyboardHide); msgMgr.on('Update Group Info', updateGroupInfo); msgMgr.on('Delete Select Message', _deleteItem); msgMgr.on('Jump to Msg', jumpToMsg); WidgetsBinding.instance.addPostFrameCallback((_) { if (widget.enterType == 1) { print('接收到的:${widget.enterContent}'); _sendFile(File(widget.enterContent)); } else if (widget.enterType == 2) { //转发消息 MsgModel originMsg = widget.enterContent; MsgModel msg = MsgHandler.createSendMsg( ChatType.valueOf(originMsg.msgType), originMsg.msgContent, channelType: ChatChannelType.Group); msg.extraInfo = originMsg.extraInfo; // msg.extraFile = originMsg.extraFile; if (originMsg.extraFile == null || originMsg.extraFile.contains('http')) { msg.extraFile = originMsg.extraFile; } else { msg.extraFile = UploadUtil().getFullUrl( originMsg.extraFile, originMsg.sessionId, originMsg.channelType); } msg.localFile = originMsg.localFile; if (msg.localFile != null) { msg.state = MsgState.Uploaded; } sendMsg(msg); } }); } jumpToMsg(time) { var screenItemNums = itemPositionListener.itemPositions.value.length; int jumIndex = 0; for (int i = 0; i < msgList.length; i++) { if (time == msgList[i].time) { jumIndex = i - screenItemNums + 2; break; } } if (jumIndex < 0) { jumIndex = 0; } if (jumIndex > msgList.length - 1) { jumIndex = msgList.length - 1; } itemScrollController.jumpTo(index: jumIndex); } void _sendFile(File file) async { // File file = await FilePicker.getFile(); int fileSize = file.lengthSync(); print('选择的文件 ${file.path} 大小 $fileSize'); if (fileSize > 33 * 1024 * 1024) { showToast('文件大于33M'); return; } var fileName = file.path.split('/').last; print('fileName $fileName'); var ext = ''; var extList = fileName.split('.'); if (extList.length > 1) { ext = extList.last; } print('ext $ext'); var fileMsg = FileChat.create(); fileMsg.type = ext; fileMsg.size = fileSize; fileMsg.name = fileName; var msg = MsgHandler.createSendMsg( ChatType.FileChatType, fileMsg.writeToBuffer(), friendId: 0, localFile: file.path, channelType: ChatChannelType.Group); sendMsg(msg); } updateGroupInfo(args) { print('更新群信息'); if (mounted) { setState(() {}); } } void getDefaultSetting() async { bool soundPlayMode = (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false; _keyboardIndexProvider.init(soundPlayMode); } dealWithKeyboardHide(args) { if (_keyboardIndexProvider.curKeyboardIndex == 0) { readOnly(); } } @override Widget build(BuildContext context) { List actions = []; actions.add(Row( children: [ CustomUI.buildImageLabel("assets/images/voucher.png", Provider.of(context).voucher, imgOpc: 0.5, imgHeight: 13), CustomUI.buildImageLabel( R.assetsImagesCoin, Provider.of(context).money, isLeft: false) ], )); actions.add(widget.groupInfoModel.isInGroup ? IconButton( icon: Icon(Icons.more_horiz), iconSize: 22, onPressed: () { //进入群管理界面 hideKeyBoard(); Navigator.of(context).push( new MaterialPageRoute( builder: (context) { return GroupSetting( groupInfoModel: widget.groupInfoModel, ); }, ), ); }, ) : IconButton( icon: Icon(Icons.delete), iconSize: 22, color: Colors.redAccent, onPressed: () { //进入群管理界面 quiteGroup(); }, )); Map refMap = Provider.of(context).refMap; bool isHaveUnreadNews = unreadTime != null; bool isAlterYou = alterTime != null; return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => _keyboardIndexProvider), Provider.value(value: true), Provider.value(value: widget.groupInfoModel), ], child: GestureDetector( onTap: hideKeyBoard, child: ExtendedTextSelectionPointerHandler( ///选择文字,消除弹窗 builder: (states) { return Listener( child: Scaffold( resizeToAvoidBottomInset: false, backgroundColor: const Color(0xFFE2E9F1), appBar: AppBar( backgroundColor: AppColors.NewAppbarBgColor, title: Text( widget.groupInfoModel.getGroupName(refMap), textScaleFactor: 1.0, style: TextStyle( color: Constants.BlackTextColor, fontSize: 16.47), ), leading: CustomUI.buildCustomLeading(context), titleSpacing: -10, elevation: 1, centerTitle: false, actions: actions), body: SafeArea( child: Stack( children: [ Column( children: [ NetStateWidget(), Expanded(child: _buildMessageList()), InputBar(sendMsg: sendMsg), ], ), isHaveUnreadNews ? Positioned( top: 32.5, right: 0, child: InkWell( onTap: () { jumpToMsg(isAlterYou ? alterTime : unreadTime); unreadTime = null; setState(() {}); }, child: Container( alignment: Alignment.center, decoration: BoxDecoration( boxShadow: [ BoxShadow( color: Color(0x33000000), //阴影颜色 blurRadius: 10.0, //阴影大小 ) ], borderRadius: BorderRadius.only( topLeft: Radius.circular(80), bottomLeft: Radius.circular(80)), color: Colors.white, ), constraints: BoxConstraints(minWidth: 120), height: 39, child: Row( children: [ Icon( Icons.arrow_upward, color: Color(0xFF3875E9), size: 20, ), Text( isAlterYou ? '$alterUserName @你' : '$unreadNums条新消息', style: TextStyle( color: Color(0xFF3875E9)), ) ], ), ))) : Container() ], ))), onPointerDown: (value) { for (var state in states) { if (!state.containsPosition(value.position)) { //clear other selection state.clearSelection(); } } }, onPointerMove: (value) { //clear other selection for (var state in states) { if (!state.containsPosition(value.position)) { //clear other selection state.clearSelection(); } } }, ); }, ))); } //更新各个子元素的偏移位置 _updateMsgItemOffset() { if (msgList.length == 0) { return; } var myId = UserData().basicInfo.userId; double offset = 0; for (var i = 0; i < msgList.length; i++) { MsgModel msg = msgList[i]; double itemHeight = 70; switch (ChatType.valueOf(msg.msgType)) { case ChatType.TextChatType: //if (msg.from == myId) { var text = utf8.decode(msg.msgContent); itemHeight = 21 + _getTextHeight(text); //} else {} break; case ChatType.ShortVoiceChatType: if (msg.from == myId) { itemHeight = 22.5 + 24; } else {} break; case ChatType.ImageChatType: itemHeight = _getImgHeight(msg); break; case ChatType.ShortVideoChatType: itemHeight = _getImgHeight(msg); break; case ChatType.EmoticonType: itemHeight = 40; break; case ChatType.RedWalletChatType: print('红包消息'); itemHeight = 70; break; case ChatType.PlaceChatType: itemHeight = 100 + 40.0; break; case ChatType.GroupChatNoticeType: itemHeight = 40; break; case ChatType.GiftChatType: itemHeight = 40; break; case ChatType.FileChatType: itemHeight = 80; break; default: } itemOffsetMap[i] = offset; offset += itemHeight; } } double _getTextHeight(String text) { var tp = TextPainter( text: TextSpan(style: TextStyle(fontSize: 15), text: text), textAlign: TextAlign.left, textDirection: TextDirection.ltr, textScaleFactor: 1, ); tp.layout(maxWidth: Screen.width - 140); return tp.height; } double _getImgHeight(MsgModel msg) { double aspectRatio = msg.extraInfo / 100; var maxWidth = Screen.width * 0.65; var maxHeight = Screen.height / 4; double height; if (maxWidth / maxHeight > aspectRatio) { height = maxHeight; } else { height = maxWidth / aspectRatio; } return height; } Widget _buildMessageList() { return Container( alignment: Alignment.topCenter, child: msgList.length == 0 ? Padding( padding: EdgeInsets.all(10), child: Text( I18n.of(context).chat_tips, textAlign: TextAlign.center, textScaleFactor: 1.0, style: TextStyle(color: Colors.grey), )) : Scrollbar( child: ScrollablePositionedList.builder( physics: new ClampingScrollPhysics(), itemCount: msgList.length, itemBuilder: _buildItem, itemScrollController: itemScrollController, itemPositionsListener: itemPositionListener, padding: EdgeInsets.all(8.0), reverse: true, hitCallback: hideKeyBoard, )), ); } hideKeyBoard() { _keyboardIndexProvider.changeSelectIndex(-1); } readOnly() { _keyboardIndexProvider.changeReadOnlyKey(true); } sendMsg(MsgModel msg) { if (!widget.groupInfoModel.isInGroup) { //如果不在该群 showToast(I18n.of(context).not_in_group); return; } MsgHandler.insertMsgToDB(msg); MsgHandler.sendChatMsg(msg); if (mounted) { setState(() {}); } itemScrollController.jumpTo(index: 0); } MsgModel msg; int count = 0; testBig(MsgModel msg) async { for (int k = 0; k < 100; k++) { msg.msgContent = utf8.encode('测试$count'); Int64 time = Int64((DateTime.now()).millisecondsSinceEpoch); msg.time = time.toInt(); MsgHandler.insertMsgToDB(msg); MsgHandler.sendChatMsg(msg); await Future.delayed(Duration(milliseconds: 300), () {}); count++; } count = 0; print('攻击完毕'); showToast('攻击完毕'); } void receiveMsg(args) { print("msgList.length: ${msgList.length}"); if (args != widget.groupInfoModel.sessionId) { return; } if (mounted) { setState(() {}); } } _deleteItem(msg) { MessageMgr().emit('Cancel Request', msg); print('#### 开始删除--'); msgList.remove(msg); setState(() {}); SqlUtil().deleteSigleRecordWith(msg.sessionId, msg.time); } Widget _buildItem(BuildContext context, int index) { var lastMsgTime; if (index < msgList.length - 1) { lastMsgTime = msgList[index].time; } MsgModel msg = msgList[index]; if (msg.from == 0) { return GroupChatPageItem( key: Key(msg.time.toString()), msg: msg, lastMsgTime: lastMsgTime); } else { return GroupChatPageItem( key: Key(msg.time.toString()), msg: msg, memberModel: widget.groupInfoModel.getMember(msg.from), hideKeyboard: readOnly, lastMsgTime: lastMsgTime); } } quiteGroup() { var function = () { GroupInfoMgr().deleteGroup(widget.groupInfoModel.sessionId); MessageMgr().emit('Quit Group', widget.groupInfoModel.sessionId); Navigator.of(context).popUntil(ModalRoute.withName('/main')); MsgHandler.quitGroup(widget.groupInfoModel.sessionId); }; showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (BuildContext context) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(13), topRight: Radius.circular(13))), height: 225, child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: EdgeInsets.fromLTRB(15, 15, 15, 13), child: Text( I18n.of(context).quit_group_tips, textScaleFactor: 1.0, style: TextStyle(fontSize: 12, color: Color(0xff777777)), ), ), Divider( color: Color(0xffE5E5E5), ), InkWell( onTap: function, child: Container( height: 60, alignment: Alignment.center, child: Text(I18n.of(context).determine, textScaleFactor: 1.0, style: TextStyle( fontSize: 18, color: Constants.ConfrimButtonColor)), ), ), Container( color: Color(0xffF2F2F2), height: 4, ), InkWell( onTap: () { Navigator.of(context).pop(); }, child: Container( height: 60, alignment: Alignment.center, child: Text(I18n.of(context).cancel, textScaleFactor: 1.0, style: TextStyle(fontSize: 18, color: Color(0xff4B4B4B))), ), ) ], ), ); }, ); } }