import 'dart:io'; import 'dart:ui'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/im/out/message.pb.dart'; import 'package:huixiang/main.dart'; import 'package:huixiang/retrofit/data/im_user_list.dart'; import 'package:huixiang/retrofit/retrofit_api.dart'; import 'package:huixiang/view_widget/my_appbar.dart'; import 'package:flutter/cupertino.dart'; import 'package:image_pickers/image_pickers.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../community/release_dynamic.dart'; import '../../generated/l10n.dart'; import '../../utils/font_weight.dart'; import 'im_view/on_chat_message.dart'; import 'im_view/on_chat_msg_instance.dart'; class ChatDetailsPage extends StatefulWidget { final Map arguments; ChatDetailsPage({this.arguments}); @override State createState() { return _ChatDetailsPage(); } } class _ChatDetailsPage extends State with WidgetsBindingObserver implements OnChatMessage { ApiService apiService; List mediaPaths = []; int selectCount = 9; int dynamicType = 0; final TextEditingController chatController = TextEditingController(); bool emojiShowing = false; double keyboard = -1; bool needShowSmiley = false; bool needHideSmiley = false; bool isKeyBoardShow = false; bool moreShow = false; bool needShowMore = false; bool needHideMore = false; var commentFocus = FocusNode(); String hintText = "输入消息内容..."; final OnChatMessage _tempOnChatMessage = OnChatMsgInstance.instance.onChatMessage; final ScrollController scrollController = ScrollController(); String tex = ""; int copyIndex = 0; String selfUserId = ""; ImUserList _toUser; @override void onMessage(txt) { // SmartDialog.showToast("聊天 $txt", alignment: Alignment.center); } List messages = []; loadMessageList() async { selfUserId = (await SharedPreferences.getInstance()).getString("userId"); messages = await hxDatabase.queryUList(_toUser.mid); socketClient.addCallback(_toUser.mid, (Message message) { messages.add(message); refreshState(); }); refreshState(); } refreshState() { if (mounted) setState(() {}); } @override void initState() { super.initState(); _toUser = widget.arguments["toUser"]; OnChatMsgInstance.instance.onChatMessage = this; WidgetsBinding.instance.addObserver(this); commentFocus.addListener(_focusNodeListener); loadMessageList(); Future.delayed(Duration.zero, () { jumpToBottom(); }); } void jumpToBottom() { scrollController.position.jumpTo(scrollController.position.maxScrollExtent); } void didChangeMetrics() { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; isKeyBoardShow = MediaQuery.of(context).viewInsets.bottom > 0; if (MediaQuery.of(context).viewInsets.bottom == 0) { if (isKeyBoardShow) { FocusScope.of(context).requestFocus(FocusNode()); if (mounted) if (!emojiShowing) if (!moreShow) setState(() { hintText = "请输入消息内容..."; isKeyBoardShow = false; }); } } else { if (mounted) setState(() { isKeyBoardShow = true; }); } }); if (needShowSmiley && window.viewInsets.bottom <= 0.1) { needShowSmiley = false; setState(() { emojiShowing = true; }); } if (needHideSmiley && window.viewInsets.bottom > 0.1) { needHideSmiley = false; setState(() { emojiShowing = false; }); } if (needShowMore && window.viewInsets.bottom <= 0.1) { needShowMore = false; setState(() { moreShow = true; }); } if (needHideMore && window.viewInsets.bottom > 0.1) { needHideMore = false; setState(() { moreShow = false; }); } } @override void dispose() { super.dispose(); OnChatMsgInstance.instance.onChatMessage = _tempOnChatMessage; WidgetsBinding.instance.removeObserver(this); commentFocus.removeListener(_focusNodeListener); socketClient.removeCallback(_toUser.mid); } void _focusNodeListener() { /*if (_focusNode.hasFocus || _focusNode.consumeKeyboardToken()){ setState(() { smileyPadGone = true; }); }*/ } ///输入框点击事件 _onTextFieldTap() { if (emojiShowing) { needHideSmiley = true; } if (moreShow) { needHideMore = true; } } ///表情包点击事件 _onSmileyTap() { if (!emojiShowing && commentFocus.hasFocus && isKeyBoardShow) { needShowSmiley = true; commentFocus.unfocus(); } else { setState(() { emojiShowing = !emojiShowing; isKeyBoardShow = emojiShowing; moreShow = false; }); } } ///更多点击事件 _onMoreTap() { if (!moreShow && commentFocus.hasFocus && isKeyBoardShow) { needShowMore = true; commentFocus.unfocus(); } else { setState(() { moreShow = !moreShow; isKeyBoardShow = moreShow; emojiShowing = false; }); } } ///图片/视频选择 Future getImageOrVideo(GalleryMode galleryMode) async { if (selectCount == 0) return; List medias = await ImagePickers.pickerPaths( galleryMode: galleryMode, selectCount: (galleryMode == GalleryMode.video) ? 1 : selectCount, showGif: true, showCamera: false, compressSize: 500, uiConfig: UIConfig( uiThemeColor: Color(0xFFFFFFFF), ), cropConfig: CropConfig( enableCrop: false, width: 200, height: 200, ), ); mediaPaths.addAll(medias.map((e) => Medias(e)).toList()); selectCount = 9 - mediaPaths.length; if (mediaPaths.length > 0) { if (galleryMode == GalleryMode.image) { dynamicType = 1; } else { dynamicType = 2; } } setState(() {}); } @override Widget build(BuildContext context) { double h = MediaQuery.of(context).viewInsets.bottom; if (h > 0) { jumpToBottom(); if (keyboard < h) { keyboard = h; // setState(() {}); } } return Scaffold( // resizeToAvoidBottomInset: false, backgroundColor: Color(0xFFF6F6F6), appBar: MyAppBar( title: "哈哈哈哈", titleColor: Color(0xFF0D0D0D), titleSize: 17.sp, leading: true, leadingColor: Colors.black, action: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { setState(() { Navigator.of(context).pushNamed('/router/chat_setting'); copyIndex = 0; }); }, child: Container( alignment: Alignment.center, child: Icon( Icons.more_horiz, color: Colors.black, size: 30, ), ), ), ), body: Container( child: Column( children: [ Expanded( child: SingleChildScrollView( physics: BouncingScrollPhysics(), controller: scrollController, child: Column( children: [ GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { setState(() { emojiShowing = false; isKeyBoardShow = emojiShowing; moreShow = false; isKeyBoardShow = moreShow; FocusScope.of(context).requestFocus(FocusNode()); }); }, child: chatDetailsList(), ), ], ), ), flex: 1, ), input() ], ), ), ); } ///聊天列表 Widget chatDetailsList() { return Container( margin: EdgeInsets.only(bottom: 48.h), child: ListView.builder( itemCount: messages.length, shrinkWrap: true, reverse: true, physics: NeverScrollableScrollPhysics(), itemBuilder: (context, position) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { setState(() { copyIndex = 0; }); }, child: chatDetailsItem(messages[position]), ); }, ), ); } Widget chatDetailsItem(Message message) { bool isSelf = message.fromId == selfUserId; bool isText = message.msgType == 0; return Container( padding: EdgeInsets.only( top: 32.h, ), child: Column( children: [ Text( "3月08日 上午 12:10", textAlign: TextAlign.center, style: TextStyle( color: Color(0xFFA29E9E), fontSize: 10.sp, fontWeight: MyFontWeight.regular, ), ), if (messages.indexOf(message) == (messages.length - 1)) Padding( padding: EdgeInsets.only(top: 10.h, bottom: 24.h), child: Text( "在对方未回复或关注你之前,你只能发送一条信息", textAlign: TextAlign.center, style: TextStyle( color: Color(0xFFA29E9E), fontSize: 10.sp, fontWeight: MyFontWeight.regular, ), ), ), if (messages.indexOf(message) == (messages.length - 1)) SizedBox( height: 16.h, ), if (copyIndex == 1) Stack( alignment: Alignment.bottomCenter, children: [ Container( padding: EdgeInsets.only(bottom: 13.h), child: Container( width: 180.w, decoration: BoxDecoration( color: Color(0xFF2A2A2A), borderRadius: BorderRadius.circular(6), ), padding: EdgeInsets.symmetric( horizontal: 32.w, vertical: 7.5.h, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( onTap: () { setState(() { copyIndex = 0; this.copy(tex); }); }, child: Column( children: [ Image.asset( "assets/image/icon_chat_copy.webp", height: 16, width: 16, ), SizedBox( height: 2.h, ), Text( "复制", textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ], ), ), GestureDetector( onTap: () {}, child: Column( children: [ Image.asset( "assets/image/icon_chat_delete.webp", height: 16, width: 16, ), SizedBox( height: 2.h, ), Text( S.of(context).shanchu, textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ], ), ) ], ), ), ), Image.asset( "assets/image/icon_copy_j.webp", height: 17, width: 17, ), ], ), /// not self if (!isSelf && isText) Padding( padding: EdgeInsets.only(left: 17.w, right: 39.w), child: Row( children: [ // MImage( // "", // isCircle: true, // width: 44, // height: 44, // fit: BoxFit.cover, // errorSrc: "assets/image/default_user.webp", // fadeSrc: "assets/image/default_user.webp", // ), Image.asset( "assets/image/fuka_zj.webp", height: 44.h, width: 44.h, ), SizedBox( width: 12.w, ), Expanded( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Color(0xFFFFFFFF), boxShadow: [ BoxShadow( color: Color(0xFFA8A3A3).withAlpha(12), offset: Offset(0, 4), blurRadius: 4, spreadRadius: 0, ) ], ), padding: EdgeInsets.symmetric( vertical: 8.h, horizontal: 12.w), child: GestureDetector( onLongPress: () { setState(() { copyIndex = 1; }); }, child: Text( tex = message.content, textAlign: TextAlign.left, style: TextStyle( height: 1.2.h, color: Color(0XFF0D0D0D), fontSize: 17.sp, fontWeight: MyFontWeight.medium, ), ), ))), ], ), ), if (!isSelf && isText) SizedBox( height: 40.h, ), if (copyIndex == 1) Stack( alignment: Alignment.bottomCenter, children: [ Container( padding: EdgeInsets.only(bottom: 13.h), child: Container( width: 180.w, decoration: BoxDecoration( color: Color(0xFF2A2A2A), borderRadius: BorderRadius.circular(6), ), padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 7.5.h), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( onTap: () { setState(() { copyIndex = 0; this.copy(tex); }); }, child: Column( children: [ Image.asset( "assets/image/icon_chat_copy.webp", height: 16, width: 16, ), SizedBox( height: 2.h, ), Text( "复制", textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ], ), ), GestureDetector( onTap: () {}, child: Column( children: [ Image.asset( "assets/image/icon_chat_delete.webp", height: 16, width: 16, ), SizedBox( height: 2.h, ), Text( S.of(context).shanchu, textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ], ), ) ], ), ), ), Image.asset( "assets/image/icon_copy_j.webp", height: 17, width: 17, ), ], ), /// self if (isSelf && isText) Padding( padding: EdgeInsets.only(left: 36.w, right: 16.w), child: Row( children: [ if (message.state == 3) Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: Color(0xFFFF441A), ), width: 20, height: 20, alignment: Alignment.center, child: Text( "!", textAlign: TextAlign.left, style: TextStyle( color: Colors.white, fontSize: 17.sp, fontWeight: MyFontWeight.regular, ), ), ), if (message.state == 3) SizedBox( width: 12.w, ), Expanded( child: Container( alignment: Alignment.centerRight, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Color(0xFF32A060), ), padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 12.w), child: GestureDetector( onLongPress: () { setState(() { copyIndex = 1; }); }, child: Text( tex = message.content, textAlign: TextAlign.left, style: TextStyle( height: 1.2.h, color: Colors.white, fontSize: 17.sp, fontWeight: MyFontWeight.medium, ), ), ), ), ), ), SizedBox( width: 12.w, ), // MImage( // "", // isCircle: true, // width: 44, // height: 44, // fit: BoxFit.cover, // errorSrc: "assets/image/default_user.webp", // fadeSrc: "assets/image/default_user.webp", // ), Image.asset( "assets/image/fuka_zj.webp", height: 44, width: 44, ), ], ), ), /// not self image if (!isSelf && !isText) Padding( padding: EdgeInsets.only(left: 17.w, right: 39.w, top: 20.h), child: Row( children: [ // MImage( // "", // isCircle: true, // width: 44, // height: 44, // fit: BoxFit.cover, // errorSrc: "assets/image/default_user.webp", // fadeSrc: "assets/image/default_user.webp", // ), Image.asset( "assets/image/fuka_zj.webp", height: 44.h, width: 44.h, ), SizedBox( width: 12.w, ), Expanded( flex: 3, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Color(0xFFFFFFFF), boxShadow: [ BoxShadow( color: Color(0xFFA8A3A3).withAlpha(12), offset: Offset(0, 4), blurRadius: 4, spreadRadius: 0, ) ], ), child: GestureDetector( onLongPress: () { setState(() {}); }, child: Image.asset( "assets/image/icon_guide_4.webp", width: 180.h, height: 200.h, fit: BoxFit.fill, ), ))), Expanded( flex: 1, child: Container(), ) ], ), ), /// no reply long time if (messages.indexOf(message) == 0) Padding( padding: EdgeInsets.only(left: 17.w, right: 17.w, top: 24.h), child: Text( "由于对方没有回复你,你只能发送一条消息,需要对方关注或回复后才能恢复正常聊天", textAlign: TextAlign.center, style: TextStyle( color: Color(0xFFA29E9E), fontSize: 10.sp, fontWeight: MyFontWeight.regular, ), ), ), ], ), ); } ///富文本输入框 Widget input() { return Container( color: Color(0xFFFDFCFC), padding: EdgeInsets.only(top: 14.h, bottom: 15.h), child: Column( children: [ ///输入框 Row( children: [ Expanded( flex: 1, child: Container( margin: EdgeInsets.only( left: 20.w, ), decoration: BoxDecoration( color: Color(0xFFF6F6F6), borderRadius: BorderRadius.circular(6), ), child: Container( margin: EdgeInsets.only(left: 17.w), alignment: Alignment.topLeft, child: TextField( textInputAction: TextInputAction.send, onTap: () { moreShow = false; _onTextFieldTap(); }, onEditingComplete: () { var commentText = chatController.text; if (commentText.trim() == "") { return; } socketClient.sendMessage(_toUser.mid, commentText).then((value) { Message message = value; messages.insert(0, message); chatController.clear(); if (mounted) setState(() {}); }); // widget.queryMemberComment(commentText); }, maxLines: 8, minLines: 1, focusNode: commentFocus, controller: chatController, decoration: InputDecoration( border: InputBorder.none, hintText: hintText, hintStyle: TextStyle( fontSize: 14.sp, color: Color(0xFF868686), ), ), ), ), ), ), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { _onSmileyTap(); Future.delayed(Duration(milliseconds: 500), () { jumpToBottom(); }); }, child: Container( padding: EdgeInsets.only(left: 15.w, right: 8.w), child: Image.asset( "assets/image/icon_chat_emo.webp", height: 26.h, width: 26.h, )), ), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { _onMoreTap(); Future.delayed(Duration(milliseconds: 500), () { jumpToBottom(); }); }, child: Container( padding: EdgeInsets.only(left: 8.w, right: 19.w), child: Image.asset( "assets/image/chat_more.webp", height: 26.h, width: 26.h, ), ), ), ], ), SizedBox( height: 16.h, ), ///表情 Offstage( offstage: !emojiShowing, child: SizedBox( height: keyboard == -1 ? 270 : keyboard, width: MediaQuery.of(context).size.width, child: EmojiPicker( textEditingController: chatController, config: Config( columns: 7, emojiSizeMax: 32 * (Platform.isIOS ? 1.10 : 1.0), verticalSpacing: 0, horizontalSpacing: 0, gridPadding: EdgeInsets.zero, initCategory: Category.RECENT, bgColor: const Color(0xFFF2F2F2), // indicatorColor: Colors.blue, iconColor: Colors.grey, iconColorSelected: Colors.blue, backspaceColor: Colors.blue, skinToneDialogBgColor: Colors.white, skinToneIndicatorColor: Colors.grey, enableSkinTones: true, showRecentsTab: true, recentsLimit: 28, replaceEmojiOnLimitExceed: false, noRecents: Text( "最近使用", style: TextStyle(fontSize: 20, color: Colors.black26), textAlign: TextAlign.center, ), loadingIndicator: const SizedBox.shrink(), tabIndicatorAnimDuration: Duration(milliseconds: 0), categoryIcons: const CategoryIcons(), buttonMode: ButtonMode.MATERIAL, checkPlatformCompatibility: true, ), )), ), ///更多 Offstage( offstage: !moreShow, child: Container( height: keyboard == -1 ? 167.h : keyboard, child: Column( children: [ Container( width: double.infinity, height: 1.h, color: Color(0xFFF7F7F7), margin: EdgeInsets.only(bottom: 10.h), ), Row( children: [ GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { getImageOrVideo(GalleryMode.video); }, child: Column( children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Color(0xFFFFFFFF), boxShadow: [ BoxShadow( color: Color(0xFFD5D5D5).withAlpha(25), offset: Offset(4, 2), blurRadius: 4, spreadRadius: 0, ) ], ), margin: EdgeInsets.only(right: 15.w, left: 14.w), padding: EdgeInsets.all(12), child: Image.asset( "assets/image/icon_chat_camera.webp", height: 24, width: 24, ), ), Padding( padding: EdgeInsets.only(top: 8.h), child: Text( "拍照", style: TextStyle( fontSize: 12.sp, color: Color(0xFFA29E9E), fontWeight: MyFontWeight.regular, ), ), ), ], ), ), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { getImageOrVideo(GalleryMode.image); }, child: Column( children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Color(0xFFFFFFFF), boxShadow: [ BoxShadow( color: Color(0xFFD5D5D5).withAlpha(25), offset: Offset(4, 2), blurRadius: 4, spreadRadius: 0, ) ], ), padding: EdgeInsets.all(12), child: Image.asset( "assets/image/icon_chat_photo.webp", height: 24, width: 24, ), ), Padding( padding: EdgeInsets.only(top: 8.h), child: Text( "相册", style: TextStyle( fontSize: 12.sp, color: Color(0xFFA29E9E), fontWeight: MyFontWeight.regular, ), ), ), ], )), ], ) ], ), ), ) ], ), ); } showCustomDialog(BuildContext context, int position) { showDialog( context: context, builder: (BuildContext context) { return new AlertDialog( backgroundColor: Color(0xFF2A2A2A), elevation: 0, contentPadding: EdgeInsets.only( top: 8.h, bottom: 5.h, ), content: Container( height: 40.h, // width:20.w, alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Column( children: [ Image.asset( "assets/image/icon_chat_copy.webp", height: 16, width: 16, ), Text( "复制", textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ], ), Column( children: [ Image.asset( "assets/image/icon_chat_delete.webp", height: 16, width: 16, ), Text( S.of(context).shanchu, textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: MyFontWeight.regular, ), ), ], ) ], ), ), ); }); } /// 复制文本 copy(String tex) { print(tex); Clipboard.setData(ClipboardData(text: tex)); } }