Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。
效果图:
实现代码:
group_chat_main.dart
/** * Author : wangning * Email : maoning20080809@163.com * Date : 2022/11/6 19:42 * Description : 群聊 */ class GroupChatMain extends StatefulWidget { String toGroupId; String account = SpUtils.getString(CommonUtils.LOGIN_ACCOUNT); GroupChatMain({required this.toGroupId}); @override _GroupChatMainState createState() => _GroupChatMainState(toGroupId); } class _GroupChatMainState extends Statewith TickerProviderStateMixin { String _toGroupId; _GroupChatMainState(this._toGroupId); //我的账户 UserBean? _meUserBean; GroupBean? _groupBean; List addTimeList = []; List items = []; ScrollController _controller = ScrollController(); var chatEvent; var baseEvent; //每页13条 static const PAGE_SIZE = 13; //当前页 var PAGE_NUM = 1; //从那一条开始(为保证最新的先显示, 先查询最后的,并且不能用desc查询) var startNum = 0; //总共多少条 var CHAT_TOTAL = 0; //群成员个数 int? memberCount = 0; @override void initState() { super.initState(); AppManager.getInstance().toGroupId = _toGroupId; _checkAvailable(); _updateChatStatus(); baseEvent = eventBus.on ((baseBean) { //刷新群聊记录 if(baseEvent != null && baseBean.type == BaseEvent.TYPE_REFRESH_GROUP){ loadAllChat(); loadGroupBean(); } }); chatEvent = eventBus.on ((groupChatBean) { LogUtils.d('群聊发送消息接收:${groupChatBean.runtimeType}'); setState(() { groupChatBean as GroupChatBean; items.add(groupChatBean); setState(() { }); }); }); loadGroupBean(); loadGroupUserCount(); loadUserBean(); loadAllChat(); jumpToBottom(400); // 监听滚动事件 _controller.addListener((){ if(_controller.position.pixels>_controller.position.maxScrollExtent-40){ } }); } //加载群成员个数 void loadGroupUserCount() async{ memberCount = await GroupUserRepository.getInstance().findGroupUserCountByGroupId(widget.toGroupId); LogUtils.d("加载群成员个数 groupId = ${widget.toGroupId} , count = ${memberCount}"); /*setState(() { });*/ } @override void dispose() { super.dispose(); eventBus.off(chatEvent); eventBus.off(baseEvent); AppManager.getInstance().toGroupId = ""; } final controller = TextEditingController(); FocusNode _chatContentFocus = FocusNode(); @override Widget build(BuildContext context) { if(!isLoadMore){ //每次发送消息滚动到底部0.1秒 jumpToBottom(100); } return Scaffold( appBar: WnAppBar.getAppBar(context, Text("${_groupBean?.groupName}(${memberCount})"), addGroup : true, addGroupClick: (){ Navigator.pushNamed(context, Routes.show_group_member, arguments: { "groupId":widget.toGroupId //参数map }); }), bottomNavigationBar: Text(""),//占用底部位置 body: GestureDetector( onTap: (){ _processClickBlank(); }, child: Container( child: Column( children: [ Expanded( child: RefreshIndicator( displacement: 2, onRefresh: _onRefresh, child: ListView.builder( controller: _controller, itemBuilder: (BuildContext context, int index) { return GroupChatContentView(items: items,account: widget.account, groupChatBean: items[index], index: index, meUserBean: _meUserBean,addTimeList: addTimeList, otherAccount: items[index].account, deleteCallback: (data){ setState(() { items.remove(items[index]); }); },clickVoiceCallback: (data){ //点击播放录音,暂停后再播放 for(int i = 0; i < items.length; i++){ if(i == index){ items[i].isPlayVoice = true; } else { items[i].isPlayVoice = false; } } setState(() { }); }, refreshTransfer: (position){ LogUtils.d("回调刷新:${position}"); },); }, itemCount: items.length, ) ), ), Divider(height: 12.0, color: Color(0xFFF7F8F8),), Container( padding: EdgeInsets.only(top: 5.0, bottom: 5.0, right: 2.0, left: 2.0), color: Color(0xFFF3F3F3), width: double.infinity, child: Row( children: [ Container( margin: EdgeInsets.symmetric(horizontal: 2.0), child: IconButton( //按下语音说活 icon: isPressVoice ? Image.asset("assets/chat/button_keyboard.png"):Image.asset("assets/chat/button_voice.png"), onPressed: () =>{ _processPressVoice() } ), //触发发送消息事件执行的函数_handleSubmitted ), Expanded( child: Stack( children: [ Offstage( offstage: !isPressVoice, child: ChatVoiceView( refreshMediaCallback: (type, mediaURL, thumbnailFileName, second, messageId){ _refreshMedia(type, mediaURL, thumbnailFileName, mediaSecond: second, messageId: messageId); }, sendMedialCallback: (type, mediaURL, second, messageId){ _sendMedia(type, mediaURL, mediaSecond: second, messageId: messageId); }, stopPlayVoiceCallback: (){ hidePlayVoiceList(); }, ), ), Offstage( offstage: isPressVoice, child: Container( padding: EdgeInsets.only(top: 8.0, bottom: 8.0, left: 8.0), decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Colors.white), child: TextField( controller: controller, focusNode: _chatContentFocus, decoration: InputDecoration.collapsed(hintText: null), autocorrect: true, //是否自动更正 autofocus: false, maxLines: 5, minLines: 1, textAlign: TextAlign.start, style: TextStyle(color: Colors.black, fontSize: 20), cursorColor: Colors.green, onTap: (){ //点击编辑框 jumpToBottom(400); hideEmoji = true; hideAdd = true; setState(() { }); }, onChanged: (value){ //录入文字 setState(() { if(value.length>0){ hideSend = false; hideAddIcon = true; } else { hideSend = true; hideAddIcon = false; } }); }, onSubmitted: _handleSubmitted, enabled: true, //是否禁用 ), ), ), ], ), ), Container( child: IconButton( icon: Image.asset("assets/chat/button_emoji.png"), onPressed: () => _processEmoji()), ), Offstage( offstage: hideAddIcon, child: Container( //margin: EdgeInsets.only(right: 4.0), child: IconButton( //添加按钮 icon: Image.asset("assets/chat/button_add.png"), onPressed: () => { _processAdd() } ), ), ), Offstage( offstage: hideSend, child: Container( margin: EdgeInsets.symmetric(horizontal: 4.0), child: IconButton( //发送按钮 icon: new Icon(Icons.send), //发送按钮图标 onPressed: () => _handleSubmitted( controller.text)), //触发发送消息事件执行的函数_handleSubmitted ), ), ], ), ), Offstage( offstage: hideAdd, child: ChatAddView( viewType: CommonUtils.VIEW_TYPE_GROUP_CHAT, toChatId: widget.toGroupId, refreshMediaCallback: (type, mediaURL, thumbnailFileName, second, messageId){ _refreshMedia(type, mediaURL, thumbnailFileName, mediaSecond: second , messageId: messageId); }, sendMedialCallback: (type, mediaURL, second, messageId){ _sendMedia(type, mediaURL, mediaSecond: second, messageId: messageId); }, refreshRedpacketAndTransfer: (type, text){ }, ), ), Offstage( offstage: hideEmoji, child: getEmojiWidget(), ), ], ), ), ), ); } //进入聊天页面,把聊天状态更新为已读 void _updateChatStatus() async{ int newMessageCount = await ChatRepository.getInstance().getAllChatUnReadByAccount(_toGroupId)??0; if(newMessageCount >= 0){ await ChatRepository.getInstance().updateChatReadByAccount(_toGroupId); Map result = HashMap (); result["from_account"] = _toGroupId; eventBus.emit(BaseEvent(BaseEvent.TYPE_UPDATE_CHAT_STATUS, result: result)); } } // 下拉刷新 Future _onRefresh() async{ // 持续两秒 await Future.delayed(Duration(milliseconds:20),(){ //LogUtils.d("下拉刷新--开始 ${startNum} , ${PAGE_SIZE} , ${CHAT_TOTAL}"); if(startNum >= PAGE_SIZE){ startNum = CHAT_TOTAL - PAGE_SIZE * PAGE_NUM; _loadMoreData(widget.account, widget.toGroupId, startNum, PAGE_SIZE); } else if(startNum > 0 && startNum < PAGE_SIZE){ //不够1页数据,查询全部,然后就不能下一页 _loadMoreData(widget.account, widget.toGroupId, 0, startNum); startNum = 0; } }); } bool isLoadMore = false; //上拉加载更多数据 void _loadMoreData(String fromAccount, String toAccount, int sNum , int pageSize){ } //检查状态, 如果不可以,先登录 void _checkAvailable() async{ var isAvailable = await XmppManager.getInstance().isAvailable(); if(!isAvailable){ String account = SpUtils.getString(CommonUtils.LOGIN_ACCOUNT); String password = SpUtils.getString(CommonUtils.LOGIN_PASSWORD); XmppManager.getInstance().connect(account, password); } } void loadAllChat() async { CHAT_TOTAL = await GroupChatRepository.getInstance().findGroupChatCountByGroupId(widget.toGroupId)??0; startNum = CHAT_TOTAL - PAGE_SIZE * PAGE_NUM; GroupChatRepository.getInstance().findAllGroupChatByGroupIdPage(widget.toGroupId, startNum, CHAT_TOTAL).then((chatList) { if(startNum > 0){ PAGE_NUM++; } setState(() { items = chatList??[]; items.forEach((element) { LogUtils.d("群aa聊:${element.toJson()}"); }); }); }); } void loadUserBean() async { UserRepository.getInstance().findUserByAccount(widget.account).then((userBean) { //setState(() { _meUserBean = userBean!; //}); }); } //加载群信息 void loadGroupBean() async { _groupBean = await GroupRepository.getInstance().findGroupByGroupId(widget.toGroupId); //setState(() { //}); } _sendMessage(var message){ int id = DateTime.now().millisecondsSinceEpoch; String toJid = "${widget.toGroupId}@conference.wangning"; XmppManager.getInstance().sendGroupMessageWithType(toJid, message, "${id}", id); Map result = HashMap (); eventBus.emit(BaseEvent(BaseEvent.TYPE_NEW_MESSAGE, result: result)); } //默认滚动到底部 void jumpToBottom(int milliseconds){ //LogUtils.d("滚动到底部"); //第一次进入,滚动到底部要0.4秒 if (items.length > 0) { Timer(Duration(milliseconds: milliseconds), () => _controller.jumpTo(_controller.position.maxScrollExtent)); } } //隐藏播放列表,停止播放录音 hidePlayVoiceList(){ for(int i = 0; i < items.length;i++){ items[i].isPlayVoice = false; } AudioPlayer.getInstance().stop(); setState(() { }); } //刷新多媒体(图片、语音、小视频) (先刷新本地,然后小视频压缩完成再慢慢发送) void _refreshMedia(int type, String mediaURL, String thumbnailFileName, {int mediaSecond=0, String messageId = "" }) async { bool isNetwork = await CommonNetwork.isNetwork(); if(!isNetwork) { CommonUtils.showNetworkError(context); return; } String addTime = WnDateUtils.getCurrentTime(); //先刷新本地聊天 //ChatBean chatBean = ChatBean(fromAccount: widget.account, toAccount: widget.toGroupId,addTime:addTime,messageId: messageId,isRead: 1); GroupChatBean groupChatBean = GroupChatBean(account: widget.account, groupId: widget.toGroupId, addTime: addTime, messageId: messageId); groupChatBean.contentType = type; if(type == CommonUtils.CHAT_CONTENT_TYPE_VOICE){ groupChatBean.localMediaUrl = mediaURL; groupChatBean.second = mediaSecond; //状态变更,向聊天记录中插入新记录 setState(() { items.add(groupChatBean); }); await GroupChatRepository.getInstance().insertGroupChat(groupChatBean); } else if(type == CommonUtils.CHAT_CONTENT_TYPE_IMG){ groupChatBean.localMediaUrl = mediaURL; //状态变更,向聊天记录中插入新记录 setState(() { items.add(groupChatBean); }); await GroupChatRepository.getInstance().insertGroupChat(groupChatBean); } else if(type == CommonUtils.CHAT_CONTENT_TYPE_VIDEO){ groupChatBean.localMediaUrl = mediaURL; groupChatBean.content = thumbnailFileName; groupChatBean.second = mediaSecond; GroupChatBean? localGroupChatBean = await GroupChatRepository.getInstance().findGroupChatByMessageId(messageId); //状态变更,向聊天记录中插入新记录 if(localGroupChatBean == null){ items.add(groupChatBean); GroupChatRepository.getInstance().insertGroupChat(groupChatBean); } else { groupChatBean.id = localGroupChatBean.id; GroupChatRepository.getInstance().updateGroupChat(groupChatBean); //如果已经存在,先删除在添加 for(int i = 0; i < items.length; i++){ GroupChatBean item = items[i]; if(item.messageId == messageId){ items.remove(item); break; } } items.add(groupChatBean); } setState(() { }); } jumpToBottom(100); } //发送多媒体(图片、语音、小视频) void _sendMedia(int type, String mediaURL, {int mediaSecond = 0, String messageId = ""}) async { bool isNetwork = await CommonNetwork.isNetwork(); if(!isNetwork) { return; } //上传文件 GroupChatBean? serverChatBean; String message = ""; String addTime = WnDateUtils.getCurrentTime(); ChatSendBean chatSendBean = ChatSendBean(); chatSendBean.fromAccount = SpUtils.getAccount(); chatSendBean.contentType = type; chatSendBean.messageId = messageId; chatSendBean.addTime = addTime; if(type == CommonUtils.CHAT_CONTENT_TYPE_IMG){ //图片 serverChatBean = await UploadUtils.getInstance().uploadGroupChatImage(widget.account, widget.toGroupId, mediaURL, messageId, addTime); } else if(type == CommonUtils.CHAT_CONTENT_TYPE_VOICE){ //语音 serverChatBean = await UploadUtils.getInstance().uploadGroupChatVoice(widget.account, widget.toGroupId, mediaURL, messageId, addTime, mediaSecond); chatSendBean.second = mediaSecond; } else if(type == CommonUtils.CHAT_CONTENT_TYPE_VIDEO){ //小视频 serverChatBean = await UploadUtils.getInstance().uploadGroupChatVideo(widget.account, widget.toGroupId, mediaURL, messageId, addTime, mediaSecond); chatSendBean.second = mediaSecond; } else { return ; } chatSendBean.content = serverChatBean?.serverMediaUrl??""; GroupChatRepository.getInstance().updateGroupChatByMessageId(serverChatBean?.messageId??"", serverChatBean?.serverMediaUrl??""); message = jsonEncode(chatSendBean); _sendMessage(message); } //是否隐藏文件 bool hideAdd = true; //是否隐藏emoji表情 bool hideEmoji = true; //是否隐藏发送按钮 bool hideSend = true; //是否隐藏添加按钮 bool hideAddIcon = false; //是否按下语音说话 bool isPressVoice = false; //点击空白地方,隐藏文件、emoji void _processClickBlank(){ setState(() { hideAdd = true; hideEmoji = true; _chatContentFocus.unfocus(); // 失去焦点 }); } void _processPressVoice(){ setState(() { isPressVoice = !isPressVoice; hideEmoji = true; hideAdd = true; _processFocus(); }); } void _processEmoji(){ LogUtils.d("点击emoji"); setState(() { hideEmoji = !hideEmoji; isPressVoice = false; hideAdd = true; _processFocus(); }); } void _processAdd(){ setState(() { hideAdd = !hideAdd; isPressVoice = false; hideEmoji = true; _processFocus(); }); } void _processFocus(){ if(!hideAdd || !hideEmoji || isPressVoice){ _chatContentFocus.unfocus(); // 失去焦点 } else { FocusScope.of(context).requestFocus(_chatContentFocus); // 获取焦点 } } emoticonClick(String name){ //LogUtils.d("选择表情:" + name); controller.text = name; } ///选中表情 _onEmojiSelected(Emoji emoji) { controller ..text += emoji.emoji ..selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length)); hideAddIcon = true; hideSend = false; setState(() { }); } ///表情删除按钮 _onBackspacePressed() { controller ..text = controller.text.characters.skipLast(1).toString() ..selection = TextSelection.fromPosition( TextPosition(offset: controller.text.length)); if (controller.text.isNotEmpty) { setState(() { }); } } //定义发送文本事件的处理函数 void _handleSubmitted(String text) async { if (text.length > 0) { bool isNetwork = await CommonNetwork.isNetwork(); if(!isNetwork) { CommonUtils.showNetworkError(context); return; } int contentType = CommonUtils.CHAT_CONTENT_TYPE_TEXT; String addTime = WnDateUtils.getCurrentTime(); String messageId = UUID.getUUID(); ChatSendBean chatSendBean = ChatSendBean(); chatSendBean.contentType = contentType; chatSendBean.content = text; chatSendBean.addTime = addTime; chatSendBean.second = 0; chatSendBean.messageId = messageId; chatSendBean.fromAccount = SpUtils.getAccount(); String message = jsonEncode(chatSendBean); controller.clear(); //清空输入框 GroupChatBean groupChatBean = GroupChatBean(account: widget.account, groupId: widget.toGroupId, content: text, contentType: contentType, addTime: addTime, messageId: messageId, localMediaUrl: "", serverMediaUrl: "", second: 0); LogUtils.d("插入数据:${groupChatBean.toJson()}"); //状态变更,向聊天记录中插入新记录 setState(() { hideAddIcon = false; hideSend = true; items.add(groupChatBean); }); await GroupChatRepository.getInstance().insertGroupChat(groupChatBean); _sendMessage(message); jumpToBottom(100); } } Widget getEmojiWidget(){ return SizedBox( height: 200.0, width: 1000.0, child: EmojiPicker( onEmojiSelected: (Category category, Emoji emoji) { _onEmojiSelected(emoji); }, onBackspacePressed: _onBackspacePressed, config: const Config( columns: 7, emojiSizeMax: 25.0, verticalSpacing: 0, horizontalSpacing: 0, initCategory: Category.RECENT, bgColor: Color(0xFFF2F2F2), indicatorColor: Color(0xff65DAC5), iconColor: Colors.orange, iconColorSelected: Color(0xff65DAC5), progressIndicatorColor: Color(0xff65DAC5), backspaceColor: Color(0xff65DAC5), showRecentsTab: true, recentsLimit: 28, categoryIcons: CategoryIcons(), buttonMode: ButtonMode.MATERIAL)), ); } }
group_chat_content_view.dart
/** * Author : wangning * Email : maoning20080809@163.com * Date : 2022/11/6 19:46 * Description : 群聊 */ class GroupChatContentView extends StatefulWidget { final Listitems; final GroupChatBean groupChatBean; final int index; final String? otherAccount; final UserBean? meUserBean; final String account; final deleteCallback; final List ? addTimeList; //点击语音播放回调 final clickVoiceCallback; //点击领取转账,刷新页面 final refreshTransfer; GroupChatContentView({required this.items, required this.account, required this.groupChatBean, required this.index, required this.meUserBean, required this.otherAccount, this.addTimeList, required this.deleteCallback, required this.clickVoiceCallback, required this.refreshTransfer}); @override State createState() => _GroupChatContentViewState(); } class _GroupChatContentViewState extends State { UserBean? otherUserBean; @override void initState() { super.initState(); _loadOtherUserBean(); } void _loadOtherUserBean() async { otherUserBean = await UserRepository.getInstance().findUserByAccount(widget.otherAccount??""); setState(() { }); } void goNewFriends(String account) async{ UserBean? userBean = await UserRepository.getInstance().findUserByAccount(account); if(userBean != null){ Navigator.push(context,MaterialPageRoute(builder: (context)=>AddFriends(userBean: userBean!,))); } else { userBean = await UserRepository.getInstance().getUserServer(account); Navigator.push(context,MaterialPageRoute(builder: (context)=>AddFriends(userBean: userBean!,))); } } @override Widget build(BuildContext context) { String addTimeResult = _getAddTime("${widget.groupChatBean.addTime}"); bool isExistTime = isExistAddTime(addTimeResult); if(!isExistTime){ widget.addTimeList?.add(addTimeResult); } //如果是最后一个,清除标志 if(widget.index == widget.items.length -1){ widget.addTimeList?.clear(); } return Column( children: [ Offstage( offstage: isExistTime, child: Container( margin: EdgeInsets.only(top: 12), child: Text("${addTimeResult}"), ), ), Container( child: widget.account == widget.groupChatBean.account ? fromAccountWidget() : toAccountWidget(), ) ], ); } //小视频缩略图 Widget getCommonThumbnail(int second){ return CommonThumbnailWidget( padding: EdgeInsets.only( top: 0.0, right: (widget.account == widget.groupChatBean.account ? 0.0 : 5.0), left: (widget.account == widget.groupChatBean.account ? 2.0 : 0.0)), image: widget.groupChatBean.content??"", second: second, onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => VideoPlayLocalPreview(widget.groupChatBean.localMediaUrl!))); }); } //显示我的 Widget fromAccountWidget(){ return Container( margin: EdgeInsets.only(top: 8.0, left: 68.0, right: 8), padding: EdgeInsets.all(2.0), child: Row( children: [ Expanded( child: GestureDetector( onLongPress: (){ _showDeleteDialog(widget.groupChatBean); }, onTap: () { }, child: Stack( alignment: AlignmentDirectional.bottomEnd, children: [ Offstage( offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_TEXT), child: Column( // Column被Expanded包裹起来,使其内部文本可自动换行 crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0), decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Color(0xFF9EEA6A),), child: Text( widget.groupChatBean.content??"", textAlign: TextAlign.left, style: TextStyle(color: Colors.black, fontSize: 20.0), ), ) ], ), ), Offstage( offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VOICE), child: InkWell( onTap: () { widget.clickVoiceCallback(true); setState(() { widget.groupChatBean.isPlayVoice = true; }); //点击语音 AudioPlayer.getInstance().playLocal(widget.groupChatBean.localMediaUrl??"", callback: (data){ //录音回调 setState(() { widget.groupChatBean.isPlayVoice = false; }); }); }, child : Container( width: 120, height: 40, padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 2.0), decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(1.0),),color: Color(0xFF9EEA6A),), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Text("${widget.groupChatBean.second}''"), SizedBox(width: 4,), widget.groupChatBean.isPlayVoice?Image.asset("assets/chat/wn_chat_me_animator.gif", height: 24,):Image.asset("assets/chat/wn_chat_me_volume_3.png", height: 24,), ], ), ) ), ), Offstage( offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_IMG), child: CommonUtils.showGroupChatImage(widget.groupChatBean.serverMediaUrl??"", widget.groupChatBean.localMediaUrl??"", width:100, height:200, angle:1, onPressed: (data){ Navigator.push(context,MaterialPageRoute(builder: (context)=>CommonImagePreview(fileName: data))); }), ), widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VIDEO?getCommonThumbnail(widget.groupChatBean.second??0):SizedBox(), ], ), ), ), //userImage Container( padding: EdgeInsets.only(left: 6, right: 6), child: GestureDetector( onTap: (){ //Navigator.push(context,MaterialPageRoute(builder: (context)=>ContactsDetails(toChatId: widget.chatBean.fromAccount??""))); }, child: CommonAvatarView.showBaseImage(widget.meUserBean?.avatar??"", 38, 38), ), ), ], ), ); } //显示好友 Widget toAccountWidget(){ return Container( margin: EdgeInsets.only(top: 8.0, right: 68.0), padding: EdgeInsets.all(2.0), child: Row( //crossAxisAlignment: CrossAxisAlignment.start, children: [ //userImage, Container( margin: EdgeInsets.only(left: 6, right: 6), child: GestureDetector( onTap : (){ Navigator.push(context,MaterialPageRoute(builder: (context)=>ContactsDetails(toChatId: otherUserBean?.account??""))); }, child: CommonAvatarView.showBaseImage(otherUserBean?.avatar??"", 38, 38), ), ), Expanded( child: GestureDetector( onLongPress: (){ _showDeleteDialog(widget.groupChatBean); }, onTap: () { }, child: Stack( alignment: AlignmentDirectional.centerStart, children: [ Offstage( offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_TEXT), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0), decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Color(0xFFEDEDED)), child: Text( widget.groupChatBean.content??"", textAlign: TextAlign.left, style: TextStyle(color: Colors.black, fontSize: 20.0), ), ) ], ), ), Offstage( offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VOICE), child: InkWell( onTap: () { setState(() { widget.groupChatBean.isPlayVoice = true; }); LogUtils.d("点击语音"); AudioPlayer.getInstance().playLocal(widget.groupChatBean.localMediaUrl??"", callback: (data){ LogUtils.d("录音回调:${data}"); setState(() { widget.groupChatBean.isPlayVoice = false; }); }); }, child : Container( width: 120, height: 40, padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 2.0), decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Color(0xFFEDEDED)), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ widget.groupChatBean.isPlayVoice?Image.asset("assets/chat/wn_chat_other_animator.gif", height: 34,):Image.asset("assets/chat/wn_chat_other_volume_3.png", height: 34,), SizedBox(width: 4,), Text("${widget.groupChatBean.second}''"), ], ), ) ), ), Offstage( offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_IMG), child: CommonUtils.showGroupChatImage(widget.groupChatBean.serverMediaUrl??"", widget.groupChatBean.localMediaUrl??"", width:100, height:200, angle:1, onPressed: (data){ Navigator.push(context,MaterialPageRoute(builder: (context)=>CommonImagePreview(fileName: data))); }), ), widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VIDEO?getCommonThumbnail(widget.groupChatBean.second??0):SizedBox(), ], ) ), ), /**/ ], ), ); } bool isExistAddTime(String addTimeResult){ return widget.addTimeList?.contains(addTimeResult)??false; } String _getAddTime(String addTime){ return WnTimeUtils.timeUtils(startTime: addTime); } //删除对话框 Future _showDeleteDialog(GroupChatBean groupChatBean) async { return showDialog ( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('确定要删除该消息吗?', style: new TextStyle(fontSize: 17.0)), actions: [ MaterialButton( child: Text('取消'), onPressed: (){ LogUtils.d("确定取消"); Navigator.of(context).pop(); }, ), MaterialButton( child: Text('确定'), onPressed: (){ LogUtils.d("确定删除"); Navigator.pop(context); _deleteChatBean(groupChatBean); }, ) ], ); } ); } //删除消息 _deleteChatBean(GroupChatBean groupChatBean) async{ int id = groupChatBean.id??0; await ChatRepository.getInstance().deleteChatById(id); widget.deleteCallback(true); } }