import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:chat/chat/translate_state.dart'; import 'package:chat/data/UserData.dart'; import 'package:chat/data/chat_data_mgr.dart'; import 'package:chat/data/constants.dart'; import 'package:chat/generated/i18n.dart'; import 'package:chat/models/ChatMsg.dart'; import 'package:chat/models/UserInfo.dart'; import 'package:chat/models/keyboard_provider.dart'; import 'package:chat/models/ref_name_provider.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/HttpUtil.dart'; import 'package:chat/utils/MessageMgr.dart'; import 'package:chat/utils/TokenMgr.dart'; import 'package:chat/utils/analyze_utils.dart'; import 'package:chat/utils/app_navigator.dart'; import 'package:chat/utils/blacklist_mgr.dart'; import 'package:chat/utils/count_down_button.dart'; import 'package:chat/utils/friend_list_mgr.dart'; import 'package:chat/utils/msgHandler.dart'; import 'package:chat/utils/net_state_widget.dart'; import 'package:chat/utils/sound_util.dart'; import 'package:chat/utils/sp_utils.dart'; import 'package:chat/utils/sql_util.dart'; import 'package:dio/dio.dart'; import 'package:extended_text/extended_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:oktoast/oktoast.dart'; import 'package:provider/provider.dart'; import '../r.dart'; import 'ChatPageItem.dart'; import 'input_bar.dart'; import 'package:chat/utils/PopUpMenu.dart' as myPop; import 'package:chat/models/money_change.dart'; class ChatPage extends StatefulWidget { final int friendId; final int enterType; // 0默认 1图片 final dynamic enterContent; final bool isTranslateButler; ChatPage({Key key, this.friendId, this.enterType = 0, this.enterContent,this.isTranslateButler=false}) : super(key: key); _ChatPageState createState() => _ChatPageState(); } class _ChatPageState extends State { ScrollController _scrollCtrl = ScrollController(); MessageMgr msgMgr = MessageMgr(); UserInfo friendInfo; List msgList; KeyboardIndexProvider _keyboardIndexProvider = KeyboardIndexProvider(); TextEditingController nickNameController = new TextEditingController(); //统计聊天时长 int startTime; @override void dispose() { var endTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; AnalyzeUtils.commitChatDuration(startTime, endTime); MessageMgr().off('Send CoinBag', _sendCoin); msgMgr.off('New Chat Message', receiveMsg); msgMgr.off('Keyboard Hide', dealWithKeyboardHide); msgMgr.off('Delete Select Message', _deleteItem); MsgHandler.curActiveSession = 0; SoundUtils().stop(); nickNameController.dispose(); _scrollCtrl.dispose(); super.dispose(); } @override void initState() { super.initState(); print('init chatpage'); getDefaultSetting(); getUserInfo(); startTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; msgList = ChatDataMgr().getRecord(); msgMgr.on('New Chat Message', receiveMsg); msgMgr.on('Keyboard Hide', dealWithKeyboardHide); msgMgr.on('Send CoinBag', _sendCoin); msgMgr.on('Delete Select Message', _deleteItem); } 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: widget.friendId, localFile: file.path, channelType: ChatChannelType.Session); sendMsg(msg); } _sendCoin(args) async { var res = await _checkCoinBeforeSend(args['amount']); if (res != null) { args['redNo'] = res['redNo']; args['friendId'] = widget.friendId; var msg = MsgHandler.createCoinBagMsg(args); sendMsg(msg); } } //校验 _checkCoinBeforeSend(int amount) async { Map data = { "userId": UserData().basicInfo.userId, "rUserId": friendInfo.userId, "price": amount, }; data['sign'] = TokenMgr().getSign(data); Response res = await HttpUtil().post('red/packet/send', data: data); if (res == null) { return null; } Map resData = res.data; if (resData['code'] == 0) { print(resData['data']); return resData['data']; } return null; } void getDefaultSetting() async { bool soundPlayMode = (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false; _keyboardIndexProvider.init(soundPlayMode); } dealWithKeyboardHide(args) { if (_keyboardIndexProvider.curKeyboardIndex == 0) { hideKeyBoard(); } } getUserInfo() async { //先从本地获取用户信息 friendInfo = await HttpUtil().getFriendInfo(widget.friendId, true); //如果是新的聊天,向服务器发送创建会话消息 if (msgList.length == 0) { MsgHandler.getActiveSesstion( [friendInfo.userId, UserData().basicInfo.userId]); } if (mounted) { setState(() {}); } 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); msg.extraInfo = originMsg.extraInfo; msg.extraFile = originMsg.extraFile; msg.localFile = originMsg.localFile; msg.friendId = widget.friendId; if (msg.localFile != null) { msg.state = MsgState.Uploaded; } sendMsg(msg); } }); } @override Widget build(BuildContext context) { print('build chatpage'); if (friendInfo == null) { return Scaffold( backgroundColor: const Color(0xFFE2E9F1), body: SafeArea( child: Center( child: CircularProgressIndicator(), ), ), ); } List actions = []; int voucher = Provider.of(context).voucher; actions.add(Row( children: [ CustomUI.buildImageLabel("assets/images/voucher.png", voucher, imgOpc: 0.5, imgHeight: 13), CustomUI.buildImageLabel( R.assetsImagesCoin, Provider.of(context).money, isLeft: false) ], )); actions.add(TranslateSateWidget(friendId: friendInfo.userId)); actions.add(Container( margin: EdgeInsets.only(top: 1), child: myPop.PopupMenuButton( icon: Icon( Icons.more_horiz, size: 24, ), offset: Offset(0, 100), onSelected: (int index) { if (index == 2) { AppNavigator.pushInformUserPage( context, friendInfo.sex == 1, friendInfo.userId); } else if (index == 1) { nickNameController.text = Provider.of(context) .getRefName(friendInfo.userId, friendInfo.nickName); var confirm = CustomUI.buildConfirmBotton( I18n.of(context).determine, () async { nickNameController.text = nickNameController.text.trim(); if (nickNameController.text == null || nickNameController.text.length > 25) { showToast(I18n.of(context).only1_8); return; } Provider.of(context).changeRefName( friendInfo.userId, nickNameController.text, () { Navigator.of(context).pop(); }); }); var tip = Column( children: [ Container( margin: EdgeInsets.only(top: 20), child: Text( I18n.of(context).setRemark, textScaleFactor: 1.0, style: TextStyle( color: Constants.BlackTextColor, fontSize: 16), ), ), Container( margin: EdgeInsets.only(top: 23, bottom: 25), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.all(Radius.circular(8))), child: TextField( keyboardAppearance: Brightness.light, controller: nickNameController, textAlign: TextAlign.center, textInputAction: TextInputAction.search, style: TextStyle( textBaseline: TextBaseline.alphabetic, fontSize: 14), decoration: InputDecoration( hintText: friendInfo.nickName, hintStyle: TextStyle(fontSize: 12), filled: true, contentPadding: EdgeInsets.only(top: 10, bottom: 10), fillColor: Colors.transparent, border: InputBorder.none, ), maxLines: 1, inputFormatters: [LengthLimitingTextInputFormatter(15)], ), ) ], ); var content = CustomUI.buildConfirmContent(tip, confirm); CustomUI.buildTip(context, '', content); } }, itemBuilder: (BuildContext context) { return >[ myPop.PopupMenuItem( child: Container( alignment: Alignment.center, color: Colors.white, padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10), child: Text(I18n.of(context).anonymous_report, textScaleFactor: 1.0, maxLines: 1, style: TextStyle( color: Color(AppColors.AppBarColor), fontSize: 12)), ), value: 2, ), myPop.PopupMenuItem( child: Container( decoration: BoxDecoration( border: Border( top: BorderSide(width: 1, color: Colors.grey[300])), color: Colors.white, ), alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10), child: Text(I18n.of(context).Remark, textScaleFactor: 1.0, maxLines: 1, style: TextStyle( color: Color(AppColors.AppBarColor), fontSize: 12)), ), value: 1, ), ]; }))); return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => _keyboardIndexProvider), Provider.value(value: false), Provider.value(value: widget.friendId), ], child: GestureDetector( onTap: hideKeyBoard, child: ExtendedTextSelectionPointerHandler( ///选择文字,消除弹窗 builder: (states) { return Listener( child: Scaffold( resizeToAvoidBottomInset: false, backgroundColor: const Color(0xFFE2E9F1), appBar: AppBar( title: Text( '${Provider.of(context).getRefName(friendInfo.userId, friendInfo.nickName)}', textScaleFactor: 1.0, style: TextStyle( color: Constants.BlackTextColor, fontSize: 16.47), ), leading: CustomUI.buildCustomLeading(context), titleSpacing: -10, centerTitle: false, elevation: 1, actions: actions), body: SafeArea( child: Column( children: [ NetStateWidget(), widget.isTranslateButler?_buildTranslationButler():Container(), widget.isTranslateButler? _buildServiceCard(true,(){}):Container(), widget.isTranslateButler? _buildServiceCard(false,(){}):Container(), Expanded(child: _buildMessageList()), InputBar(sendMsg: sendMsg), ], ))), behavior: HitTestBehavior.translucent, 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(); } } }, ); }, ))); } Widget _buildTranslationButler() { bool hasHeadImg = true; if (friendInfo.headimgurl == null || friendInfo.headimgurl.length == 0) { hasHeadImg = false; } /// 5H币/1分钟 String coinTIme = I18n.of(context).translation_butler_coin_time; coinTIme = coinTIme.replaceAll('/s1', '5'); coinTIme = coinTIme.replaceAll('/s2', '1'); return Container( padding: EdgeInsets.fromLTRB(10, 10, 10, 10), color: Colors.white, child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: hasHeadImg ? CachedNetworkImage( imageUrl: friendInfo.headimgurl, placeholder: (context, url) => Image.asset( Constants.DefaultHeadImgUrl, width: 54, height: 54, ), width: 54, height: 54, ) : SizedBox( width: 54, height: 54, child: Image.asset(R.assetsImagesDefaultNorAvatar))), Padding( padding: EdgeInsets.only(left: 10), child: Column( children: [ Text( I18n.of(context).translation_butler, textScaleFactor: 1.0, style: TextStyle( color: AppColors.NewAppbarTextColor, fontSize: 15), ), Text( coinTIme, textScaleFactor: 1.0, style: TextStyle( color: AppColors.NewAppbarTextColor, fontSize: 13), ) ], ), ), Expanded( child: Container( width: double.maxFinite, child: CountDownButton( I18n.of(context).translation_butler_end_service, () { Navigator.of(context).pop(); }, countDownTime: 60, align: Alignment.centerRight, onPress: (){}, ), // alignment: Alignment(1,0), )), ], ), ); } Widget _buildServiceCard(bool isStart, Function callBack) { return Container( margin: EdgeInsets.all(10), child: Card( elevation: 5, // 阴影 shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), // side: BorderSide(color: Colors.green,width: 25), ), child: Padding(padding: EdgeInsets.only(left: 10,right: 10,top: 15,bottom: 15),child: Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isStart?I18n.of(context).translation_butler_start_service:I18n.of(context).translation_butler_service_end, textScaleFactor: 1.0, style: TextStyle( color: AppColors.NewAppbarTextColor, fontSize: 15), ), Text( isStart?I18n.of(context).translation_butler_start_tips:I18n.of(context).translation_butler_evaluation_tips, textScaleFactor: 1.0, style: TextStyle( color: AppColors.NewAppbarTextColor, fontSize: 13), ) ], ), isStart?Container():Expanded(child: Container( margin: EdgeInsets.only(left: 15 ), height: 30, child: RaisedButton( color: Color(0xff3875E9), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10))), child: Text( I18n.of(context).translation_butler_evaluation , textScaleFactor: 1.0, style: TextStyle(color: Colors.white, fontSize: 19), ), onPressed:(){ CustomUI.buildTranslationEvaluationDialog(context); } ), )), ], ),), ), ); } Widget _buildMessageList() { return Container( alignment: Alignment.topCenter, child: msgList.length == 0 ? Padding( padding: EdgeInsets.all(8), child: Text( I18n.of(context).chat_tips, textAlign: TextAlign.center, textScaleFactor: 1.0, style: TextStyle(color: Colors.grey, fontSize: 12), )) : NotificationListener( child: Scrollbar( child: ListView.builder( reverse: true, shrinkWrap: true, itemCount: msgList.length, controller: _scrollCtrl, padding: EdgeInsets.all(8.0), itemBuilder: _buildItem, )), onNotification: (notification) { if (notification is ScrollNotification) { // var offset = notification.metrics.pixels; // print('滚动事件 offset $offset'); } return true; }, ), ); } hideKeyBoard() { _keyboardIndexProvider.changeSelectIndex(-1); } readOnly() { _keyboardIndexProvider.changeReadOnlyKey(true); } sendMsg(MsgModel msg) { print('对方是否拉黑你 ${friendInfo.isBlackened}'); if (BlacklistMgr.isBlack(friendInfo.userId)) { return; } if (!friendInfo.isCanStrangerNews && !FriendListMgr().isMyFriend(friendInfo.userId)) { showToast(I18n.of(context).stranger_close_tips); return; } MsgHandler.insertMsgToDB(msg); MsgHandler.sendChatMsg(msg); if (mounted) { setState(() {}); if (_scrollCtrl.hasClients) { _scrollCtrl.animateTo(0, duration: new Duration(milliseconds: 500), curve: Curves.ease); } } } void receiveMsg(args) { if (mounted) { setState(() {}); if (_scrollCtrl.hasClients) { _scrollCtrl.animateTo(0, duration: new Duration(milliseconds: 500), curve: Curves.ease); } } } _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 + 1].time; } MsgModel msg = msgList[index]; return ChatPageItem( key: Key(msg.time.toString()), msg: msg, hideKeyboard: readOnly, friendInfo: friendInfo, lastMsgTime: lastMsgTime); } }