import 'package:flutter/material.dart'; import 'package:flutter_openim_sdk/flutter_openim_sdk.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:openim_common/openim_common.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:sprintf/sprintf.dart'; import '../../company/brand/app_tokens.dart'; import '../../company/ui/state_views.dart'; import 'conversation_logic.dart'; class ConversationPage extends StatelessWidget { final logic = Get.find(); ConversationPage({super.key}); @override Widget build(BuildContext context) { return Obx(() => Scaffold( backgroundColor: AppColors.pageBg, appBar: TitleBar( height: 56, backgroundColor: AppColors.pageBg, left: const Expanded( child: Align( alignment: Alignment.centerLeft, child: Text( '消息', style: TextStyle( fontSize: AppFont.title, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), ), ), 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( children: [ Padding( padding: const EdgeInsets.fromLTRB( AppGap.x4, 0, AppGap.x4, AppGap.x2), child: GestureDetector( onTap: logic.globalSearch, behavior: HitTestBehavior.opaque, child: Container( height: 36, decoration: BoxDecoration( color: AppColors.searchBg, borderRadius: BorderRadius.circular(AppRadius.control), ), padding: const EdgeInsets.symmetric(horizontal: AppGap.x3), alignment: Alignment.centerLeft, child: const Row( children: [ Icon(Icons.search, size: AppIconSize.sm, color: AppColors.textSecondary), SizedBox(width: AppGap.x2), Text('搜索', style: TextStyle( fontSize: AppFont.sub, color: AppColors.textSecondary)), ], ), ), ), ), Expanded(child: _conversationBody()), ], ), )); } Widget _conversationBody() { if (logic.showLoading) { return const CompanyLoadingView(); } if (logic.showFail) { return CompanyFailView(onRetry: logic.reloadConversations); } if (logic.list.isEmpty) { return const CompanyEmptyView( icon: Icons.chat_bubble_outline, title: '暂时没有会话', subtitle: '去通讯录找个同事聊聊吧', ); } return ScrollablePositionedList.builder( itemScrollController: logic.itemScrollController, itemBuilder: (_, index) => _buildItemView(logic.list.elementAt(index)), itemCount: logic.list.length, itemPositionsListener: logic.itemPositionsListener, ); } void _showConversationMenu(BuildContext context, ConversationInfo info) { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (ctx) { 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: [ ListTile( title: Text( logic.isPinned(info) ? '取消置顶' : '置顶', style: const TextStyle( fontSize: AppFont.body, color: AppColors.textPrimary), ), onTap: () { Navigator.pop(ctx); logic.pinConversation(info); }, ), const Divider(height: 0.5, color: AppColors.divider), ListTile( title: const Text( '删除', style: TextStyle( fontSize: AppFont.body, color: AppColors.danger), ), onTap: () { Navigator.pop(ctx); logic.deleteConversation(info); }, ), ], ), ), ); }, ); } Widget _buildItemView(ConversationInfo info) => Builder( builder: (context) { return InkWell( onTap: () => logic.toChat(conversationInfo: info), onLongPress: () => _showConversationMenu(context, info), splashColor: AppColors.chatBg, highlightColor: AppColors.chatBg, child: Container( height: 72, padding: const EdgeInsets.symmetric(horizontal: AppGap.x4), child: Row( children: [ AvatarView( width: 48.w, height: 48.h, text: logic.getShowName(info), url: info.faceURL, isGroup: logic.isGroupChat(info), textStyle: Styles.ts_FFFFFF_14sp_medium, ), const SizedBox(width: AppGap.x3), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Row( children: [ ConstrainedBox( constraints: BoxConstraints(maxWidth: 180.w), child: Text( logic.getShowName(info), maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: AppFont.body, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), ), const Spacer(), Text( logic.getTime(info), style: const TextStyle( fontSize: AppFont.small, color: AppColors.textSecondary), ), ], ), const SizedBox(height: AppGap.x1), Row( children: [ MatchTextView( text: logic.getContent(info), textStyle: const TextStyle( fontSize: AppFont.sub, color: AppColors.textSecondary), prefixSpan: TextSpan( text: '', children: [ if (logic.isNotDisturb(info) && logic.getUnreadCount(info) > 0) TextSpan( text: '[${sprintf(StrRes.nPieces, [ logic.getUnreadCount(info) ])}] ', style: const TextStyle( fontSize: AppFont.sub, color: AppColors.textSecondary), ), TextSpan( text: logic.getPrefixTag(info), style: const TextStyle( fontSize: AppFont.sub, color: AppColors.primary), ), ], ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const Spacer(), if (logic.isNotDisturb(info)) const Icon(Icons.notifications_off_outlined, size: AppIconSize.sm, color: AppColors.textSecondary) else UnreadCountView( count: logic.getUnreadCount(info)), ], ), ], ), ), ], ), ), ); }, ); }