onufitness_mobile/lib/screens/chat/views/chat_user_search_screen.dart
2026-01-13 11:36:24 +05:30

366 lines
13 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:onufitness/constants/color_constant.dart';
import 'package:onufitness/constants/text_constant.dart';
import 'package:onufitness/screens/echoboard/controllers/echoboard_controller.dart';
import 'package:onufitness/screens/chat/views/chat_inside_screen.dart';
import 'package:onufitness/services/logger_service.dart';
import 'package:onufitness/utils/custom_sneakbar.dart';
import 'package:onufitness/widgets/appbars/custom_appbar.dart';
class ChatUserSearchScreen extends StatefulWidget {
const ChatUserSearchScreen({super.key});
@override
State<ChatUserSearchScreen> createState() => _ChatUserSearchScreenState();
}
class _ChatUserSearchScreenState extends State<ChatUserSearchScreen> {
final EchoBoardController controller = Get.find<EchoBoardController>();
final ScrollController _scrollController = ScrollController();
final logger = LoggerService();
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.fetchExclusiveConnections(refresh: true);
});
}
void _onScroll() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 300) {
if (controller.hasMoreDataConnection.value &&
!controller.isConnectionPaginationLoading.value) {
controller.fetchExclusiveConnections();
}
}
}
Future<void> _onRefresh() async {
await controller.fetchExclusiveConnections(refresh: true);
return Future.value();
}
void onConnectionTap({
required String? agoraGroupId,
required String? connectedUserId,
required String? userName,
required String? groupName,
required String? groupImage,
required String? userProfilePic,
}) {
// ---------- GROUP CHAT ----------
if (agoraGroupId != null && agoraGroupId.isNotEmpty) {
if (groupName == null || groupName.isEmpty) {
customSnackbar(
title: "Group Info Missing",
message: "Group name not found.",
duration: 2,
);
return;
}
Get.to(
() => ChatDetailScreen(
targetUserId: agoraGroupId,
targetUserName: groupName,
targetUserAvatar: groupImage,
isGroupMessage: true,
),
);
return;
}
// ---------- 1-1 CHAT ----------
final cleanedUserId = connectedUserId?.replaceAll('-', '');
if (cleanedUserId == null || cleanedUserId.isEmpty) {
customSnackbar(
title: "Chat Not Available",
message: "Chat window is not available.",
duration: 2,
);
return;
}
if (userName == null || userName.isEmpty) {
customSnackbar(
title: "User Info Missing",
message: "User name not found.",
duration: 2,
);
return;
}
Get.to(
() => ChatDetailScreen(
targetUserId: cleanedUserId,
targetUserName: userName,
targetUserAvatar: userProfilePic,
),
);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: 'Select Connection',
textColor: appbarTextColor,
titleFontSize: appBarHeardingText,
backgroundColor: Color(primaryColor),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () {
Get.back();
},
),
),
body: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(10.sp),
child: TextField(
controller: controller.connectionSearchController,
decoration: InputDecoration(
hintText: 'Search for members',
hintStyle: TextStyle(
fontWeight: FontWeight.w600,
color: greyBorderColor,
),
prefixIcon: const Padding(
padding: EdgeInsets.only(left: 20),
child: Icon(Icons.search),
),
suffixIcon: IconButton(
icon: const Padding(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.close),
),
onPressed: () => controller.clearSearch(),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide.none,
),
filled: true,
fillColor: textFieldFillColor,
),
onChanged: (newValue) {
controller.connectionSearchValue.value = newValue;
},
onSubmitted: (_) {
controller.fetchExclusiveConnections(refresh: true);
},
),
),
const SizedBox(height: 12),
],
),
),
Expanded(
child: Obx(
() =>
controller.isConnectionsFetchLoading.value &&
controller.exclusiveConnections.data?.items == null
? const Center(
child: CircularProgressIndicator(color: Colors.black),
)
: controller.exclusiveConnections.data?.items == null ||
controller.exclusiveConnections.data!.items!.isEmpty
? Center(
child: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: [
SizedBox(height: 150.h),
Center(child: Text('No connections found')),
],
),
),
)
: RefreshIndicator(
backgroundColor: Colors.white,
color: Colors.black,
onRefresh: _onRefresh,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
controller: _scrollController,
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount:
controller
.exclusiveConnections
.data!
.items!
.length +
(controller.isConnectionPaginationLoading.value
? 1
: 0),
itemBuilder: (_, i) {
if (i ==
controller
.exclusiveConnections
.data!
.items!
.length) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Center(
child: CircularProgressIndicator(
backgroundColor: Colors.white,
color: Colors.black,
),
),
);
}
final connection =
controller.exclusiveConnections.data!.items![i];
return SingleConnectionTile(
name:
connection.tribeId != null
? connection.tribeName ?? 'Unknown Tribe'
: connection.fullName ?? 'Unknown User',
type:
connection.tribeId != null ? 'group' : 'user',
members: connection.totalMemberCount,
imageUrl:
connection.tribeId != null
? connection.tribeImage ??
connection.adminProfilePicture!
: connection.profilePicture ?? '',
onTap: () {
onConnectionTap(
agoraGroupId: connection.agoraGroupID,
connectedUserId: connection.connectedUserId,
userName: connection.fullName,
groupName: connection.tribeName,
groupImage: connection.tribeImage,
userProfilePic: connection.profilePicture,
);
},
);
},
),
),
),
),
],
),
);
}
}
class SingleConnectionTile extends StatelessWidget {
final String name;
final String type;
final int? members;
final String imageUrl;
final VoidCallback onTap;
const SingleConnectionTile({
required this.name,
required this.type,
this.members,
required this.imageUrl,
required this.onTap,
super.key,
});
@override
Widget build(BuildContext context) {
return ListTile(
onTap: onTap,
contentPadding: EdgeInsets.symmetric(vertical: 4.h, horizontal: 8.w),
leading:
type == 'group'
? Stack(
clipBehavior: Clip.none,
children: [
// Admin profile picture
CircleAvatar(
radius: 30.r,
backgroundColor: Colors.grey[300],
backgroundImage:
imageUrl.isNotEmpty ? NetworkImage(imageUrl) : null,
child:
imageUrl.isEmpty
? Icon(
Icons.person,
size: 25.sp,
color: Color(darkGreyColor),
)
: null,
),
// +N badge
Positioned(
right: -8.r,
top: -8.r,
child: Container(
width: 40.r,
height: 40.r,
decoration: BoxDecoration(
color: const Color(0xFFDBF403),
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
alignment: Alignment.center,
child: Text(
'+${(members ?? 1) - 1}',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
),
],
)
: CircleAvatar(
radius: 30.r,
backgroundImage:
imageUrl.isNotEmpty ? NetworkImage(imageUrl) : null,
backgroundColor: Colors.grey[300],
child:
imageUrl.isEmpty
? Icon(
Icons.person,
size: 30.sp,
color: Colors.grey[600],
)
: null,
),
title: Text(
name,
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500),
),
subtitle:
type == 'group'
? Text(
'${members ?? 0} members',
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
)
: null,
trailing: Icon(Icons.arrow_forward_ios, color: Colors.grey, size: 16.sp),
);
}
}