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

299 lines
8.7 KiB
Dart

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:onufitness/constants/color_constant.dart';
import 'package:onufitness/constants/text_constant.dart';
import 'package:onufitness/routes/route_constant.dart';
import 'package:onufitness/screens/home/controllers/home_controller.dart';
import 'package:onufitness/screens/home/widgets/empty_data_widget.dart';
import 'package:onufitness/screens/rise/controllers/rise_controller.dart';
import 'package:onufitness/utils/custom_sneakbar.dart';
class ChallengePieChartSection extends StatelessWidget {
final FitnessController controller;
final RiseController riseController;
const ChallengePieChartSection({
super.key,
required this.controller,
required this.riseController,
});
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 15,
offset: Offset(0, 5),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
challengePieChartHeader(context),
SizedBox(height: 30.h),
Obx(() {
if (controller.isChallengePieChartLoading.value) {
return _buildLoadingChart();
}
if (controller.challengePieChartData.value.data == null ||
controller
.challengePieChartData
.value
.data!
.dataCount!
.isEmpty ||
controller
.challengePieChartData
.value
.data!
.dataPercentage!
.isEmpty) {
return emptyChartWidget();
}
return InkWell(
onTap: () async {
Get.toNamed(
RouteConstant.challengeListScreen,
arguments: {'tabIndex': 2},
);
await riseController.fetchJoinedChallenges(isRefresh: true);
await riseController.fetchOngoingChallenges(isRefresh: true);
await riseController.fetchUpcomingChallenges(isRefresh: true);
},
child: _buildPieChart(),
);
}),
],
),
);
}
Widget challengePieChartHeader(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Challenge Progress Overview',
style: TextStyle(
fontSize: mediumSizeText,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(child: _buildDateButtonForChallenge(context, true)),
SizedBox(width: 12.w),
Icon(Icons.arrow_forward, size: 20.w, color: Colors.grey),
SizedBox(width: 12.w),
Expanded(child: _buildDateButtonForChallenge(context, false)),
],
),
],
);
}
Widget _buildDateButtonForChallenge(BuildContext context, bool isStartDate) {
return Obx(() {
DateTime date =
isStartDate
? controller.challengePieChartStartDate.value
: controller.challengePieChartEndDate.value;
return InkWell(
onTap: () => _selectDateForChallenge(context, isStartDate),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
children: [
Icon(Icons.calendar_today, size: 15.w, color: Colors.grey[600]),
SizedBox(width: 8.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isStartDate ? 'Start Date' : 'End Date',
style: TextStyle(
fontSize: verySmallSizeText,
color: Colors.grey[600],
),
),
Text(
DateFormat('MMM dd, yyyy').format(date),
style: TextStyle(
fontSize: verySmallSizeText,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
],
),
),
],
),
),
);
});
}
Future<void> _selectDateForChallenge(
BuildContext context,
bool isStartDate,
) async {
DateTime initialDate =
isStartDate
? controller.challengePieChartStartDate.value
: controller.challengePieChartEndDate.value;
final DateTime? picked = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: DateTime(2020),
lastDate: DateTime.now(),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: Color(primaryColor),
onPrimary: Colors.white,
),
),
child: child!,
);
},
);
if (picked != null) {
if (isStartDate) {
if (picked.isBefore(controller.challengePieChartEndDate.value)) {
controller.updatePieChartDateRangeForChallenge(
picked,
controller.challengePieChartEndDate.value,
);
} else {
customSnackbar(
title: 'Invalid Date Range',
message: 'Start date must be before end date',
duration: 2,
);
}
} else {
if (picked.isAfter(controller.challengePieChartStartDate.value)) {
controller.updatePieChartDateRangeForChallenge(
controller.challengePieChartStartDate.value,
picked,
);
} else {
customSnackbar(
title: 'Invalid Date Range',
message: 'End date must be after start date',
duration: 2,
);
}
}
}
}
Widget _buildPieChart() {
final data = controller.challengePieChartData.value.data!;
final colors = [
Color(primaryColor),
Colors.orange,
Colors.blue,
Colors.purple,
Colors.green,
Colors.red,
];
List<PieChartSectionData> sections = [];
for (int i = 0; i < data.labels!.length; i++) {
sections.add(
PieChartSectionData(
value: data.dataPercentage![i],
title: '${data.dataPercentage![i].toStringAsFixed(2)}%',
color: colors[i % colors.length],
radius: 80.r,
titleStyle: TextStyle(
fontSize: smallSizeText,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
);
}
return Column(
children: [
SizedBox(
height: 250.h,
child: PieChart(
PieChartData(
sections: sections,
sectionsSpace: 2,
centerSpaceRadius: 40.r,
),
),
),
SizedBox(height: 30.h),
_buildLegend(data.labels!, data.dataCount!, colors),
],
);
}
Widget _buildLegend(
List<String> labels,
List<int> counts,
List<Color> colors,
) {
return Wrap(
spacing: 16.w,
runSpacing: 12.h,
children: List.generate(labels.length, (index) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 16.w,
height: 16.h,
decoration: BoxDecoration(
color: colors[index % colors.length],
shape: BoxShape.circle,
),
),
SizedBox(width: 8.w),
Text(
'${labels[index]} (${counts[index]})',
style: TextStyle(fontSize: smallSizeText, color: Colors.black87),
),
],
);
}),
);
}
Widget _buildLoadingChart() {
return SizedBox(
height: 350.h,
child: Center(
child: CircularProgressIndicator(color: Color(primaryColor)),
),
);
}
}