299 lines
8.7 KiB
Dart
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)),
|
|
),
|
|
);
|
|
}
|
|
}
|