import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:huixiang/constant.dart'; import 'package:huixiang/generated/l10n.dart'; import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/main.dart'; import 'package:huixiang/retrofit/data/base_data.dart'; import 'package:huixiang/retrofit/retrofit_api.dart'; import 'package:huixiang/utils/font_weight.dart'; import 'package:huixiang/view_widget/classic_header.dart'; import 'package:huixiang/view_widget/my_footer.dart'; import 'package:huixiang/view_widget/round_button.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../retrofit/data/im_user.dart'; import '../../utils/flutter_utils.dart'; import '../../view_widget/custom_image.dart'; import 'on_chat_message.dart'; import 'on_chat_msg_instance.dart'; class IMPage extends StatefulWidget { IMPage(Key key) : super(key: key); @override State createState() { return _IMPage(); } } class _IMPage extends State implements OnChatMessage { ApiService apiService; int pageNum = 1; List messages = []; Map msgNumber = { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "6": 0, }; int state = 0; List conversationIds = []; Map lastMessageMap = {}; Map unreadCountMap = {}; Map contactMap = {}; int insertIndex = 0; String selfUserId; @override void onMessage(txt) { // SmartDialog.showToast("列表 $txt", alignment: Alignment.center); } @override void dispose() { super.dispose(); OnChatMsgInstance.instance.onChatMessage = null; socketClient.removeCallback(socketClient.userId); } @override void initState() { super.initState(); OnChatMsgInstance.instance.onChatMessage = this; initSocketClient(); SharedPreferences.getInstance().then((value) { apiService = ApiService(Dio(), token: value.getString("token"), context: context); // queryMsgStats(); }); } initSocketClient() async { SharedPreferences shared = await SharedPreferences.getInstance(); selfUserId = shared.getString("userId"); socketClient.addCallback(selfUserId, (Message message) { if (conversationIds.contains(message.conversationId)) { conversationIds.remove(message.conversationId); } conversationIds.insert(insertIndex, message.conversationId); lastMessageMap[message.conversationId] = message; listenerRefresh(message); }); loadMessageList(); } _refresh() { pageNum = 1; loadMessageList(); // queryMsgStats(); } listenerRefresh(Message message) async { await sortConversation(lastMessageMap); await queryUnreadCount(conversationIds); debugPrint("messages_records : ${message.toJson()}"); if (contactMap[message.fromId] == null) { queryMemberInfo([message.fromId]); return; } refreshState(); } loadMessageList() async { messages = await hxDatabase.queryList(); lastMessageMap = messages.lGroupBy((p0) => p0.conversationId).mGroupItem(key: (p1) => num.parse(p1.time)); await queryImUserInfo(messages.map((e) => e.toId != selfUserId ? e.toId : e.fromId).toList()); await sortConversation(lastMessageMap); await queryUnreadCount(conversationIds); refreshState(); } /// update conversation by time sort sortConversation(lastMessageMap) async { List sortMessages = lastMessageMap.values.toList(); sortMessages.sort((a, b) => (num.parse(b.time)).compareTo(num.parse(a.time))); conversationIds = sortMessages .map((e) => e.conversationId) .toSet() .toList(); } /// update conversation unreadcount queryUnreadCount(conversationIds) async { unreadCountMap = await hxDatabase.messageUnreadCount(conversationIds); debugPrint("unreadCountMap: $unreadCountMap"); } /// update imuser info by mids queryImUserInfo(userIds) async { List contacts = (await hxDatabase.queryImUser(userIds)) ?? []; if (contacts?.isEmpty ?? true) { await queryMemberInfo(userIds); return; } else { List queryUserIds = userIds.where((u) => contacts.where((c) => c.mid == u).isEmpty).toList(); if (queryUserIds.isNotEmpty) { await queryMemberInfo(queryUserIds); return; } } contactMap = contacts.lGroupBy((p0) => conversationId(p0.mid, selfUserId)).mGroupItem(); List topConversationIds = [],notTopUserIds = []; contactMap.forEach((key, value) { if(value.isTop == 1) topConversationIds.add(key); else notTopUserIds.add(key); }); insertIndex = topConversationIds.length; this.conversationIds = topConversationIds..addAll(notTopUserIds); } /// update one conversation last message ,and update conversation sort void updateLastMessage(String conversationId) async { Message message = await hxDatabase.lastMessage(conversationId); debugPrint("lastmessage: $conversationId ${message?.content} ${message?.toJson()}"); if (message != null) { lastMessageMap[conversationId] = message; await sortConversation(lastMessageMap); refreshState(); } } void updateUnreadCount() async { unreadCountMap = await hxDatabase.messageUnreadCount(conversationIds); refreshState(); } refreshState() { if(_refreshController.isRefresh)_refreshController.refreshCompleted(); if (mounted) setState(() {}); } ///批量查询用户信息 queryMemberInfo(List mids) async { if (mids.isEmpty) { return; } BaseData> baseData = await apiService.memberInfoByIds({ "mids": mids, }).catchError((error) { SmartDialog.showToast( AppUtils.dioErrorTypeToString(error.type), alignment: Alignment.center, ); }); if (baseData != null && baseData.isSuccess) { if (baseData.data.isNotEmpty) { baseData.data.forEach((element) async { await hxDatabase.insertOrUpdateImUser(element.toJson()); }); baseData.data.forEach((element) { if (contactMap[conversationId(element.mid, selfUserId)] == null) { contactMap[conversationId(element.mid, selfUserId)] = element; } }); refreshState(); } } else { SmartDialog.showToast(baseData.msg, alignment: Alignment.center); } } final RefreshController _refreshController = RefreshController(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xFFFFFFFF), body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFFD9FFDE), Color(0xFFD9FFDE), Color(0xFFFFFFFF), Color(0xFFFFFFFF), ], stops: [0, 0.2, 0.4, 1], ), ), child: SmartRefresher( enablePullDown: true, enablePullUp: false, header: MyHeader(), physics: BouncingScrollPhysics(), footer: CustomFooter( loadStyle: LoadStyle.ShowWhenLoading, builder: (BuildContext context, LoadStatus mode) { return (messages.length == 0) ? Container() : MyFooter(mode); }, ), controller: _refreshController, onRefresh: _refresh, onLoading: () { _refresh(); }, child: SingleChildScrollView( physics: BouncingScrollPhysics(), child: Column( children: [ Container( padding: EdgeInsets.only( top: MediaQuery.of(context).padding.top + 12.h, bottom: 15.h, right: 16.w, left: 16.w, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Text( S.of(context).xiaoxi, style: TextStyle( color: Colors.black, fontSize: 18.sp, fontWeight: MyFontWeight.bold, ), ), ), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context).pushNamed('/router/chat_friend_group') .then((value) { _refresh(); }); }, child: Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Color(0xFFFFFFFF), borderRadius: BorderRadius.circular(20.r), ), child: Image.asset( "assets/image/friend_grouping.webp", fit: BoxFit.fill, height: 14.h, width: 14.h, ), ), ), ], ), ), imPageSearch(), chatList(), ], ), ), ), ), ); } ///搜索 Widget imPageSearch() { return GestureDetector( behavior: HitTestBehavior.opaque, onTap:(){ Navigator.of(context).pushNamed('/router/im_search').then((value) { _refresh(); }); }, child: Container( margin: EdgeInsets.fromLTRB(16.w, 0, 16.w, 0), padding: EdgeInsets.symmetric(vertical: 13.h), decoration: BoxDecoration( color: Color(0xFFFDFCFC), borderRadius: BorderRadius.circular(4), ), child: Row( children: [ Padding( padding: EdgeInsets.only(left: 15.w, right: 5.w), child: Image.asset( "assets/image/icon_search.webp", width: 14.h, height: 14.h, color: Color(0xFF353535), ), ), Text( "搜索", style: TextStyle( color: Color(0xFFA29E9E), fontSize: 16.sp, fontWeight: MyFontWeight.regular, ), ), ], ) ), ); } ///聊天列表 Widget chatList() { return Container( child: SlidableAutoCloseBehavior( child: ListView( padding: EdgeInsets.only(top: 16), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), children: conversationIds.map((e) { int position = conversationIds.indexOf(e); return ClipRRect( // borderRadius: BorderRadius.all(Radius.circular(4)), child: Slidable( groupTag: true, endActionPane: ActionPane( extentRatio:0.16, motion: ScrollMotion(), children: [ CustomSlidableAction( onPressed: (bc) { showDelDialog(conversationIds[position]); }, backgroundColor: Color(0xFFFB312B), foregroundColor: Colors.white, child: Container( color: Colors.red, height: double.infinity, alignment: Alignment.center, child: Text( S.of(context).shanchu, style: TextStyle( color: Colors.white, fontSize: 14.sp, fontWeight: MyFontWeight.regular, ), ), ), ), ], ), child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context).pushNamed( '/router/chat_details_page', arguments: { "toUser": contactMap[conversationIds[position]], }, ).then((value) { unreadCountMap[conversationIds[position]] = 0; updateLastMessage(conversationIds[position]); _refresh(); }); }, child: chatItem(conversationIds[position]), ), ), ); }).toList(), ), ), ); } Widget chatItem(conversationId) { return Container( padding: EdgeInsets.only( left: 16.w, right: 17.w, bottom: 18.h, ), child: Row( children: [ MImage( !contactMap.containsKey(conversationId) ? null : contactMap[conversationId]?.avatar ?? "", isCircle: true, height: 54.h, width: 54.h, fit: BoxFit.cover, errorSrc: "assets/image/default_1.webp", fadeSrc: "assets/image/default_1.webp", ), SizedBox( width: 12.w, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( !contactMap.containsKey(conversationId) ? "" : contactMap[conversationId]?.nickname ?? "", // overflow: TextOverflow.fade, maxLines: 1, style: TextStyle( fontSize: 16.sp, color: Color(0xFF060606), fontWeight: MyFontWeight.semi_bold, ), ), ), Text( lastMessageMap[conversationId]?.time != null ? AppUtils.timeFormatter( DateTime.fromMillisecondsSinceEpoch(num.parse( lastMessageMap[conversationId]?.time ?? ""))) : "", style: TextStyle( fontSize: 12.sp, color: Color(0xFFA29E9E), fontWeight: MyFontWeight.regular, ), ), ], ), SizedBox( height: 7.h, ), Row( children: [ Expanded( child: Text( lastMessageMap[conversationId]?.content ?? "", maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 12.sp, color: Color(0xFF353535), fontWeight: MyFontWeight.regular, ), ), ), if (unreadCountMap[conversationId] != null && unreadCountMap[conversationId] > 0) Container( width: 16, height: 16, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: Color(0xFFFF441A), ), child: RoundButton( text: "${unreadCountMap[conversationId]}", textColor: Colors.white, fontWeight: MyFontWeight.regular, backgroup: Color(0xFFFF441A), fontSize: 10.sp, radius: 100, ), ), ], ), ], ), ), ], ), ); } ///确认删除弹窗 showDelDialog(conversationId) { showDialog( context: context, builder: (context) { return AlertDialog( contentPadding: EdgeInsets.zero, // 移除默认内边距 content: Container( // width: MediaQuery.of(context).size.width - 84, height: 130.h, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded(child:Container( alignment:Alignment.center, child: Text( "删除并清空聊天记录", style: TextStyle( color: Color(0xFF060606), fontSize: 16.sp, fontWeight: MyFontWeight.bold, ), ), ), ), // Spacer(), Container( height:1.h, width: double.infinity, color:Color(0xFFEDEDED), ), Row( children: [ Expanded( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context).pop(); }, child: Container( child: Text( "取消", textAlign: TextAlign.center, style: TextStyle( fontSize: 16.sp, color: Color(0xFF060606), ) ) ) ) ), Container( height:45, width: 1.w, color: Color(0xFFEDEDED), ), Expanded( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () async{ await hxDatabase.deleteByUser(conversationId); _refresh(); Navigator.of(context).pop(); }, child: Text( "确认", textAlign: TextAlign.center, style: TextStyle( color: Color(0xFFFF370A), ), ), ), ), ], ) ], ), ), ); }, ); } }