364 lines
14 KiB
Dart
364 lines
14 KiB
Dart
import 'package:carousel_slider/carousel_slider.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:hoshan/ui/theme/colors.dart';
|
|
import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart';
|
|
import 'package:hoshan/ui/theme/text.dart';
|
|
import 'package:hoshan/ui/widgets/components/dropdown/simple_dropdown.dart';
|
|
import 'package:shamsi_date/shamsi_date.dart';
|
|
|
|
class PersianDatePicker extends StatefulWidget {
|
|
final double dateHeight;
|
|
final Function(List<Jalali>)? onDates;
|
|
final Function(List<Jalali>)? onConfirm;
|
|
final Function()? onDismise;
|
|
final bool hasConfirm;
|
|
final bool weekSelect;
|
|
final int? dateCounts;
|
|
final List<Jalali>? selectedDates;
|
|
const PersianDatePicker({
|
|
super.key,
|
|
this.dateHeight = 32,
|
|
this.onDates,
|
|
this.dateCounts,
|
|
this.onConfirm,
|
|
this.hasConfirm = true,
|
|
this.onDismise,
|
|
this.selectedDates,
|
|
this.weekSelect = false,
|
|
});
|
|
|
|
@override
|
|
State<PersianDatePicker> createState() => _PersianDatePickerState();
|
|
}
|
|
|
|
class _PersianDatePickerState extends State<PersianDatePicker> {
|
|
final Jalali initialDate = Jalali.now();
|
|
final Jalali startDate = Jalali(1390);
|
|
late int persianMonthIndex = initialDate.month;
|
|
late String persianYearSelected = initialDate.year.toString();
|
|
final CarouselSliderControllerImpl controllerImpl =
|
|
CarouselSliderControllerImpl();
|
|
|
|
late final List<Jalali> selectedDates = widget.selectedDates ?? [];
|
|
|
|
List<String> persianMonths = [
|
|
"فروردین",
|
|
"اردیبهشت",
|
|
"خرداد",
|
|
"تیر",
|
|
"مرداد",
|
|
"شهریور",
|
|
"مهر",
|
|
"آبان",
|
|
"آذر",
|
|
"دی",
|
|
"بهمن",
|
|
"اسفند"
|
|
];
|
|
|
|
List<String> daysOfWeek = [
|
|
"شنبه",
|
|
"یک شنبه",
|
|
"دو شنبه",
|
|
"سه شنبه",
|
|
"چهار شنبه",
|
|
"پنج شنبه",
|
|
"جمعه"
|
|
];
|
|
|
|
List<int> days = [];
|
|
List<int> getDaysOfMonth() {
|
|
final List<int> days = [];
|
|
final date = Jalali(int.parse(persianYearSelected), persianMonthIndex);
|
|
int monthLength = date.monthLength;
|
|
final day = Jalali(int.parse(persianYearSelected), persianMonthIndex, 1);
|
|
int index = daysOfWeek.indexOf(day.formatter.wN);
|
|
for (var i = 0; i < index; i++) {
|
|
days.add(0);
|
|
}
|
|
for (var i = 1; i <= monthLength; i++) {
|
|
days.add(i);
|
|
}
|
|
this.days = days;
|
|
return days;
|
|
}
|
|
|
|
List<String> years = [];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
for (var i = 0; i <= initialDate.year - startDate.year; i++) {
|
|
years.add((startDate.year + i).toString());
|
|
}
|
|
years = years.reversed.toList();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'انتخاب تاریخ',
|
|
style: AppTextStyles.body4.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: Theme.of(context).colorScheme.onSurface),
|
|
),
|
|
const SizedBox(
|
|
height: 8,
|
|
),
|
|
Text(
|
|
'تاریخ روز: ${initialDate.day} ${persianMonths[initialDate.month - 1]} ${initialDate.year}',
|
|
style: AppTextStyles.body4.copyWith(
|
|
color: AppColors
|
|
.gray[context.read<ThemeModeCubit>().isDark() ? 600 : 900]),
|
|
),
|
|
const SizedBox(
|
|
height: 16,
|
|
),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: SimpleDropdown(
|
|
initialItem: years.first,
|
|
list: years,
|
|
onSelect: (selected) {
|
|
setState(() {
|
|
persianYearSelected = years[selected];
|
|
});
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(
|
|
width: 12,
|
|
),
|
|
Expanded(
|
|
child: SimpleDropdown(
|
|
initialItem: persianMonths[persianMonthIndex - 1],
|
|
list: persianMonths,
|
|
onSelect: (selected) {
|
|
controllerImpl
|
|
.animateToPage(selected)
|
|
.then((value) => setState(() {
|
|
persianMonthIndex = selected + 1;
|
|
}));
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
GridView.builder(
|
|
shrinkWrap: true,
|
|
itemCount: daysOfWeek.length,
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 7, crossAxisSpacing: 16, mainAxisSpacing: 16),
|
|
itemBuilder: (context, index) {
|
|
return Container(
|
|
alignment: Alignment.center,
|
|
child: Text(
|
|
daysOfWeek[index].split(' ').first,
|
|
style: AppTextStyles.body5,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
CarouselSlider.builder(
|
|
carouselController: controllerImpl,
|
|
itemBuilder: (context, index, realIndex) {
|
|
return GridView.builder(
|
|
shrinkWrap: true,
|
|
itemCount: getDaysOfMonth().length,
|
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 7,
|
|
crossAxisSpacing: 8,
|
|
mainAxisSpacing: 8,
|
|
mainAxisExtent: widget.dateHeight),
|
|
itemBuilder: (context, index) {
|
|
return days[index] == 0
|
|
? const SizedBox()
|
|
: GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
final date = Jalali(
|
|
int.parse(persianYearSelected),
|
|
persianMonthIndex,
|
|
days[index]);
|
|
if (date > Jalali.now()) return;
|
|
|
|
if (widget.weekSelect) {
|
|
if (selectedDates.isEmpty) {
|
|
selectedDates.add(date);
|
|
} else if (selectedDates.length == 7) {
|
|
selectedDates.clear();
|
|
selectedDates.add(date);
|
|
} else {
|
|
final today = Jalali.now();
|
|
if (date == selectedDates.first) {
|
|
selectedDates.remove(date);
|
|
} else if (date > selectedDates.first) {
|
|
if (selectedDates.first.addDays(6) <=
|
|
today) {
|
|
for (int i = 1; i <= 6; i++) {
|
|
selectedDates.add(
|
|
selectedDates.first.addDays(i));
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 1; i <= 6; i++) {
|
|
selectedDates
|
|
.add(selectedDates.first.addDays(-i));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (selectedDates.contains(date)) {
|
|
selectedDates.remove(date);
|
|
} else {
|
|
if (widget.dateCounts != null &&
|
|
selectedDates.length ==
|
|
widget.dateCounts) {
|
|
if (selectedDates.length == 1 &&
|
|
widget.dateCounts == 1) {
|
|
selectedDates.clear();
|
|
selectedDates.add(date);
|
|
}
|
|
return;
|
|
}
|
|
selectedDates.add(date);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
child: Container(
|
|
width: widget.dateHeight,
|
|
height: widget.dateHeight,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border:
|
|
persianMonthIndex == (initialDate.month) &&
|
|
(days[index]) == initialDate.day &&
|
|
(selectedDates.contains(Jalali(
|
|
int.parse(persianYearSelected),
|
|
persianMonthIndex,
|
|
days[index])))
|
|
? Border.all(
|
|
width: 2,
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.secondary)
|
|
: null,
|
|
color: persianMonthIndex ==
|
|
(initialDate.month) &&
|
|
(days[index]) == initialDate.day
|
|
? context.read<ThemeModeCubit>().isDark()
|
|
? Theme.of(context)
|
|
.colorScheme
|
|
.onSurface
|
|
.withAlpha(80)
|
|
: AppColors.primaryColor[50]
|
|
: selectedDates.contains(Jalali(
|
|
int.parse(persianYearSelected),
|
|
persianMonthIndex,
|
|
days[index]))
|
|
? Theme.of(context)
|
|
.colorScheme
|
|
.secondary
|
|
: null),
|
|
child: Text(
|
|
'${days[index]}',
|
|
style: AppTextStyles.body5.copyWith(
|
|
color: selectedDates.contains(Jalali(
|
|
int.parse(persianYearSelected),
|
|
persianMonthIndex,
|
|
days[index])) &&
|
|
!(persianMonthIndex ==
|
|
(initialDate.month) &&
|
|
(days[index]) == initialDate.day)
|
|
? Colors.white
|
|
: Theme.of(context)
|
|
.colorScheme
|
|
.onSurface),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
itemCount: persianMonths.length,
|
|
options: CarouselOptions(
|
|
viewportFraction: 1,
|
|
initialPage: persianMonthIndex - 1,
|
|
disableCenter: false,
|
|
enableInfiniteScroll: true,
|
|
reverse: false,
|
|
autoPlay: false,
|
|
autoPlayCurve: Curves.fastOutSlowIn,
|
|
enlargeCenterPage: true,
|
|
enlargeFactor: 0.3,
|
|
height: 6 * widget.dateHeight + 16,
|
|
onPageChanged: (index, reason) {
|
|
setState(() {
|
|
if (reason == CarouselPageChangedReason.manual) {
|
|
persianMonthIndex = index + 1;
|
|
}
|
|
});
|
|
},
|
|
scrollDirection: Axis.horizontal,
|
|
),
|
|
),
|
|
if (widget.hasConfirm)
|
|
Column(
|
|
children: [
|
|
Divider(
|
|
color: AppColors.gray.defaultShade,
|
|
),
|
|
Padding(
|
|
padding:
|
|
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
widget.onDismise?.call();
|
|
},
|
|
child: Text(
|
|
'انصراف',
|
|
style: AppTextStyles.body4.copyWith(
|
|
color: AppColors.gray[
|
|
context.read<ThemeModeCubit>().isDark()
|
|
? 600
|
|
: 900]),
|
|
),
|
|
),
|
|
const SizedBox(
|
|
width: 24,
|
|
),
|
|
GestureDetector(
|
|
onTap: () {
|
|
widget.onConfirm?.call(selectedDates);
|
|
},
|
|
child: Text(
|
|
'تایید',
|
|
style: AppTextStyles.body4.copyWith(
|
|
color: Theme.of(context).colorScheme.primary),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|