first commit
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,568 @@
|
||||
import '../controllers/month_builder_controller.dart';
|
||||
import '../controllers/month_ui_controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../calendar_builder.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
//Todo: Add Image Documentation
|
||||
///A year Drop down class--- default used in [CbMonthBuilder]
|
||||
class CbYearDropDown extends StatefulWidget {
|
||||
///
|
||||
const CbYearDropDown({
|
||||
Key? key,
|
||||
this.matchId,
|
||||
this.yearHeaderCustomizer,
|
||||
this.yearHeaderBuilder,
|
||||
this.yearButtonCustomizer,
|
||||
this.yearButtonBuilder,
|
||||
this.uiStateTag,
|
||||
this.expandedYearHeight = 200,
|
||||
this.expandedYearWidth,
|
||||
}) : super(key: key);
|
||||
|
||||
/// if u want to use same data between your Calendar Builder -- make sure to give same [matchId]
|
||||
final String? matchId;
|
||||
|
||||
///customise year Header style [YearHeaderCustomizer]
|
||||
final YearHeaderCustomizer? yearHeaderCustomizer;
|
||||
|
||||
///customise year button style [YearButtonCustomizer]
|
||||
final YearButtonCustomizer? yearButtonCustomizer;
|
||||
|
||||
///Build your custom YearDropDown Header
|
||||
final WidgetCbYearHeader? yearHeaderBuilder;
|
||||
|
||||
///Build your own year buttons
|
||||
final WidgetCbYearButton? yearButtonBuilder;
|
||||
|
||||
///Expanded Height of year dropDown
|
||||
///default = 200
|
||||
final double expandedYearHeight;
|
||||
|
||||
///Expanded Width of year dropDown
|
||||
final double? expandedYearWidth;
|
||||
|
||||
///UI controller tag
|
||||
final String? uiStateTag;
|
||||
|
||||
@override
|
||||
_CbYearDropDownState createState() => _CbYearDropDownState();
|
||||
}
|
||||
|
||||
class _CbYearDropDownState extends State<CbYearDropDown> {
|
||||
late String matchingId;
|
||||
late String uiStateTag;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initControlers();
|
||||
}
|
||||
|
||||
void initControlers() {
|
||||
uiStateTag = widget.uiStateTag ?? UniqueKey().toString();
|
||||
Get.put(MonthUiController(), tag: uiStateTag);
|
||||
|
||||
matchingId = widget.matchId ?? UniqueKey().toString();
|
||||
Get.put(MonthBuilderController(), tag: matchingId);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
Get.delete<MonthUiController>(tag: uiStateTag);
|
||||
Get.delete<MonthBuilderController>(tag: matchingId);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String matchingId = widget.matchId ?? UniqueKey().toString();
|
||||
final mUiCtr = Get.find<MonthUiController>(tag: uiStateTag);
|
||||
final size = MediaQuery.of(context).size;
|
||||
|
||||
Widget _ifBuilderEmpty = SizedBox(
|
||||
height: widget.yearHeaderCustomizer?.height ?? 40,
|
||||
width: widget.yearHeaderCustomizer?.width ?? size.width,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
GetBuilder<MonthBuilderController>(
|
||||
tag: matchingId,
|
||||
id: 'dateChangeId',
|
||||
builder: (mCtr) {
|
||||
String preMonth = mCtr.mSelectedDate.year ==
|
||||
mCtr.mSelectedYear.year
|
||||
? '${CalendarGlobals.months[mCtr.mSelectedDate.month - 1]} - '
|
||||
: '';
|
||||
String year = '$preMonth${mCtr.mSelectedYear.year}';
|
||||
return Text(
|
||||
year,
|
||||
style: widget.yearHeaderCustomizer?.titleTextStyle ??
|
||||
const TextStyle(fontWeight: FontWeight.bold),
|
||||
);
|
||||
}),
|
||||
GetBuilder<MonthUiController>(
|
||||
tag: uiStateTag,
|
||||
id: 'monthExpanded',
|
||||
builder: (mCtr) => Icon(!mCtr.isYearPickerExpanded
|
||||
? widget.yearHeaderCustomizer?.downIcon ??
|
||||
Icons.arrow_drop_down_outlined
|
||||
: widget.yearHeaderCustomizer?.upIcon ??
|
||||
Icons.arrow_drop_up_outlined)),
|
||||
],
|
||||
),
|
||||
);
|
||||
return Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
mUiCtr.onYearHeaderExpanded?.call(!mUiCtr.isYearPickerExpanded);
|
||||
mUiCtr.chageYearExpanded(
|
||||
isExpanded: !mUiCtr.isYearPickerExpanded,
|
||||
commonUpdateId: 'commonId',
|
||||
updateId: 'monthExpanded');
|
||||
},
|
||||
child: widget.yearHeaderBuilder == null
|
||||
? _ifBuilderEmpty
|
||||
: GetBuilder<MonthUiController>(
|
||||
id: 'monthExpanded',
|
||||
tag: uiStateTag,
|
||||
builder: (mUiCtr) => GetBuilder<MonthBuilderController>(
|
||||
id: 'commonId',
|
||||
tag: matchingId,
|
||||
builder: (mCtr) {
|
||||
String preMonth = mCtr.mSelectedDate.year ==
|
||||
mCtr.mSelectedYear.year
|
||||
? '${CalendarGlobals.months[mCtr.mSelectedDate.month - 1]} - '
|
||||
: '';
|
||||
String year = '$preMonth${mCtr.mSelectedYear.year}';
|
||||
return widget.yearHeaderBuilder!(
|
||||
mUiCtr.isYearPickerExpanded,
|
||||
mCtr.mSelectedDate,
|
||||
mCtr.mSelectedYear,
|
||||
year,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
_YearDropDown(
|
||||
matchId: matchingId,
|
||||
uiStateTag: uiStateTag,
|
||||
height: widget.expandedYearHeight,
|
||||
width: widget.expandedYearWidth ?? size.width,
|
||||
yearButtonBuilder: widget.yearButtonBuilder,
|
||||
yearButtonCustomizer: widget.yearButtonCustomizer,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _YearDropDown extends StatefulWidget {
|
||||
const _YearDropDown({
|
||||
Key? key,
|
||||
required this.matchId,
|
||||
required this.uiStateTag,
|
||||
required this.height,
|
||||
required this.width,
|
||||
this.yearButtonBuilder,
|
||||
this.yearButtonCustomizer,
|
||||
}) : super(key: key);
|
||||
|
||||
final String matchId;
|
||||
final String uiStateTag;
|
||||
final double height;
|
||||
final double width;
|
||||
final WidgetCbYearButton? yearButtonBuilder;
|
||||
final YearButtonCustomizer? yearButtonCustomizer;
|
||||
|
||||
@override
|
||||
__YearDropDownState createState() => __YearDropDownState();
|
||||
}
|
||||
|
||||
class __YearDropDownState extends State<_YearDropDown>
|
||||
with SingleTickerProviderStateMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MonthUiController>(
|
||||
tag: widget.uiStateTag,
|
||||
id: 'monthExpanded',
|
||||
builder: (mUiCtr) {
|
||||
///!this scrolling will not work while we are using builder
|
||||
// WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||
// Scrollable.ensureVisible(
|
||||
// mCtr.mYearPickerGlobalKey[8].currentContext!);
|
||||
// });
|
||||
return AnimatedSize(
|
||||
curve: Curves.fastOutSlowIn,
|
||||
duration: CalendarGlobals.kMinDuration,
|
||||
reverseDuration: CalendarGlobals.kMinDuration,
|
||||
child: AnimatedOpacity(
|
||||
duration: CalendarGlobals.kMaxDuration,
|
||||
opacity: mUiCtr.isYearPickerExpanded ? 1.0 : 0,
|
||||
child: SizedBox(
|
||||
height: mUiCtr.isYearPickerExpanded ? widget.height : 0,
|
||||
width: widget.width,
|
||||
child: Visibility(
|
||||
///by using [Visibility] will improve the performace
|
||||
visible: mUiCtr.isYearPickerExpanded,
|
||||
child: GetBuilder<MonthBuilderController>(
|
||||
id: 'monthExpanded',
|
||||
tag: widget.matchId,
|
||||
builder: (mCtr) => _YearBtnGridBuilder(
|
||||
uiStateTag: widget.uiStateTag,
|
||||
matchId: widget.matchId,
|
||||
height: widget.height,
|
||||
width: widget.width,
|
||||
mCtr: mCtr,
|
||||
isExpanded: mUiCtr.isYearPickerExpanded,
|
||||
yearButtonBuilder: widget.yearButtonBuilder,
|
||||
yearButtonCustomizer: widget.yearButtonCustomizer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _YearBtnGridBuilder extends StatefulWidget {
|
||||
const _YearBtnGridBuilder({
|
||||
Key? key,
|
||||
required this.height,
|
||||
required this.width,
|
||||
required this.mCtr,
|
||||
required this.isExpanded,
|
||||
required this.matchId,
|
||||
required this.uiStateTag,
|
||||
this.yearButtonBuilder,
|
||||
this.yearButtonCustomizer,
|
||||
}) : super(key: key);
|
||||
|
||||
final double height;
|
||||
final double width;
|
||||
final MonthBuilderController mCtr;
|
||||
final bool isExpanded;
|
||||
final String matchId;
|
||||
final String uiStateTag;
|
||||
final WidgetCbYearButton? yearButtonBuilder;
|
||||
final YearButtonCustomizer? yearButtonCustomizer;
|
||||
|
||||
@override
|
||||
__YearBtnGridBuilderState createState() => __YearBtnGridBuilderState();
|
||||
}
|
||||
|
||||
class __YearBtnGridBuilderState extends State<_YearBtnGridBuilder>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late ScrollController _controller;
|
||||
late double scrollPosition;
|
||||
|
||||
///[scrollPosition] it finds the exact scroll position of the [cbYearDropDown]
|
||||
///
|
||||
///
|
||||
///[.truncate] is used to remove fractional part/decimal part
|
||||
///``if val = 1.89
|
||||
///val.truncate()
|
||||
///it returns 1 and removes fractoinal part``
|
||||
void _findScrollPositon() {
|
||||
scrollPosition = (((((widget.mCtr.m16DateTimeYears.isEmpty
|
||||
? widget.mCtr.mAllYears.indexWhere((element) =>
|
||||
element.year == widget.mCtr.mSelectedYear.year)
|
||||
: widget.mCtr.m16DateTimeYears.indexWhere((element) =>
|
||||
element.year == widget.mCtr.mSelectedYear.year))) /
|
||||
4)
|
||||
.truncate() *
|
||||
(widget.height / 4)) -
|
||||
(widget.height / 4));
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_findScrollPositon();
|
||||
_controller = ScrollController(
|
||||
initialScrollOffset: scrollPosition, keepScrollOffset: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _YearBtnGridBuilder oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.isExpanded != widget.isExpanded) {
|
||||
_findScrollPositon();
|
||||
_controller.jumpTo(
|
||||
scrollPosition,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_controller.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final bool isMaxSize = widget.width >= 1200;
|
||||
return GridView.builder(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
///height of 1 button in grid
|
||||
mainAxisExtent: isMaxSize ? widget.height / 3 : widget.height / 4,
|
||||
crossAxisCount: isMaxSize ? 5 : 4,
|
||||
),
|
||||
//!same
|
||||
// SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
// maxCrossAxisExtent:widget.width / 4,
|
||||
// mainAxisExtent: widget.height / 4,
|
||||
// ),
|
||||
controller: _controller,
|
||||
addAutomaticKeepAlives: true,
|
||||
cacheExtent: 20,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: widget.mCtr.m16DateTimeYears.isEmpty
|
||||
? widget.mCtr.mAllYears.length
|
||||
: (widget.mCtr.m16DateTimeYears.length - (isMaxSize ? 1 : 0)),
|
||||
itemBuilder: (context, i) => GetBuilder<MonthBuilderController>(
|
||||
tag: widget.matchId,
|
||||
id: widget.mCtr.m16DateTimeYears.isEmpty
|
||||
? widget.mCtr.mAllYears[i]
|
||||
: widget.mCtr.m16DateTimeYears[i],
|
||||
builder: (mCtr) {
|
||||
// Global.debugLogs('Year:$i');
|
||||
DateTime thisLoopDate = mCtr.m16DateTimeYears.isEmpty
|
||||
? mCtr.mAllYears[i]
|
||||
: mCtr.m16DateTimeYears[i];
|
||||
bool isYearselected = DateUtilsCB.checkYearIsSelected(
|
||||
///
|
||||
isDisabled: DateUtilsCB.checkDayisDisabled(
|
||||
listOfDates: mCtr.mAllYears, date: thisLoopDate),
|
||||
|
||||
///
|
||||
dateSelected: mCtr.mSelectedYear,
|
||||
loopedDay: thisLoopDate,
|
||||
);
|
||||
bool isyearDisabled = DateUtilsCB.checkYearisDisabled(
|
||||
listOfDates: mCtr.mAllYears,
|
||||
date: thisLoopDate,
|
||||
);
|
||||
return FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: _YearButtons(
|
||||
matchId: widget.matchId,
|
||||
uiStateTag: widget.uiStateTag,
|
||||
isyearDisabled: isyearDisabled,
|
||||
thisLoopDate: thisLoopDate,
|
||||
isYearselected: isYearselected,
|
||||
height: widget.height,
|
||||
width: widget.width,
|
||||
builder: widget.yearButtonBuilder,
|
||||
borderColorOnDisabled:
|
||||
widget.yearButtonCustomizer?.borderColorOnDisabled,
|
||||
borderColorOnEnabled:
|
||||
widget.yearButtonCustomizer?.borderColorOnEnabled,
|
||||
borderColorOnSelected:
|
||||
widget.yearButtonCustomizer?.borderColorOnSelected,
|
||||
shrinkOnButtonPressed: widget.yearButtonCustomizer == null
|
||||
? true
|
||||
: widget.yearButtonCustomizer!.shrinkOnButtonPressed,
|
||||
hoverColor: widget.yearButtonCustomizer?.hoverColor,
|
||||
onPressed: widget.yearButtonCustomizer?.onPressed,
|
||||
splashColor: widget.yearButtonCustomizer?.splashColor,
|
||||
textColorOnDisabled:
|
||||
widget.yearButtonCustomizer?.textColorOnDisabled,
|
||||
textColorOnEnabled:
|
||||
widget.yearButtonCustomizer?.textColorOnEnabled,
|
||||
textColorOnSelected:
|
||||
widget.yearButtonCustomizer?.textColorOnSelected,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
|
||||
class _YearButtons extends StatefulWidget {
|
||||
const _YearButtons({
|
||||
Key? key,
|
||||
required this.isyearDisabled,
|
||||
required this.thisLoopDate,
|
||||
required this.isYearselected,
|
||||
required this.height,
|
||||
required this.width,
|
||||
required this.matchId,
|
||||
required this.uiStateTag,
|
||||
this.builder,
|
||||
this.borderColorOnSelected,
|
||||
this.borderColorOnDisabled,
|
||||
this.borderColorOnEnabled,
|
||||
this.textColorOnSelected,
|
||||
this.textColorOnDisabled,
|
||||
this.textColorOnEnabled,
|
||||
this.splashColor,
|
||||
this.hoverColor,
|
||||
this.onPressed,
|
||||
this.shrinkOnButtonPressed = true,
|
||||
}) : super(key: key);
|
||||
|
||||
final bool isyearDisabled;
|
||||
final DateTime thisLoopDate;
|
||||
final bool isYearselected;
|
||||
final double height;
|
||||
final double width;
|
||||
final String matchId;
|
||||
final String uiStateTag;
|
||||
final WidgetCbYearButton? builder;
|
||||
|
||||
///
|
||||
final Color? borderColorOnSelected;
|
||||
final Color? borderColorOnDisabled;
|
||||
final Color? borderColorOnEnabled;
|
||||
final Color? textColorOnSelected;
|
||||
final Color? textColorOnDisabled;
|
||||
final Color? textColorOnEnabled;
|
||||
final Color? splashColor;
|
||||
final Color? hoverColor;
|
||||
final Function(bool isButtonDisabled, DateTime selectedYear)? onPressed;
|
||||
final bool shrinkOnButtonPressed;
|
||||
@override
|
||||
__YearButtons createState() => __YearButtons();
|
||||
}
|
||||
|
||||
class __YearButtons extends State<_YearButtons>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
///Callback when a year button is pressed
|
||||
void _onYearPressed() {
|
||||
final mCtr = Get.find<MonthBuilderController>(tag: widget.matchId);
|
||||
final mUiCtr = Get.find<MonthUiController>(tag: widget.uiStateTag);
|
||||
if (widget.isyearDisabled) {
|
||||
if (widget.onPressed != null) {
|
||||
widget.onPressed!(widget.isyearDisabled, widget.thisLoopDate);
|
||||
}
|
||||
CalendarGlobals.debugLogs('Calendar_builder: year button disabled');
|
||||
} else {
|
||||
if (widget.onPressed != null) {
|
||||
widget.onPressed!(widget.isyearDisabled, widget.thisLoopDate);
|
||||
}
|
||||
|
||||
///Changes selected date
|
||||
mCtr.changeSelectedYear(
|
||||
selectedYear: widget.thisLoopDate,
|
||||
oldSelectedYear: DateTime(mCtr.mSelectedYear.year,
|
||||
mCtr.mSelectedYear.month, mCtr.mSelectedYear.day),
|
||||
updateByID: true,
|
||||
updateId: 'dateChangeId',
|
||||
commonUpdateId: 'commonId');
|
||||
|
||||
///isExpanded
|
||||
if (widget.shrinkOnButtonPressed) {
|
||||
Future.delayed(const Duration(milliseconds: 200)).then((value) {
|
||||
mUiCtr.chageYearExpanded(
|
||||
isExpanded: false,
|
||||
updateId: 'monthExpanded',
|
||||
commonUpdateId: 'commonId');
|
||||
});
|
||||
}
|
||||
|
||||
///changes the page of month pageView
|
||||
int _mPage = mCtr.mAllYears
|
||||
.indexWhere((element) => element.year == widget.thisLoopDate.year);
|
||||
mUiCtr.mPageController.jumpToPage(_mPage);
|
||||
|
||||
///saves and removes months if length exedes 3
|
||||
mCtr.addToSavedMonthDates(DateTime(mCtr.mSelectedYear.year,
|
||||
mCtr.mSelectedYear.month, mCtr.mSelectedYear.day));
|
||||
mCtr.savedMonthRemover(DateTime(widget.thisLoopDate.year,
|
||||
widget.thisLoopDate.month, widget.thisLoopDate.day));
|
||||
}
|
||||
mUiCtr.onYearButtonClicked
|
||||
?.call(widget.thisLoopDate, widget.isyearDisabled ? false : true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final txtTheme = Theme.of(context).textTheme;
|
||||
final colorTheme = Theme.of(context);
|
||||
|
||||
Widget ifBuilderisEmpty = SizedBox(
|
||||
height: (widget.height / 5.5) >= 35 ? 35 : widget.height / 5.5,
|
||||
width: (widget.width / 6) >= 150 ? 150 : (widget.width / 6),
|
||||
child: InkWell(
|
||||
onTap: _onYearPressed,
|
||||
splashColor:
|
||||
widget.isyearDisabled ? Colors.transparent : widget.splashColor,
|
||||
hoverColor: widget.isyearDisabled ? null : widget.hoverColor,
|
||||
customBorder: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(
|
||||
(widget.height / 5.5) >= 35 ? 35 : widget.height / 5.5),
|
||||
),
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(
|
||||
(widget.height / 5.5) >= 35 ? 35 : widget.height / 5.5),
|
||||
),
|
||||
border: Border.all(
|
||||
color: widget.isyearDisabled
|
||||
? widget.borderColorOnDisabled ??
|
||||
colorTheme.disabledColor.withOpacity(0.1)
|
||||
: widget.isYearselected
|
||||
? widget.borderColorOnSelected ??
|
||||
(colorTheme.brightness == Brightness.dark
|
||||
? colorTheme.colorScheme.secondary
|
||||
: Colors.black)
|
||||
: widget.borderColorOnEnabled ?? Colors.grey,
|
||||
width: widget.isyearDisabled
|
||||
? 1
|
||||
: widget.isYearselected
|
||||
? 1.5
|
||||
: 1),
|
||||
),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(widget.thisLoopDate.year.toString(),
|
||||
style: widget.isyearDisabled
|
||||
? txtTheme.titleMedium!
|
||||
.copyWith(color: widget.textColorOnDisabled)
|
||||
: widget.isYearselected
|
||||
? txtTheme.bodyLarge!
|
||||
.copyWith(color: widget.textColorOnSelected)
|
||||
: txtTheme.bodyLarge!
|
||||
.copyWith(color: widget.textColorOnEnabled)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return widget.builder == null
|
||||
? ifBuilderisEmpty
|
||||
: GestureDetector(
|
||||
onTap: () {
|
||||
///execute functions
|
||||
_onYearPressed();
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: widget.builder!(
|
||||
widget.thisLoopDate,
|
||||
(widget.height / 5.5) >= 35 ? 35 : widget.height / 5.5,
|
||||
(widget.width / 6) >= 150 ? 150 : (widget.width / 6),
|
||||
widget.isyearDisabled,
|
||||
widget.isYearselected,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
Reference in New Issue
Block a user