onufitness_mobile/lib/screens/rise/widgets/add_participent_bottom_sheet.dart
2026-01-13 11:36:24 +05:30

422 lines
16 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/rise/controllers/create_challenge_controller.dart';
import 'package:onufitness/screens/echoboard/widget/exclusive_connection_tile.dart';
import 'package:onufitness/screens/rise/controllers/rise_controller.dart';
class MembersSelectionPopup extends StatefulWidget {
final int selectedChallengeId;
final bool isChallengePrivacyUpdate;
final int? currentVisibilityId;
final RiseController? riseController;
const MembersSelectionPopup({
super.key,
required this.selectedChallengeId,
this.isChallengePrivacyUpdate = false,
this.currentVisibilityId,
this.riseController,
});
@override
State<MembersSelectionPopup> createState() => _MembersSelectionPopupState();
}
class _MembersSelectionPopupState extends State<MembersSelectionPopup> {
final CreateChallengeController controller = Get.put(
CreateChallengeController(),
);
final ScrollController _scrollController = ScrollController();
RxBool isChallengePrivayOpen = false.obs;
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
controller.fetchExclusiveConnections(
refresh: true,
challengeId: widget.selectedChallengeId,
);
// Initialize visibility ID if it's a privacy update
if (widget.isChallengePrivacyUpdate && widget.currentVisibilityId != null) {
controller.challengeVisibilityId.value = widget.currentVisibilityId!;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
isChallengePrivayOpen.value = widget.isChallengePrivacyUpdate;
});
}
void _onScroll() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200) {
if (controller.hasMoreDataConnection.value &&
!controller.isConnectionPaginationLoading.value) {
controller.fetchExclusiveConnections(
challengeId: widget.selectedChallengeId,
);
}
}
}
@override
void dispose() {
_scrollController.removeListener(_onScroll);
controller.selectedConnections.clear();
controller.selectedConnections.refresh();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text(
widget.isChallengePrivacyUpdate
? "Update Challenge Visibility"
: "Select Members and Groups",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.sp),
),
),
SizedBox(height: 15.h),
Divider(),
SizedBox(height: 15.h),
// Privacy Radio Buttons (only for privacy update)............................................
if (widget.isChallengePrivacyUpdate) ...[
challengeVisibilitySection(),
SizedBox(height: 15.h),
Divider(),
SizedBox(height: 15.h),
],
//............................................................................................
// Show search and member list only if exclusive or not privacy update........................
Obx(() {
bool showMembersList =
!isChallengePrivayOpen.value ||
controller.challengeVisibilityId.value == 3;
if (!showMembersList) {
return Container(
padding: EdgeInsets.symmetric(vertical: 40.h),
child: Center(
child: Column(
children: [
Icon(Icons.public, size: 48.sp, color: Colors.grey),
SizedBox(height: 12.h),
Text(
'Public challenges are visible to everyone',
style: TextStyle(
fontSize: 14.sp,
color: greyTextColor1,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.isChallengePrivacyUpdate)
Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: Text(
"Select Members and Groups",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.sp,
),
),
),
TextField(
controller: controller.connectionSearchController,
onChanged: (value) {
controller.connectionSearchValue.value = value;
},
onSubmitted: (_) {
controller.fetchExclusiveConnections(
refresh: true,
challengeId: widget.selectedChallengeId,
);
},
decoration: InputDecoration(
hintText: 'Search members...',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: controller.clearSearch,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
borderSide: BorderSide(color: lightGreyColor),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
borderSide: BorderSide(color: lightGreyColor),
),
filled: true,
fillColor: textFieldFillColor,
),
),
SizedBox(height: 12.h),
Container(
height: 300.h,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8.r),
),
child: Obx(() {
if (controller.isConnectionsFetchLoading.value &&
controller.exclusiveConnections.data?.items == null) {
return Center(child: CircularProgressIndicator());
}
final items =
controller.exclusiveConnections.data?.items ?? [];
if (items.isEmpty) {
return Center(child: Text("No members found"));
}
return ListView.builder(
controller: _scrollController,
padding: EdgeInsets.all(8.w),
itemCount:
items.length +
(controller.isConnectionPaginationLoading.value
? 1
: 0),
itemBuilder: (context, index) {
if (index == items.length) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.h),
child: Center(child: CircularProgressIndicator()),
);
}
final connection = items[index];
final String itemId =
connection.tribeId != null
? 'tribe_${connection.tribeId}'
: 'user_${connection.connectedUserId}';
return Obx(() {
final isSelected = controller.selectedIds.contains(
itemId,
);
return ExclusiveConnectionTile(
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.adminProfilePicture ?? ''
: connection.profilePicture ?? '',
isSelected: isSelected,
onTap: () {
controller.exclusiveConnectionToggleSelection(
tribeId: connection.tribeId,
connectedUserId: connection.connectedUserId,
);
},
isLocked: connection.isAdded!,
);
});
},
);
}),
),
],
);
}),
//............................................................................................
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => Get.back(),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
padding: EdgeInsets.symmetric(vertical: 14.h),
),
child: Text(
'Cancel',
style: TextStyle(
fontSize: regularSizeText,
fontWeight: FontWeight.w600,
),
),
),
),
SizedBox(width: 16.w),
Expanded(
child: Obx(
() => ElevatedButton(
onPressed: () {
if (widget.isChallengePrivacyUpdate) {
// Call update privacy API
controller
.updateChallengePrivacy(
selectedChallengeId: widget.selectedChallengeId,
)
.then((value) async {
if (value) {
Future.delayed(Duration(milliseconds: 500), () {
Get.back();
});
// Refresh the list after privacy update
await widget.riseController!
.fetchCreatedChallenges(isRefresh: true);
}
});
} else {
// Call add participant API
controller
.addParticipentToChallenge(
selectedChallengeId: widget.selectedChallengeId,
)
.then((value) {
if (value) {
Future.delayed(Duration(milliseconds: 600), () {
Get.back();
});
}
});
}
},
style: ElevatedButton.styleFrom(
backgroundColor:
(widget.isChallengePrivacyUpdate
? controller.isUpdatePrivacyLoading.value
: controller.isAddPrticipentLoading.value)
? lightGreyColor
: Color(primaryColor),
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
padding: EdgeInsets.symmetric(vertical: 14.h),
),
child:
(widget.isChallengePrivacyUpdate
? controller.isUpdatePrivacyLoading.value
: controller.isAddPrticipentLoading.value)
? SizedBox(
height: 20.h,
width: 20.w,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2.0,
),
)
: Text(
widget.isChallengePrivacyUpdate
? 'Update'
: 'Add People',
style: TextStyle(
fontSize: regularSizeText,
fontWeight: FontWeight.w600,
),
),
),
),
),
],
),
SizedBox(height: 20.h),
Container(
color: Colors.white,
padding: EdgeInsetsDirectional.only(
bottom: MediaQuery.of(context).viewPadding.bottom,
),
),
],
),
);
}
//.........................................................................................................
// Privacy visibility section widget.......................................................................
//.........................................................................................................
Widget challengeVisibilitySection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() {
return Column(
children: [
RadioListTile<int>(
title: Text(
'Public',
style: TextStyle(
fontSize: regularSizeText,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
'Anyone can join this challenge.',
style: TextStyle(fontSize: 12.sp, color: greyTextColor1),
),
value: 1,
groupValue: controller.challengeVisibilityId.value,
activeColor: Color(primaryColor),
onChanged: (int? value) {
if (value != null) {
controller.challengeVisibilityId.value = value;
// Clear selected connections when switching to public
if (value == 1) {
controller.selectedConnections.clear();
controller.selectedIds.clear();
}
}
},
contentPadding: EdgeInsets.zero,
),
RadioListTile<int>(
title: Text(
'Exclusive',
style: TextStyle(
fontSize: regularSizeText,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
'Only selected members can join this challenge.',
style: TextStyle(fontSize: 12.sp, color: greyTextColor1),
),
value: 3,
groupValue: controller.challengeVisibilityId.value,
activeColor: Color(primaryColor),
onChanged: (int? value) {
if (value != null) {
controller.challengeVisibilityId.value = value;
}
},
contentPadding: EdgeInsets.zero,
),
],
);
}),
],
);
}
}