2025-07-21 15:46:30 +08:00
|
|
|
|
/// 聊天首页模板
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
2025-09-17 15:32:18 +08:00
|
|
|
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
|
|
|
|
import 'package:loopin/IM/global_badge.dart';
|
|
|
|
|
import 'package:loopin/IM/im_service.dart';
|
2025-09-17 15:32:18 +08:00
|
|
|
|
import 'package:loopin/api/shop_api.dart';
|
2025-08-26 17:38:59 +08:00
|
|
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:loopin/components/scan_util.dart';
|
2025-08-26 17:38:59 +08:00
|
|
|
|
import 'package:loopin/models/conversation_type.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/models/conversation_view_model.dart';
|
2025-09-17 15:32:18 +08:00
|
|
|
|
import 'package:loopin/pages/chat/menu/add_friend.dart';
|
|
|
|
|
import 'package:loopin/service/http.dart';
|
2025-08-27 23:26:29 +08:00
|
|
|
|
import 'package:loopin/utils/index.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:loopin/utils/parse_message_summary.dart';
|
2025-09-17 15:32:18 +08:00
|
|
|
|
import 'package:loopin/utils/scan_code_type.dart'; // 导入外部枚举
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
2025-09-13 17:01:01 +08:00
|
|
|
|
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
import '../../behavior/custom_scroll_behavior.dart';
|
|
|
|
|
import '../../styles/index.dart';
|
|
|
|
|
|
|
|
|
|
class ChatPage extends StatefulWidget {
|
|
|
|
|
const ChatPage({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<ChatPage> createState() => ChatPageState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ChatPageState extends State<ChatPage> {
|
|
|
|
|
late final ChatController controller;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
controller = Get.find<ChatController>();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-21 10:50:38 +08:00
|
|
|
|
void deletConv(context, ConversationViewModel item) async {
|
|
|
|
|
final res = await ImService.instance.deleteConversation(conversationID: item.conversation.conversationID);
|
|
|
|
|
if (res.success) {
|
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
|
controller.chatList.remove(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
// 长按坐标点
|
|
|
|
|
double posDX = 0.0;
|
|
|
|
|
double posDY = 0.0;
|
2025-09-18 15:33:14 +08:00
|
|
|
|
late RxInt followed = 0.obs; // 是否关注
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
// 下拉刷新
|
|
|
|
|
Future<void> handleRefresh() async {
|
|
|
|
|
await Future.delayed(Duration(seconds: 1));
|
|
|
|
|
setState(() {});
|
|
|
|
|
}
|
2025-09-17 15:32:18 +08:00
|
|
|
|
|
|
|
|
|
// 处理扫码结果
|
2025-09-13 16:14:27 +08:00
|
|
|
|
void handleScanResult(String code) {
|
|
|
|
|
print('扫码结果11111111111111111111111:$code');
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
2025-09-13 16:14:27 +08:00
|
|
|
|
// 使用外部枚举的方法检查扫码类型
|
|
|
|
|
final scanType = ScanCodeType.fromCode(code);
|
|
|
|
|
if (scanType != null) {
|
|
|
|
|
final value = code.substring(scanType.prefix.length + 1); // 获取后缀值
|
|
|
|
|
_processScanCode(scanType, value);
|
|
|
|
|
} else {
|
|
|
|
|
// 未知类型的码
|
|
|
|
|
Get.snackbar('未知的二维码类型', '无法识别此二维码: $code');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理不同类型的扫码结果
|
|
|
|
|
void _processScanCode(ScanCodeType type, String value) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case ScanCodeType.verification:
|
|
|
|
|
_handleVerificationCode(value);
|
|
|
|
|
break;
|
|
|
|
|
case ScanCodeType.friend:
|
|
|
|
|
_handleFriendCode(value);
|
|
|
|
|
break;
|
|
|
|
|
case ScanCodeType.promotion:
|
|
|
|
|
_handlePromotionCode(value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 15:32:18 +08:00
|
|
|
|
// 处理核销码
|
|
|
|
|
void _handleVerificationCode(String value) async {
|
2025-09-13 16:14:27 +08:00
|
|
|
|
print('处理核销码: $value');
|
|
|
|
|
// 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
|
2025-09-13 16:14:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理好友码
|
|
|
|
|
void _handleFriendCode(String value) {
|
|
|
|
|
print('处理好友码: $value');
|
|
|
|
|
// 模仿抖音,去个人页面手动点击关注
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Get.toNamed('/vloger', arguments: {'memberId': value});
|
2025-09-13 16:14:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-09-18 15:33:14 +08:00
|
|
|
|
// 检测当前用户是否关注博主
|
|
|
|
|
checkFollowType(memberId) async {
|
|
|
|
|
/// 0:不是好友也没有关注
|
|
|
|
|
/// 1:你关注了对方(单向)
|
|
|
|
|
/// 2:对方关注了你(单向)
|
|
|
|
|
/// 3:互相关注(双向好友)
|
|
|
|
|
final res = await ImService.instance.checkFollowType(userIDList: [memberId]);
|
|
|
|
|
if (res.success) {
|
|
|
|
|
final followType = res.data?.first.followType ?? 0;
|
|
|
|
|
logger.i(res.data?.first.toJson());
|
|
|
|
|
followed.value = followType;
|
|
|
|
|
logger.i(followed.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理推广码,先调用IM互相关注逻辑,成功后在调用绑定关系接口
|
2025-09-17 15:32:18 +08:00
|
|
|
|
void _handlePromotionCode(String value) async {
|
2025-09-18 15:33:14 +08:00
|
|
|
|
await checkFollowType(value); // 检查当前用户是否关注了团长
|
|
|
|
|
if(followed.value == 0 || followed.value == 2){
|
|
|
|
|
final res = await ImService.instance.followUser(userIDList: [value]);
|
|
|
|
|
if (res.success) {
|
|
|
|
|
followed.value = followed.value == 0 ? 1 : 3;
|
2025-09-18 16:46:13 +08:00
|
|
|
|
final res = await Http.post(ShopApi.bindSpreadCodeId, data: {"id": value});
|
2025-09-18 15:33:14 +08:00
|
|
|
|
if (res != null && res['code'] == 200) {
|
|
|
|
|
MyDialog.toast('推广码绑定成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
|
|
|
|
Get.toNamed('/vloger', arguments: {'memberId': value});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
Get.toNamed('/vloger', arguments: {'memberId': value});
|
2025-09-15 10:32:08 +08:00
|
|
|
|
}
|
2025-09-13 16:14:27 +08:00
|
|
|
|
}
|
2025-09-17 15:32:18 +08:00
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
// 长按菜单
|
2025-08-21 10:50:38 +08:00
|
|
|
|
void showContextMenu(BuildContext context, ConversationViewModel item) {
|
2025-09-17 15:32:18 +08:00
|
|
|
|
return;
|
2025-07-21 15:46:30 +08:00
|
|
|
|
bool isLeft = posDX > MediaQuery.of(context).size.width / 2 ? false : true;
|
|
|
|
|
bool isTop = posDY > MediaQuery.of(context).size.height / 2 ? false : true;
|
|
|
|
|
|
|
|
|
|
showDialog(
|
|
|
|
|
context: context,
|
|
|
|
|
barrierColor: Colors.black12, // 遮罩透明
|
|
|
|
|
builder: (context) {
|
|
|
|
|
return Stack(
|
|
|
|
|
children: [
|
|
|
|
|
Positioned(
|
|
|
|
|
top: isTop ? posDY : posDY - 135,
|
|
|
|
|
left: isLeft ? posDX : posDX - 135,
|
|
|
|
|
width: 135,
|
|
|
|
|
child: Material(
|
|
|
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
elevation: 2.0,
|
|
|
|
|
clipBehavior: Clip.hardEdge,
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
ListTile(
|
|
|
|
|
title: const Text(
|
|
|
|
|
'设为免打扰',
|
|
|
|
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
|
|
|
|
),
|
|
|
|
|
dense: true,
|
|
|
|
|
onTap: () {},
|
|
|
|
|
),
|
|
|
|
|
ListTile(
|
|
|
|
|
title: const Text(
|
|
|
|
|
'置顶消息',
|
|
|
|
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
|
|
|
|
),
|
|
|
|
|
dense: true,
|
|
|
|
|
onTap: () {},
|
|
|
|
|
),
|
|
|
|
|
ListTile(
|
|
|
|
|
title: const Text(
|
|
|
|
|
'不显示该消息',
|
|
|
|
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
|
|
|
|
),
|
|
|
|
|
dense: true,
|
|
|
|
|
onTap: () {},
|
|
|
|
|
),
|
|
|
|
|
ListTile(
|
|
|
|
|
title: const Text(
|
|
|
|
|
'删除',
|
|
|
|
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
|
|
|
|
),
|
|
|
|
|
dense: true,
|
2025-08-21 10:50:38 +08:00
|
|
|
|
onTap: () {
|
|
|
|
|
deletConv(context, item);
|
|
|
|
|
},
|
2025-07-21 15:46:30 +08:00
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Scaffold(
|
2025-09-17 15:32:18 +08:00
|
|
|
|
// backgroundColor: Colors.grey[50],
|
2025-07-21 15:46:30 +08:00
|
|
|
|
appBar: AppBar(
|
|
|
|
|
forceMaterialTransparency: true,
|
|
|
|
|
title: Row(
|
|
|
|
|
spacing: 8.0,
|
|
|
|
|
children: [
|
|
|
|
|
Text('消息'),
|
|
|
|
|
Container(
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 4.0),
|
|
|
|
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.0), boxShadow: [
|
|
|
|
|
BoxShadow(
|
|
|
|
|
color: Colors.black.withAlpha(20),
|
|
|
|
|
offset: Offset(0.0, 1.0),
|
|
|
|
|
blurRadius: 2.0,
|
|
|
|
|
spreadRadius: 0.0,
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
child: InkWell(
|
|
|
|
|
onTap: () async {
|
|
|
|
|
if (Get.find<GlobalBadge>().totalUnread > 0) {
|
|
|
|
|
final res = await ImService.instance.clearConversationUnreadCount(conversationID: '');
|
|
|
|
|
if (res.success) {
|
|
|
|
|
MyDialog.toast('操作成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
|
|
|
|
} else {
|
|
|
|
|
MyDialog.toast(res.desc, icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
child: Row(
|
|
|
|
|
spacing: 3.0,
|
|
|
|
|
children: [
|
|
|
|
|
Icon(
|
|
|
|
|
Icons.cleaning_services_sharp,
|
|
|
|
|
size: 14.0,
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
'清除未读',
|
|
|
|
|
style: TextStyle(fontSize: 12.0),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
)),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
actions: [
|
|
|
|
|
/// 先不做搜索功能了后面再说
|
|
|
|
|
// IconButton(
|
|
|
|
|
// icon: const Icon(Icons.search),
|
|
|
|
|
// onPressed: () {},
|
|
|
|
|
// ),
|
|
|
|
|
IconButton(
|
|
|
|
|
icon: const Icon(Icons.add_circle_outline),
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
final paddingTop = MediaQuery.of(Get.context!).padding.top;
|
|
|
|
|
|
|
|
|
|
final selected = await showMenu(
|
|
|
|
|
context: Get.context!,
|
|
|
|
|
position: RelativeRect.fromLTRB(
|
|
|
|
|
double.infinity,
|
|
|
|
|
kToolbarHeight + paddingTop - 12,
|
|
|
|
|
8,
|
|
|
|
|
double.infinity,
|
|
|
|
|
),
|
|
|
|
|
color: FStyle.primaryColor,
|
|
|
|
|
elevation: 8,
|
|
|
|
|
items: [
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
value: 'group',
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Icon(Icons.group, color: Colors.white, size: 18),
|
|
|
|
|
SizedBox(width: 8),
|
|
|
|
|
Text(
|
|
|
|
|
'发起群聊',
|
|
|
|
|
style: TextStyle(color: Colors.white),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
value: 'friend',
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Icon(Icons.person_add, color: Colors.white, size: 18),
|
|
|
|
|
SizedBox(width: 8),
|
|
|
|
|
Text(
|
|
|
|
|
'添加朋友',
|
|
|
|
|
style: TextStyle(color: Colors.white),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
PopupMenuItem<String>(
|
|
|
|
|
value: 'scan',
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Icon(Icons.qr_code_scanner, color: Colors.white, size: 18),
|
|
|
|
|
SizedBox(width: 8),
|
|
|
|
|
Text(
|
|
|
|
|
'扫一扫',
|
|
|
|
|
style: TextStyle(color: Colors.white),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (selected != null) {
|
|
|
|
|
switch (selected) {
|
|
|
|
|
case 'group':
|
2025-09-03 11:25:31 +08:00
|
|
|
|
logger.w('点击了发起群聊');
|
|
|
|
|
Get.toNamed('/group');
|
2025-07-21 15:46:30 +08:00
|
|
|
|
break;
|
|
|
|
|
case 'friend':
|
2025-09-03 11:25:31 +08:00
|
|
|
|
logger.w('点击了添加朋友');
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Get.to(() => AddFriend());
|
2025-07-21 15:46:30 +08:00
|
|
|
|
break;
|
|
|
|
|
case 'scan':
|
2025-09-03 11:25:31 +08:00
|
|
|
|
logger.w('点击了扫一扫');
|
2025-09-13 16:14:27 +08:00
|
|
|
|
ScanUtil.openScanner(onResult: handleScanResult);
|
2025-07-21 15:46:30 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
body: ScrollConfiguration(
|
|
|
|
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Container(
|
|
|
|
|
margin: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
|
|
|
|
padding: EdgeInsets.all(10.0),
|
|
|
|
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [
|
|
|
|
|
BoxShadow(
|
|
|
|
|
color: Colors.black.withAlpha(10),
|
|
|
|
|
offset: Offset(0.0, 1.0),
|
|
|
|
|
blurRadius: 2.0,
|
|
|
|
|
spreadRadius: 0.0,
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
child: Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
|
|
|
children: [
|
2025-09-09 10:57:52 +08:00
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
// 去群聊列表
|
|
|
|
|
Get.toNamed('/groupList');
|
|
|
|
|
},
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
SvgPicture.asset(
|
|
|
|
|
'assets/images/svg/order.svg',
|
|
|
|
|
height: 36.0,
|
|
|
|
|
width: 36.0,
|
|
|
|
|
),
|
|
|
|
|
Text('群聊'),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-03 11:25:31 +08:00
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
//互关
|
|
|
|
|
Get.toNamed('/eachFlow');
|
|
|
|
|
},
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
SvgPicture.asset(
|
|
|
|
|
'assets/images/svg/kefu.svg',
|
|
|
|
|
height: 36.0,
|
|
|
|
|
width: 36.0,
|
|
|
|
|
),
|
|
|
|
|
Text('互关'),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-03 11:25:31 +08:00
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
// 粉丝
|
|
|
|
|
Get.toNamed('/fans');
|
|
|
|
|
},
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
SvgPicture.asset(
|
|
|
|
|
'assets/images/svg/comment.svg',
|
|
|
|
|
height: 36.0,
|
|
|
|
|
width: 36.0,
|
|
|
|
|
),
|
|
|
|
|
Text('粉丝'),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-03 11:25:31 +08:00
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
// 关注
|
|
|
|
|
Get.toNamed('/flow');
|
|
|
|
|
},
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
SvgPicture.asset(
|
|
|
|
|
'assets/images/svg/comment.svg',
|
|
|
|
|
height: 36.0,
|
|
|
|
|
width: 36.0,
|
|
|
|
|
),
|
|
|
|
|
Text('关注'),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Expanded(
|
2025-09-03 11:25:31 +08:00
|
|
|
|
child: Obx(
|
|
|
|
|
() {
|
|
|
|
|
final chatList = controller.chatList;
|
2025-08-21 10:50:38 +08:00
|
|
|
|
|
2025-09-03 11:25:31 +08:00
|
|
|
|
return ListView.builder(
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
physics: BouncingScrollPhysics(),
|
|
|
|
|
itemCount: chatList.length,
|
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
|
// logger.w(chatList[index].conversation.conversationGroupList);
|
|
|
|
|
// logger.w(chatList[index].isCustomAdmin);
|
2025-09-13 17:01:01 +08:00
|
|
|
|
// logger.w(chatList[index].conversation.recvOpt);
|
2025-09-17 15:32:18 +08:00
|
|
|
|
final item = chatList[index];
|
2025-09-13 17:01:01 +08:00
|
|
|
|
final bool quiet = [ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify_Except_At].contains(chatList[index].conversation.recvOpt ?? 0)
|
|
|
|
|
? true
|
|
|
|
|
: false; // 是否设置了免打扰
|
2025-09-03 11:25:31 +08:00
|
|
|
|
final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
|
|
|
|
|
final isAdmin =
|
|
|
|
|
chatList[index].isCustomAdmin != null && (chatList[index].isCustomAdmin?.isNotEmpty ?? false) && chatList[index].isCustomAdmin != '0';
|
2025-09-13 17:01:01 +08:00
|
|
|
|
final placeholderAsset = chatList[index].conversation.type == 2 ? 'assets/images/group.png' : 'assets/images/avatar/default.png';
|
2025-09-03 11:25:31 +08:00
|
|
|
|
// logger.e(chatList[index].isCustomAdmin);
|
2025-09-17 15:32:18 +08:00
|
|
|
|
return Slidable(
|
|
|
|
|
key: ValueKey(chatList[index].conversation.conversationID),
|
|
|
|
|
endActionPane: ActionPane(
|
|
|
|
|
motion: const DrawerMotion(), // 可以改成 StretchMotion 或 ScrollMotion ,DrawerMotion
|
|
|
|
|
children: [
|
|
|
|
|
SlidableAction(
|
|
|
|
|
onPressed: (_) {
|
|
|
|
|
//
|
|
|
|
|
},
|
|
|
|
|
backgroundColor: Colors.blue,
|
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
|
icon: Icons.delete,
|
|
|
|
|
label: '已读',
|
|
|
|
|
),
|
|
|
|
|
if (!(isAdmin || isNoFriend))
|
|
|
|
|
SlidableAction(
|
|
|
|
|
onPressed: (_) {
|
|
|
|
|
//
|
|
|
|
|
},
|
|
|
|
|
backgroundColor: FStyle.primaryColor,
|
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
|
icon: quiet ? Icons.notifications_off : Icons.notifications,
|
|
|
|
|
label: quiet ? '取消' : '免打扰',
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
child: Ink(
|
|
|
|
|
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
|
|
|
|
child: InkWell(
|
|
|
|
|
key: ValueKey(chatList[index].conversation.conversationID),
|
|
|
|
|
splashColor: Colors.grey[200],
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
|
|
|
|
child: Row(
|
|
|
|
|
spacing: 10.0,
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
// 头图
|
|
|
|
|
ClipOval(
|
|
|
|
|
child: NetworkOrAssetImage(
|
|
|
|
|
imageUrl: chatList[index].faceUrl,
|
|
|
|
|
width: 50,
|
|
|
|
|
height: 50,
|
|
|
|
|
placeholderAsset: placeholderAsset,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// 消息
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
// 昵称
|
|
|
|
|
Text(
|
|
|
|
|
chatList[index].conversation.showName ?? '未知',
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
softWrap: false,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: (isAdmin || isNoFriend) ? 20 : 16,
|
|
|
|
|
fontWeight: (isAdmin || isNoFriend) ? FontWeight.bold : FontWeight.normal),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 2.0),
|
|
|
|
|
// 消息内容
|
|
|
|
|
Text(
|
|
|
|
|
chatList[index].conversation.lastMessage != null
|
|
|
|
|
? parseMessageSummary(chatList[index].conversation.lastMessage!)
|
|
|
|
|
: '',
|
|
|
|
|
style: const TextStyle(color: Colors.grey, fontSize: 13.0),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
softWrap: false,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-17 15:32:18 +08:00
|
|
|
|
// 右侧
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
children: <Widget>[
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Visibility(
|
|
|
|
|
visible: !(isAdmin || isNoFriend),
|
|
|
|
|
child: Text(
|
|
|
|
|
// 转成日期字符串显示
|
|
|
|
|
// DateTime.fromMillisecondsSinceEpoch(
|
|
|
|
|
// (chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
|
|
|
|
|
// ).toLocal().toString().substring(0, 16), // 简单截取年月日时分
|
|
|
|
|
Utils.formatTime(
|
|
|
|
|
chatList[index].conversation.lastMessage!.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
|
|
|
|
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-17 15:32:18 +08:00
|
|
|
|
const SizedBox(height: 5.0),
|
|
|
|
|
// 数字角标
|
|
|
|
|
// class ReceiveMsgOptType {
|
|
|
|
|
// 在线正常接收消息,离线时会进行 APNs 推送
|
|
|
|
|
// static const int kTIMRecvMsgOpt_Receive = 0;
|
|
|
|
|
// 不会接收到消息,离线不会有推送通知
|
|
|
|
|
// static const int kTIMRecvMsgOpt_Not_Receive = 1;
|
|
|
|
|
// 在线正常接收消息,离线不会有推送通知
|
|
|
|
|
// static const int kTIMRecvMsgOpt_Not_Notify = 2;
|
|
|
|
|
// 在线接收消息,离线只接收 at 消息的推送
|
|
|
|
|
// static const int kTIMRecvMsgOpt_Not_Notify_Except_At = 3;
|
|
|
|
|
// 在线和离线都只接收@消息
|
|
|
|
|
// static const int kTIMRecvMsgOpt_Not_Receive_Except_At = 4;
|
|
|
|
|
// }
|
|
|
|
|
// 现阶段只允许设置为0和3
|
|
|
|
|
Visibility(
|
|
|
|
|
visible: (chatList[index].conversation.unreadCount ?? 0) > 0,
|
|
|
|
|
child: FStyle.badge(chatList[index].conversation.unreadCount ?? 0, color: quiet ? Colors.grey : Colors.red),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Visibility(
|
|
|
|
|
visible: (isAdmin || isNoFriend),
|
|
|
|
|
child: const Icon(
|
|
|
|
|
Icons.arrow_forward_ios,
|
|
|
|
|
color: Colors.blueGrey,
|
|
|
|
|
size: 14.0,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
2025-09-17 15:32:18 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-17 15:32:18 +08:00
|
|
|
|
onTap: () {
|
|
|
|
|
if (conversationTypeFromString(chatList[index].isCustomAdmin) != null) {
|
|
|
|
|
// 跳转对应的通知消息页
|
|
|
|
|
logger.e(chatList[index].isCustomAdmin);
|
|
|
|
|
Get.toNamed('/${chatList[index].isCustomAdmin}', arguments: chatList[index].conversation);
|
|
|
|
|
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
|
|
|
|
|
// 跳转陌生人消息页面
|
|
|
|
|
logger.e(chatList[index].conversation.conversationGroupList);
|
|
|
|
|
Get.toNamed('/noFriend');
|
|
|
|
|
} else if (chatList[index].conversation.type == 2) {
|
|
|
|
|
// 跳转群聊 type=0非法,1=单聊,2=群聊
|
|
|
|
|
Get.toNamed('/chatGroup', arguments: chatList[index].conversation);
|
|
|
|
|
} else {
|
|
|
|
|
// 会话id查询会话详情
|
|
|
|
|
Get.toNamed('/chat', arguments: chatList[index].conversation);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onTapDown: (TapDownDetails details) {
|
|
|
|
|
posDX = details.globalPosition.dx;
|
|
|
|
|
posDY = details.globalPosition.dy;
|
|
|
|
|
},
|
|
|
|
|
onLongPress: () {
|
|
|
|
|
showContextMenu(context, chatList[index]);
|
|
|
|
|
},
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-09-03 11:25:31 +08:00
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|