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 State<GroupChatMain> with TickerProviderStateMixin {
String _toGroupId;
_GroupChatMainState(this._toGroupId);
//我的账户
UserBean? _meUserBean;
GroupBean? _groupBean;
List<String> addTimeList = [];
List<GroupChatBean> 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<BaseEvent>((baseBean) {
//刷新群聊记录
if(baseEvent != null && baseBean.type == BaseEvent.TYPE_REFRESH_GROUP){
loadAllChat();
loadGroupBean();
}
});
chatEvent = eventBus.on<GroupChatBean>((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: <Widget>[
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: <Widget>[
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<String, Object> result = HashMap<String, Object>();
result["from_account"] = _toGroupId;
eventBus.emit(BaseEvent(BaseEvent.TYPE_UPDATE_CHAT_STATUS, result: result));
}
}
// 下拉刷新
Future<void> _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<String, Object> result = HashMap<String, Object>();
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 List<GroupChatBean> items;
final GroupChatBean groupChatBean;
final int index;
final String? otherAccount;
final UserBean? meUserBean;
final String account;
final deleteCallback;
final List<String>? 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<GroupChatContentView> createState() => _GroupChatContentViewState();
}
class _GroupChatContentViewState extends State<GroupChatContentView> {
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: <Widget>[
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: <Widget>[
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: <Widget>[
//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: <Widget>[
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<void> _showDeleteDialog(GroupChatBean groupChatBean) async {
return showDialog<Null>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('确定要删除该消息吗?', style: new TextStyle(fontSize: 17.0)),
actions: <Widget>[
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);
}
}





![[附源码]java毕业设计在线二手车交易信息管理系统](https://img-blog.csdnimg.cn/06da7ba9f0464ec9bdc8344f25b001d9.png)













