import 'package:elysia/plugin/Remind.dart'; import 'package:elysia/plugin/RouteAnimation.dart'; import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import '../bean/ChatCacheItem.dart'; import '../plugin/CacheAavatar.dart'; import '../plugin/HTTP.dart'; import '../plugin/C.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:oktoast/oktoast.dart'; import '../plugin/HiverCache.dart'; import '../plugin/LoadingOverlay.dart'; import '../plugin/StringTool.dart'; import 'child/ChatPage.dart'; class ChatRoomPage extends StatefulWidget { const ChatRoomPage({Key? key}) : super(key: key); @override State createState() => _ChatRoomPageState(); static Function(dynamic?)? flushData; } class _ChatRoomPageState extends State with AutomaticKeepAliveClientMixin { List _chatList = []; bool _loading = true; @override void initState() { super.initState(); ChatRoomPage.flushData = (data) { if (data == null) { getCache(); } else { int index = _chatList.indexWhere( (item) => item.roomId == data["roomId"].toString(), ); if (index == -1) return; // 找不到就直接返回 // 深拷贝并更新数据 ChatCacheItem chatItem = _chatList[index].copy(); chatItem.lastChatMessage = data["message"] ?? data["content"]; chatItem.updateTime(DateTime.now()); setState(() { // 先移除旧 item _chatList.removeAt(index); // 插入到列表头 _chatList.insert(0, chatItem); }); HiverCache.cache(C.HIVE_CACHE_CACHE_LIST, _chatList); } }; getCache(); } Future getCache() async { List? result = await HiverCache.getCache( C.HIVE_CACHE_CACHE_LIST, ChatCacheItem.fromJson, ); setState(() { _loading = false; }); await fetchChatList(result??[]); startFlushData(result??[]); } Future startFlushData(List cache) async { if(cache?.length==0){ _loading=true; } dynamic result = await loadData(); try { if (result['code'] == 200) { List> listOfMap = result["data"] .cast>(); List dbData = listOfMap .map((e) => ChatCacheItem.fromJson(e)) .toList(); syncLists(cache ?? [], dbData); fetchChatList(cache); HiverCache.cache(C.HIVE_CACHE_CACHE_LIST, cache); } else { setState(() { _loading = false; }); } } catch (e) { setState(() { _loading = false; }); } } void syncLists(List cacheList, List dbList) { // 提取 bList 的所有 id final bIds = dbList.map((e) => e.roomId).toSet(); // 删除 A 中多余的项(ID 不在 B 里) cacheList.removeWhere((a) => !bIds.contains(a.roomId)); // 提取 A 的所有 id final aIds = cacheList.map((e) => e.roomId).toSet(); // 添加 B 中缺少的项 for (var b in dbList) { if (!aIds.contains(b.roomId)) { cacheList.add(b); } } } Future fetchChatList(List result) async { print(result); setState(() { _chatList = result; _loading = false; }); } Future loadData() async { dynamic result = await HTTP .create("${C.BASE_URL}/chat/list") .setHeader(C.TOKEN) .setRequestType(RequestType.GET) .execute(); return result; } @override Widget build(BuildContext context) { return Scaffold( body: _loading ? ListView.builder( itemCount: 10, // 加载前显示 10 个 Shimmer itemBuilder: (context, index) { return ShimmerChatTile(); }, ) : ListView.builder( itemCount: _chatList.length, itemBuilder: (context, index) { final chat = _chatList[index]; return ChatTile( chat: chat, onDelete: () { Remind.show( context, "提示", "是否删除聊天,这是不可恢复的!!!", onTap: (status) async { if (status) { LoadingOverlay.show( context: context, barrierColor: Colors.black54, ); try { dynamic result = await HTTP .create("${C.BASE_URL}/chat/del/${chat.roomId}") .setHeader(C.TOKEN) .setRequestType(RequestType.DELETE) .execute(); LoadingOverlay.hide(); if (result['code'] == 200) { setState(() { _chatList.removeAt(index); }); HiverCache.cache(C.HIVE_CACHE_CACHE_LIST, _chatList); } else { showToast( result["message"], position: ToastPosition.bottom, ); } } catch (e) { LoadingOverlay.hide(); } } }, ); }, onClear: () { Remind.show( context, "提示", "是否清空聊天记录", onTap: (status) async { if (status) { LoadingOverlay.show( context: context, barrierColor: Colors.black54, ); try { dynamic result = await HTTP .create( "${C.BASE_URL}/chat/clear/${chat.roomId}", ) .setHeader(C.TOKEN) .setRequestType(RequestType.DELETE) .execute(); LoadingOverlay.hide(); if (result['code'] == 200) { setState(() { _chatList[index].lastChatTime = ""; _chatList[index].lastChatMessage = ""; }); HiverCache.cache(C.HIVE_CACHE_CACHE_LIST, _chatList); } else { showToast( result["message"], position: ToastPosition.bottom, ); } } catch (e) { LoadingOverlay.hide(); } } }, ); }, ); }, ), ); } @override bool get wantKeepAlive => true; } /// 单条聊天列表项 class ChatTile extends StatelessWidget { final ChatCacheItem chat; final VoidCallback? onDelete; final VoidCallback? onClear; const ChatTile({Key? key, required this.chat, this.onDelete, this.onClear}) : super(key: key); @override Widget build(BuildContext context) { return Slidable( key: ValueKey(chat.roomId), endActionPane: ActionPane( motion: const DrawerMotion(), // 滑动动画 extentRatio: 0.45, // 右滑按钮占宽度比例 children: [ Expanded( child: SlidableAction( onPressed: (context) { if (onDelete != null) onDelete!(); }, backgroundColor: Colors.red, foregroundColor: Colors.white, icon: Icons.delete, label: '删除', ), flex: 4, ), Expanded( child: SlidableAction( onPressed: (context) { if (onClear != null) onClear!(); }, backgroundColor: Colors.amberAccent, foregroundColor: Colors.white, icon: Icons.cleaning_services_rounded, label: '清空聊天记录', ), flex: 6, ), ], ), child: ListTile( leading: CacheAvatar(url: chat.roomImage), title: Text( chat.roomName, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( StringTool.stripMarkdown(chat.lastChatMessage), maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Text( chat.lastChatTime, style: const TextStyle(fontSize: 12, color: Colors.grey), ), onTap: () { C.PUSH_STATUS = false; Navigator.push( context, RouteAnimation( ChatPage( roomId: chat.roomId, robotAvatar: chat.roomImage, robotName: chat.roomName, ), Offset(1, 0), ), ); }, ), ); } } /// Shimmer 占位列表项 class ShimmerChatTile extends StatelessWidget { const ShimmerChatTile({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ListTile( leading: Shimmer.fromColors( baseColor: Colors.grey.shade300, highlightColor: Colors.white, child: Container( width: 48, height: 48, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), ), ), title: Shimmer.fromColors( baseColor: Colors.grey.shade300, highlightColor: Colors.grey.shade100, child: Container( height: 16, color: Colors.grey, margin: const EdgeInsets.symmetric(vertical: 4), ), ), subtitle: Shimmer.fromColors( baseColor: Colors.grey.shade300, highlightColor: Colors.grey.shade100, child: Container( height: 14, color: Colors.grey, margin: const EdgeInsets.symmetric(vertical: 4), ), ), ); } }