feat(mobile-next): 会话页接全局搜索、精简加号菜单并改为长按操作

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
编码工程师
2026-08-20 03:51:27 +08:00
co-authored by Cursor multica-agent
parent ce2a3ae059
commit 7f131457f7
@@ -1,10 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart'; import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart'; import 'package:openim_common/openim_common.dart';
import 'package:rotated_corner_decoration/rotated_corner_decoration.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sprintf/sprintf.dart'; import 'package:sprintf/sprintf.dart';
@@ -21,19 +19,13 @@ class ConversationPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Obx(() => Scaffold( return Obx(() => Scaffold(
backgroundColor: AppColors.pageBg, backgroundColor: AppColors.pageBg,
appBar: TitleBar.conversation( appBar: TitleBar(
statusStr: logic.imSdkStatus, height: 56,
isFailed: logic.isFailedSdkStatus, backgroundColor: AppColors.pageBg,
popCtrl: logic.popCtrl, left: const Expanded(
onScan: logic.scan,
onAddFriend: logic.addFriend,
onAddGroup: logic.addGroup,
onCreateGroup: logic.createGroup,
left: Expanded(
flex: 2,
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: const Text( child: Text(
'消息', '消息',
style: TextStyle( style: TextStyle(
fontSize: AppFont.title, fontSize: AppFont.title,
@@ -42,24 +34,53 @@ class ConversationPage extends StatelessWidget {
), ),
), ),
), ),
)), ),
right: PopButton(
popCtrl: logic.popCtrl,
bgRadius: AppRadius.modal,
menus: [
PopMenuInfo(
text: '发起群聊',
iconWidget: const Icon(Icons.group_outlined,
size: AppIconSize.sm, color: AppColors.textPrimary),
onTap: logic.createGroup,
),
PopMenuInfo(
text: '添加同事',
iconWidget: const Icon(Icons.person_add_alt,
size: AppIconSize.sm, color: AppColors.textPrimary),
onTap: logic.addFriend,
),
],
child: const Padding(
padding: EdgeInsets.all(AppGap.x1),
child: Icon(Icons.add,
size: AppIconSize.md, color: AppColors.textPrimary),
),
),
),
body: Column( body: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.fromLTRB( padding: const EdgeInsets.fromLTRB(
AppGap.x4, 0, AppGap.x4, AppGap.x2), AppGap.x4, 0, AppGap.x4, AppGap.x2),
child: GestureDetector(
onTap: logic.globalSearch,
behavior: HitTestBehavior.opaque,
child: Container( child: Container(
height: 36, height: 36,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.searchBg, color: AppColors.searchBg,
borderRadius: BorderRadius.circular(AppRadius.control), borderRadius: BorderRadius.circular(AppRadius.control),
), ),
padding: const EdgeInsets.symmetric(horizontal: AppGap.x3), padding:
const EdgeInsets.symmetric(horizontal: AppGap.x3),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: const Row( child: const Row(
children: [ children: [
Icon(Icons.search, Icon(Icons.search,
size: AppIconSize.sm, color: AppColors.textSecondary), size: AppIconSize.sm,
color: AppColors.textSecondary),
SizedBox(width: AppGap.x2), SizedBox(width: AppGap.x2),
Text('搜索', Text('搜索',
style: TextStyle( style: TextStyle(
@@ -69,6 +90,7 @@ class ConversationPage extends StatelessWidget {
), ),
), ),
), ),
),
Expanded(child: _conversationBody()), Expanded(child: _conversationBody()),
], ],
), ),
@@ -89,67 +111,75 @@ class ConversationPage extends StatelessWidget {
subtitle: '去通讯录找个同事聊聊吧', subtitle: '去通讯录找个同事聊聊吧',
); );
} }
return SlidableAutoCloseBehavior( return ScrollablePositionedList.builder(
child: ScrollablePositionedList.builder(
itemScrollController: logic.itemScrollController, itemScrollController: logic.itemScrollController,
itemBuilder: (_, index) => _buildConversationItemView( itemBuilder: (_, index) =>
logic.list.elementAt(index), _buildItemView(logic.list.elementAt(index)),
),
itemCount: logic.list.length, itemCount: logic.list.length,
itemPositionsListener: logic.itemPositionsListener, itemPositionsListener: logic.itemPositionsListener,
),
); );
} }
Widget _buildConversationItemView(ConversationInfo info) => Slidable( void _showConversationMenu(BuildContext context, ConversationInfo info) {
endActionPane: ActionPane( showModalBottomSheet<void>(
motion: const ScrollMotion(), context: context,
extentRatio: logic.existUnreadMsg(info) backgroundColor: Colors.transparent,
? 0.7 builder: (ctx) {
: (logic.isPinned(info) ? 0.5 : 0.4), return Padding(
padding: const EdgeInsets.fromLTRB(AppGap.x4, 0, AppGap.x4, AppGap.x6),
child: Material(
color: AppColors.pageBg,
elevation: 0,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.modal),
),
clipBehavior: Clip.antiAlias,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
CustomSlidableAction( ListTile(
onPressed: (_) => logic.pinConversation(info), title: Text(
flex: logic.isPinned(info) ? 3 : 2, logic.isPinned(info) ? '取消置顶' : '置顶',
backgroundColor: Styles.c_0089FF, style: const TextStyle(
padding: const EdgeInsets.all(1), fontSize: AppFont.body, color: AppColors.textPrimary),
child:
(logic.isPinned(info) ? StrRes.cancelTop : StrRes.top).toText
..style = Styles.ts_FFFFFF_14sp,
), ),
if (logic.existUnreadMsg(info)) onTap: () {
CustomSlidableAction( Navigator.pop(ctx);
onPressed: (_) => logic.markMessageHasRead(info), logic.pinConversation(info);
flex: 3, },
backgroundColor: Styles.c_8E9AB0,
padding: const EdgeInsets.all(1),
child: StrRes.markHasRead.toText
..style = Styles.ts_FFFFFF_14sp
..maxLines = 1,
), ),
CustomSlidableAction( const Divider(height: 0.5, color: AppColors.divider),
onPressed: (_) => logic.deleteConversation(info), ListTile(
flex: 2, title: const Text(
backgroundColor: Styles.c_FF381F, '删除',
padding: const EdgeInsets.all(1), style: TextStyle(
child: StrRes.delete.toText..style = Styles.ts_FFFFFF_14sp, fontSize: AppFont.body, color: AppColors.danger),
),
onTap: () {
Navigator.pop(ctx);
logic.deleteConversation(info);
},
), ),
], ],
), ),
child: _buildItemView(info), ),
); );
},
);
}
Widget _buildItemView(ConversationInfo info) => Ink( Widget _buildItemView(ConversationInfo info) => Builder(
child: InkWell( builder: (context) {
return InkWell(
onTap: () => logic.toChat(conversationInfo: info), onTap: () => logic.toChat(conversationInfo: info),
child: Stack( onLongPress: () => _showConversationMenu(context, info),
children: [ splashColor: AppColors.chatBg,
Container( highlightColor: AppColors.chatBg,
child: Container(
height: 72, height: 72,
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: const EdgeInsets.symmetric(horizontal: AppGap.x4),
child: Row( child: Row(
children: [
Stack(
children: [ children: [
AvatarView( AvatarView(
width: 48.w, width: 48.w,
@@ -159,9 +189,7 @@ class ConversationPage extends StatelessWidget {
isGroup: logic.isGroupChat(info), isGroup: logic.isGroupChat(info),
textStyle: Styles.ts_FFFFFF_14sp_medium, textStyle: Styles.ts_FFFFFF_14sp_medium,
), ),
], const SizedBox(width: AppGap.x3),
),
12.horizontalSpace,
Expanded( Expanded(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -176,21 +204,28 @@ class ConversationPage extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
fontSize: AppFont.body, fontSize: AppFont.body,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary, color: AppColors.textPrimary,
), ),
), ),
), ),
const Spacer(), const Spacer(),
logic.getTime(info).toText Text(
..style = Styles.ts_8E9AB0_12sp, logic.getTime(info),
style: const TextStyle(
fontSize: AppFont.small,
color: AppColors.textSecondary),
),
], ],
), ),
3.verticalSpace, const SizedBox(height: AppGap.x1),
Row( Row(
children: [ children: [
MatchTextView( MatchTextView(
text: logic.getContent(info), text: logic.getContent(info),
textStyle: Styles.ts_8E9AB0_14sp, textStyle: const TextStyle(
fontSize: AppFont.sub,
color: AppColors.textSecondary),
prefixSpan: TextSpan( prefixSpan: TextSpan(
text: '', text: '',
children: [ children: [
@@ -200,11 +235,15 @@ class ConversationPage extends StatelessWidget {
text: '[${sprintf(StrRes.nPieces, [ text: '[${sprintf(StrRes.nPieces, [
logic.getUnreadCount(info) logic.getUnreadCount(info)
])}] ', ])}] ',
style: Styles.ts_8E9AB0_14sp, style: const TextStyle(
fontSize: AppFont.sub,
color: AppColors.textSecondary),
), ),
TextSpan( TextSpan(
text: logic.getPrefixTag(info), text: logic.getPrefixTag(info),
style: Styles.ts_0089FF_14sp, style: const TextStyle(
fontSize: AppFont.sub,
color: AppColors.primary),
), ),
], ],
), ),
@@ -213,9 +252,9 @@ class ConversationPage extends StatelessWidget {
), ),
const Spacer(), const Spacer(),
if (logic.isNotDisturb(info)) if (logic.isNotDisturb(info))
ImageRes.notDisturb.toImage const Icon(Icons.notifications_off_outlined,
..width = 13.63.w size: AppIconSize.sm,
..height = 14.07.h color: AppColors.textSecondary)
else else
UnreadCountView( UnreadCountView(
count: logic.getUnreadCount(info)), count: logic.getUnreadCount(info)),
@@ -227,17 +266,7 @@ class ConversationPage extends StatelessWidget {
], ],
), ),
), ),
if (logic.isPinned(info)) );
Container( },
height: 72,
margin: EdgeInsets.only(right: 6.w),
foregroundDecoration: RotatedCornerDecoration.withColor(
color: Styles.c_0089FF,
badgeSize: Size(8.29.w, 8.29.h),
),
)
],
),
),
); );
} }