flutter/lib/pages/chat/index.dart

413 lines
16 KiB
Dart
Raw Normal View History

2025-07-21 15:46:30 +08:00
/// 聊天首页模板
library;
import 'package:flutter/material.dart';
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';
import 'package:loopin/components/scan_util.dart';
import 'package:loopin/utils/parse_message_summary.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
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>();
}
// 长按坐标点
double posDX = 0.0;
double posDY = 0.0;
// 下拉刷新
Future<void> handleRefresh() async {
await Future.delayed(Duration(seconds: 1));
setState(() {});
}
// 长按菜单
void showContextMenu(BuildContext context) {
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,
onTap: () {},
)
],
),
),
)
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
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':
print('点击了发起群聊');
break;
case 'friend':
print('点击了添加朋友');
break;
case 'scan':
print('点击了扫一扫');
ScanUtil.openScanner(onResult: (code) {
print('扫码结果:$code');
Get.snackbar('扫码成功', code);
// 在这里继续你的业务逻辑,比如跳转页面、请求接口等
});
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: [
Column(
children: [
SvgPicture.asset(
'assets/images/svg/order.svg',
height: 36.0,
width: 36.0,
),
Text('群聊'),
],
),
Column(
children: [
SvgPicture.asset(
'assets/images/svg/kefu.svg',
height: 36.0,
width: 36.0,
),
Text('互关'),
],
),
Column(
children: [
SvgPicture.asset(
'assets/images/svg/comment.svg',
height: 36.0,
width: 36.0,
),
Text('粉丝'),
],
),
Column(
children: [
SvgPicture.asset(
'assets/images/svg/comment.svg',
height: 36.0,
width: 36.0,
),
Text('关注'),
],
),
],
),
),
Expanded(
child: RefreshIndicator(
backgroundColor: Colors.white,
color: Color(0xFFFF5000),
displacement: 10.0,
onRefresh: handleRefresh,
child: Obx(() {
final chatList = controller.chatList;
return ListView.builder(
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemCount: chatList.length,
itemBuilder: (context, index) {
return Ink(
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
child: InkWell(
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: Image.network(
chatList[index].faceUrl ?? 'https://wuzhongjie.com.cn/download/logo.png',
height: 50.0,
width: 50.0,
fit: BoxFit.cover,
),
),
// 消息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
chatList[index].conversation.showName ?? '',
style: const TextStyle(fontSize: 16.0),
),
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,
),
],
),
),
// 右侧
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Visibility(
visible: chatList[index].conversation.lastMessage?.timestamp != null,
child: Text(
// 转成日期字符串显示
DateTime.fromMillisecondsSinceEpoch(
(chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
).toLocal().toString().substring(0, 16), // 简单截取年月日时分
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
),
),
const SizedBox(height: 5.0),
// 数字角标
Visibility(
visible: (chatList[index].conversation.unreadCount ?? 0) > 0,
child: FStyle.badge(chatList[index].conversation.unreadCount ?? 0),
),
],
),
Visibility(
visible: chatList[index].isCustomAdmin != '0',
child: const Icon(
Icons.arrow_forward_ios,
color: Colors.grey,
size: 12.0,
),
),
],
),
),
onTap: () {
if (chatList[index].isCustomAdmin != '0') {
// 跳转系统消息级别页面
} else {
// 会话id查询会话详情
Get.toNamed('/chat', arguments: chatList[index].conversation);
}
},
onTapDown: (TapDownDetails details) {
posDX = details.globalPosition.dx;
posDY = details.globalPosition.dy;
},
onLongPress: () {
showContextMenu(context);
},
),
);
},
);
})),
),
],
),
),
);
}
}