From 70e95661357b266802f57c12f676173c9a4b1986 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 May 2024 16:34:08 -0600 Subject: [PATCH] use nicer looking date picker --- lib/widgets/date_picker/date_picker.dart | 166 ++++++++++-------- lib/widgets/date_picker/sw_date_picker.dart | 184 ++++++++++++++++++++ pubspec.lock | 16 +- pubspec.yaml | 2 +- 4 files changed, 291 insertions(+), 77 deletions(-) create mode 100644 lib/widgets/date_picker/sw_date_picker.dart diff --git a/lib/widgets/date_picker/date_picker.dart b/lib/widgets/date_picker/date_picker.dart index d2880d41b..054f89ee5 100644 --- a/lib/widgets/date_picker/date_picker.dart +++ b/lib/widgets/date_picker/date_picker.dart @@ -1,78 +1,108 @@ +import 'dart:math'; + +import 'package:calendar_date_picker2/calendar_date_picker2.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/constants.dart'; -import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; + +part 'sw_date_picker.dart'; Future showSWDatePicker(BuildContext context) async { - final date = await showRoundedDatePicker( + final Size size; + if (Util.isDesktop) { + size = const Size(450, 450); + } else { + final _size = MediaQuery.of(context).size; + size = Size( + _size.width - 32, + _size.height >= 550 ? 450 : _size.height - 32, + ); + } + print("====================================="); + print(size); + + final now = DateTime.now(); + + final date = await _showDatePickerDialog( context: context, - initialDate: DateTime.now(), - height: MediaQuery.of(context).size.height / 3.0, - theme: ThemeData( - primarySwatch: Util.createMaterialColor( - Theme.of(context).extension()!.accentColorDark), - ), - //TODO pick a better initial date - // 2007 chosen as that is just before bitcoin launched - firstDate: DateTime(2007), - lastDate: DateTime.now(), - borderRadius: Constants.size.circularBorderRadius * 2, - - textPositiveButton: "SELECT", - - styleDatePicker: _buildDatePickerStyle(context), - styleYearPicker: _buildYearPickerStyle(context), - ); - return date; -} - -MaterialRoundedDatePickerStyle _buildDatePickerStyle(BuildContext context) { - final baseColor = Theme.of(context).extension()!.textSubtitle2; - return MaterialRoundedDatePickerStyle( - backgroundPicker: Theme.of(context).extension()!.popupBG, - paddingMonthHeader: const EdgeInsets.only(top: 11), - colorArrowNext: Theme.of(context).extension()!.textSubtitle1, - colorArrowPrevious: - Theme.of(context).extension()!.textSubtitle1, - textStyleButtonNegative: STextStyles.datePicker600(context).copyWith( - color: baseColor, - ), - textStyleButtonPositive: STextStyles.datePicker600(context).copyWith( - color: baseColor, - ), - textStyleCurrentDayOnCalendar: STextStyles.datePicker400(context), - textStyleDayHeader: STextStyles.datePicker600(context), - textStyleDayOnCalendar: STextStyles.datePicker400(context).copyWith( - color: baseColor, - ), - textStyleDayOnCalendarDisabled: STextStyles.datePicker400(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle3, - ), - textStyleDayOnCalendarSelected: STextStyles.datePicker400(context).copyWith( - color: Theme.of(context).extension()!.textWhite, - ), - textStyleMonthYearHeader: STextStyles.datePicker600(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle1, - ), - textStyleYearButton: STextStyles.datePicker600(context).copyWith( - color: Theme.of(context).extension()!.textWhite, - ), - textStyleButtonAction: GoogleFonts.inter(), - ); -} - -MaterialRoundedYearPickerStyle _buildYearPickerStyle(BuildContext context) { - return MaterialRoundedYearPickerStyle( - backgroundPicker: Theme.of(context).extension()!.popupBG, - textStyleYear: STextStyles.datePicker600(context).copyWith( - color: Theme.of(context).extension()!.textSubtitle2, - fontSize: 16, - ), - textStyleYearSelected: STextStyles.datePicker600(context).copyWith( - fontSize: 18, + value: [now], + dialogSize: size, + config: CalendarDatePicker2WithActionButtonsConfig( + firstDate: DateTime(2007), + lastDate: now, + currentDate: now, + buttonPadding: const EdgeInsets.only( + right: 16, + ), + centerAlignModePicker: true, + selectedDayHighlightColor: + Theme.of(context).extension()!.accentColorDark, + daySplashColor: Theme.of(context) + .extension()! + .accentColorDark + .withOpacity(0.6), ), ); + return date?.first; +} + +Future?> _showDatePickerDialog({ + required BuildContext context, + required CalendarDatePicker2WithActionButtonsConfig config, + required Size dialogSize, + List value = const [], + bool useRootNavigator = true, + bool barrierDismissible = true, + Color? barrierColor = Colors.black54, + bool useSafeArea = true, + RouteSettings? routeSettings, + String? barrierLabel, + TransitionBuilder? builder, +}) { + final dialog = Dialog( + insetPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + backgroundColor: Theme.of(context).extension()!.popupBG, + surfaceTintColor: Colors.transparent, + shadowColor: Colors.transparent, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius * 2, + ), + ), + clipBehavior: Clip.antiAlias, + child: SizedBox( + width: dialogSize.width, + height: max(dialogSize.height, 410), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _SWDatePicker( + value: value, + config: config.copyWith(openedFromDialog: true), + ), + ], + ), + ), + ); + + return showDialog>( + context: context, + useRootNavigator: useRootNavigator, + routeSettings: routeSettings, + builder: (BuildContext context) { + return builder == null ? dialog : builder(context, dialog); + }, + barrierDismissible: barrierDismissible, + barrierColor: barrierColor, + barrierLabel: barrierLabel, + useSafeArea: useSafeArea, + ); } diff --git a/lib/widgets/date_picker/sw_date_picker.dart b/lib/widgets/date_picker/sw_date_picker.dart new file mode 100644 index 000000000..665787139 --- /dev/null +++ b/lib/widgets/date_picker/sw_date_picker.dart @@ -0,0 +1,184 @@ +part of 'date_picker.dart'; + +class _SWDatePicker extends StatefulWidget { + const _SWDatePicker({ + super.key, + required this.value, + required this.config, + this.onValueChanged, + this.onDisplayedMonthChanged, + this.onCancelTapped, + this.onOkTapped, + }); + final List value; + + /// Called when the user taps 'OK' button + final ValueChanged>? onValueChanged; + + /// Called when the user navigates to a new month/year in the picker. + final ValueChanged? onDisplayedMonthChanged; + + /// The calendar configurations including action buttons + final CalendarDatePicker2WithActionButtonsConfig config; + + /// The callback when cancel button is tapped + final Function? onCancelTapped; + + /// The callback when ok button is tapped + final Function? onOkTapped; + @override + State<_SWDatePicker> createState() => _SWDatePickerState(); +} + +class _SWDatePickerState extends State<_SWDatePicker> { + List _values = []; + List _editCache = []; + + @override + void initState() { + _values = widget.value; + _editCache = widget.value; + super.initState(); + } + + @override + void didUpdateWidget(covariant _SWDatePicker oldWidget) { + var isValueSame = oldWidget.value.length == widget.value.length; + + if (isValueSame) { + for (int i = 0; i < oldWidget.value.length; i++) { + final isSame = + (oldWidget.value[i] == null && widget.value[i] == null) || + DateUtils.isSameDay(oldWidget.value[i], widget.value[i]); + if (!isSame) { + isValueSame = false; + break; + } + } + } + + if (!isValueSame) { + _values = widget.value; + _editCache = widget.value; + } + + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + return Theme( + data: Theme.of(context).copyWith( + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + focusColor: Colors.transparent, + colorScheme: Theme.of(context).colorScheme.copyWith( + background: Theme.of(context).extension()!.popupBG, + onBackground: + Theme.of(context).extension()!.accentColorDark, + surface: Theme.of(context).extension()!.popupBG, + surfaceVariant: + Theme.of(context).extension()!.popupBG, + onSurface: + Theme.of(context).extension()!.accentColorDark, + onSurfaceVariant: + Theme.of(context).extension()!.accentColorDark, + surfaceTint: Colors.transparent, + shadow: Colors.transparent, + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + MediaQuery.removePadding( + context: context, + child: CalendarDatePicker2( + value: [..._editCache], + config: widget.config, + onValueChanged: (values) => _editCache = values, + onDisplayedMonthChanged: widget.onDisplayedMonthChanged, + ), + ), + SizedBox(height: widget.config.gapBetweenCalendarAndButtons ?? 10), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (!Util.isDesktop) + SizedBox( + width: widget.config.buttonPadding?.right ?? 0, + ), + ConditionalParent( + condition: !Util.isDesktop, + builder: (child) => Expanded( + child: child, + ), + child: Padding( + padding: widget.config.buttonPadding ?? EdgeInsets.zero, + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) => SizedBox( + width: 140, + child: child, + ), + child: SecondaryButton( + label: "Cancel", + buttonHeight: Util.isDesktop ? ButtonHeight.m : null, + onPressed: () { + setState( + () { + _editCache = _values; + widget.onCancelTapped?.call(); + if ((widget.config.openedFromDialog ?? false) && + (widget.config.closeDialogOnCancelTapped ?? + true)) { + Navigator.pop(context); + } + }, + ); + }, + ), + ), + ), + ), + if ((widget.config.gapBetweenCalendarAndButtons ?? 0) > 0) + SizedBox(width: widget.config.gapBetweenCalendarAndButtons), + ConditionalParent( + condition: !Util.isDesktop, + builder: (child) => Expanded( + child: child, + ), + child: Padding( + padding: widget.config.buttonPadding ?? EdgeInsets.zero, + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) => SizedBox( + width: 140, + child: child, + ), + child: PrimaryButton( + buttonHeight: Util.isDesktop ? ButtonHeight.m : null, + label: "Ok", + onPressed: () { + setState( + () { + _values = _editCache; + widget.onValueChanged?.call(_values); + widget.onOkTapped?.call(); + if ((widget.config.openedFromDialog ?? false) && + (widget.config.closeDialogOnOkTapped ?? true)) { + Navigator.pop(context, _values); + } + }, + ); + }, + ), + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 65cbfdc6e..e937fb4d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -238,6 +238,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.6.2" + calendar_date_picker2: + dependency: "direct main" + description: + name: calendar_date_picker2 + sha256: "7ff3f372faff6814a2ba69427d116fb9a3d52e28644b9de4b06db6638fdac798" + url: "https://pub.dev" + source: hosted + version: "1.0.2" characters: dependency: transitive description: @@ -757,14 +765,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - flutter_rounded_date_picker: - dependency: "direct main" - description: - name: flutter_rounded_date_picker - sha256: e6aa2dc5d3b44e8bbe85ef901be69eac59ba4136427f11f4c8b2a303e1e774e7 - url: "https://pub.dev" - source: hosted - version: "3.0.4" flutter_secure_storage: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e3b9ad5ac..b3bb574a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -124,7 +124,6 @@ dependencies: decimal: ^2.1.0 event_bus: ^2.0.0 uuid: ^3.0.5 - flutter_rounded_date_picker: ^3.0.1 crypto: ^3.0.2 barcode_scan2: ^4.2.3 wakelock: ^0.6.2 @@ -178,6 +177,7 @@ dependencies: url: https://github.com/cypherstack/espresso-cash-public.git ref: a83e375678eb22fe544dc125d29bbec0fb833882 path: packages/solana + calendar_date_picker2: ^1.0.2 dev_dependencies: flutter_test: