From 7bed950e89aedff37ce2d42928005f39b9e0cb6a Mon Sep 17 00:00:00 2001 From: Matheus Date: Sun, 23 Jul 2023 17:32:10 +0200 Subject: [PATCH 1/5] month picker --- example/lib/main.dart | 63 +++++++--- example/pubspec.lock | 34 +++--- lib/date_picker_timeline.dart | 1 + lib/extra/color.dart | 2 + lib/extra/dimen.dart | 1 + lib/extra/extensions.dart | 3 + lib/extra/style.dart | 8 +- lib/month_picker_widget.dart | 208 ++++++++++++++++++++++++++++++++++ lib/month_widget.dart | 69 +++++++++++ pubspec.lock | 34 +++--- 10 files changed, 375 insertions(+), 48 deletions(-) create mode 100644 lib/extra/extensions.dart create mode 100644 lib/month_picker_widget.dart create mode 100644 lib/month_widget.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index d64e903..0d148d2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -27,23 +27,36 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { DatePickerController _controller = DatePickerController(); - DateTime _selectedValue = DateTime.now(); - + DateTime _selectedDayValue = DateTime.now(); + DateTime _selectedMonthValue = DateTime.now(); @override void initState() { super.initState(); } + // function to get total days in a month + int daysInMonth(DateTime date) { + var firstDayThisMonth = firstDayOfMonth(date); + var firstDayNextMonth = new DateTime(date.year, date.month + 1, 1); + + return firstDayNextMonth.difference(firstDayThisMonth).inDays; + } + + // function to get the first day of the month + DateTime firstDayOfMonth(DateTime date) { + return DateTime(date.year, date.month, 1); + } + @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.replay), - onPressed: () { - _controller.animateToSelection(); - }, - ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.replay), + onPressed: () { + _controller.animateToSelection(); + }, + ), appBar: AppBar( title: Text(widget.title!), ), @@ -53,23 +66,47 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text("You Selected:"), + Text("You Selected Month:"), Padding( padding: EdgeInsets.all(10), ), - Text(_selectedValue.toString()), + Text(_selectedMonthValue.toString()), Padding( padding: EdgeInsets.all(20), ), + Text("You Selected Day:"), + Padding( + padding: EdgeInsets.all(10), + ), + Text(_selectedDayValue.toString()), + Padding( + padding: EdgeInsets.all(20), + ), + Container( + child: MonthPicker( + startDate: DateTime.now(), + height: 80, + initialSelectedDate: _selectedMonthValue, + selectionColor: Colors.black, + selectedTextColor: Colors.white, + onDateChange: (date) { + // New date selected + setState(() { + _selectedMonthValue = date; + }); + }, + ), + ), Container( child: DatePicker( - DateTime.now(), + firstDayOfMonth(_selectedMonthValue), width: 60, height: 80, controller: _controller, - initialSelectedDate: DateTime.now(), + initialSelectedDate: _selectedDayValue, selectionColor: Colors.black, selectedTextColor: Colors.white, + daysCount: daysInMonth(_selectedMonthValue), inactiveDates: [ DateTime.now().add(Duration(days: 3)), DateTime.now().add(Duration(days: 4)), @@ -78,7 +115,7 @@ class _MyHomePageState extends State { onDateChange: (date) { // New date selected setState(() { - _selectedValue = date; + _selectedDayValue = date; }); }, ), diff --git a/example/pubspec.lock b/example/pubspec.lock index c2c3800..fe6c5df 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" cupertino_icons: dependency: "direct main" description: @@ -86,18 +86,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -110,18 +110,18 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" sky_engine: dependency: transitive description: flutter @@ -171,10 +171,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" vector_math: dependency: transitive description: @@ -184,4 +184,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=3.0.0-0 <4.0.0" diff --git a/lib/date_picker_timeline.dart b/lib/date_picker_timeline.dart index dea4fb8..3cb7c4b 100644 --- a/lib/date_picker_timeline.dart +++ b/lib/date_picker_timeline.dart @@ -1,3 +1,4 @@ library date_picker_timeline; export 'date_picker_widget.dart'; +export 'month_picker_widget.dart'; diff --git a/lib/extra/color.dart b/lib/extra/color.dart index 2049190..243ab00 100644 --- a/lib/extra/color.dart +++ b/lib/extra/color.dart @@ -9,9 +9,11 @@ import 'package:flutter/material.dart'; class AppColors { AppColors._(); + static const Color defaultBackgroundColor = Color(0xFF434343); static const Color defaultDateColor = Colors.black; static const Color defaultDayColor = Colors.black; static const Color defaultMonthColor = Colors.black; static const Color defaultSelectionColor = Color(0x30000000); static const Color defaultDeactivatedColor = Color(0xFF666666); + static const Color defaultSelectedMonthColor = Colors.white; } diff --git a/lib/extra/dimen.dart b/lib/extra/dimen.dart index a5d3179..e76ea92 100644 --- a/lib/extra/dimen.dart +++ b/lib/extra/dimen.dart @@ -11,4 +11,5 @@ class Dimen { static const double dateTextSize = 24; static const double dayTextSize = 11; static const double monthTextSize = 11; + static const double selectedMonthTextSize = 13; } diff --git a/lib/extra/extensions.dart b/lib/extra/extensions.dart new file mode 100644 index 0000000..64bcf49 --- /dev/null +++ b/lib/extra/extensions.dart @@ -0,0 +1,3 @@ +extension E on String { + String lastChars(int n) => substring(length - n); +} diff --git a/lib/extra/style.dart b/lib/extra/style.dart index a72aa53..defd9c7 100644 --- a/lib/extra/style.dart +++ b/lib/extra/style.dart @@ -1,6 +1,6 @@ import 'package:date_picker_timeline/extra/color.dart'; -import 'package:flutter/material.dart'; import 'package:date_picker_timeline/extra/dimen.dart'; +import 'package:flutter/material.dart'; const TextStyle defaultMonthTextStyle = TextStyle( color: AppColors.defaultMonthColor, @@ -19,3 +19,9 @@ const TextStyle defaultDayTextStyle = TextStyle( fontSize: Dimen.dayTextSize, fontWeight: FontWeight.w500, ); + +final TextStyle defaultSelectedMonthTextStyle = const TextStyle( + color: AppColors.defaultSelectedMonthColor, + fontSize: Dimen.selectedMonthTextSize, + fontWeight: FontWeight.bold, +); diff --git a/lib/month_picker_widget.dart b/lib/month_picker_widget.dart new file mode 100644 index 0000000..dde08d5 --- /dev/null +++ b/lib/month_picker_widget.dart @@ -0,0 +1,208 @@ +import 'package:date_picker_timeline/extra/color.dart'; +import 'package:date_picker_timeline/extra/style.dart'; +import 'package:date_picker_timeline/month_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/date_symbol_data_local.dart'; + +import 'gestures/tap.dart'; + +class MonthPicker extends StatefulWidget { + /// Height of the selector + final double height; + final double width; + final DateTime startDate; + final DateTime? initialSelectedDate; + + final int monthCount; + final String locale; + final TextStyle? monthTextStyle, dateTextStyle; + final MonthPickerTimelineController? controller; + final DateChangeListener? onDateChange; + + final Color selectedTextColor; + final Color selectionColor; + final Color iconColor; + + const MonthPicker( + {Key? key, + required this.startDate, + this.initialSelectedDate, + this.width = 60, + this.monthCount = 12, + this.height = 80, + this.locale = "pt_BR", + this.monthTextStyle = defaultMonthTextStyle, + this.dateTextStyle = defaultDateTextStyle, + this.controller, + this.onDateChange, + this.selectedTextColor = Colors.white, + this.iconColor = Colors.white, + this.selectionColor = AppColors.defaultSelectionColor}) + : super(key: key); + + @override + State createState() => _MonthPickerState(); +} + +class _MonthPickerState extends State { + DateTime? _currentDate; + + final ScrollController _controller = ScrollController(); + + final monthTimelineController = MonthPickerTimelineController(); + + late final TextStyle selectedDateStyle; + late final TextStyle selectedMonthStyle; + + @override + void initState() { + initializeDateFormatting(widget.locale, null); + + _currentDate = widget.initialSelectedDate; + + selectedDateStyle = + widget.dateTextStyle!.copyWith(color: widget.selectedTextColor); + selectedMonthStyle = widget.monthTextStyle! + .copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); + + if (widget.controller != null) { + widget.controller!.setMonthTimelineState(this); + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: widget.height, + width: widget.width * widget.monthCount, + child: ListView.builder( + itemCount: widget.monthCount, + scrollDirection: Axis.horizontal, + controller: _controller, + reverse: true, + itemBuilder: (context, index) { + DateTime date = widget.startDate.subtract(Duration(days: index * 30)); + date = _firstDayOfMonth(date); + + // Check if this date is the one that is currently selected + bool isSelected = _currentDate != null + ? DateUtils.isSameMonth(date, _currentDate!) + : false; + + return MonthWidget( + width: widget.width, + locale: widget.locale, + month: date, + isSelected: isSelected, + monthTextStyle: + isSelected ? selectedMonthStyle : widget.monthTextStyle, + dateTextStyle: + isSelected ? selectedDateStyle : widget.dateTextStyle, + selectionColor: + isSelected ? widget.selectionColor : Colors.transparent, + iconColor: isSelected ? widget.iconColor : widget.selectionColor, + onDateSelected: (selectedDate) { + // A date is selected + if (widget.onDateChange != null) { + widget.onDateChange!(selectedDate); + } + setState(() { + _currentDate = selectedDate; + }); + }, + ); + }, + ), + ); + } + + // function to convert month to last day of month + DateTime _lastDayOfMonth(DateTime date) { + return DateTime(date.year, date.month + 1, 0); + } + + // function to convert month to fist day of month + DateTime _firstDayOfMonth(DateTime date) { + return DateTime(date.year, date.month, 1); + } + + bool _compareDate(DateTime date1, DateTime date2) { + return date1.day == date2.day && + date1.month == date2.month && + date1.year == date2.year; + } +} + +class MonthPickerTimelineController { + _MonthPickerState? _monthTimelineState; + + void setMonthTimelineState(_MonthPickerState state) { + _monthTimelineState = state; + } + + void jumpToSelection() { + assert(_monthTimelineState != null, + 'DatePickerController is not attached to any DatePicker View.'); + + // jump to the current Date + _monthTimelineState!._controller + .jumpTo(_calculateDateOffset(_monthTimelineState!._currentDate!)); + } + + /// This function will animate the Timeline to the currently selected Date + void animateToSelection( + {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, + 'DatePickerController is not attached to any DatePicker View.'); + + // animate to the current date + _monthTimelineState!._controller.animateTo( + _calculateDateOffset(_monthTimelineState!._currentDate!), + duration: duration, + curve: curve); + } + + /// This function will animate to any date that is passed as an argument + /// In case a date is out of range nothing will happen + void animateToDate(DateTime date, + {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, + 'MonthTimelineController is not attached to any DatePicker View.'); + + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), + duration: duration, curve: curve); + } + + /// This function will animate to any date that is passed as an argument + /// this will also set that date as the current selected date + void setDateAndAnimate(DateTime date, + {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, + 'DatePickerController is not attached to any DatePicker View.'); + + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), + duration: duration, curve: curve); + + if (date.compareTo(_monthTimelineState!.widget.startDate) >= 0 && + date.compareTo(_monthTimelineState!.widget.startDate + .add(Duration(days: _monthTimelineState!.widget.monthCount))) <= + 0) { + // date is in the range + _monthTimelineState!._currentDate = date; + } + } + + /// Calculate the number of pixels that needs to be scrolled to go to the + /// date provided in the argument + double _calculateDateOffset(DateTime date) { + final startDate = DateTime( + _monthTimelineState!.widget.startDate.year, + _monthTimelineState!.widget.startDate.month, + _monthTimelineState!.widget.startDate.day); + + int offset = date.difference(startDate).inDays; + return (offset * _monthTimelineState!.widget.width) + (offset * 6); + } +} diff --git a/lib/month_widget.dart b/lib/month_widget.dart new file mode 100644 index 0000000..2074562 --- /dev/null +++ b/lib/month_widget.dart @@ -0,0 +1,69 @@ +import 'package:date_picker_timeline/extra/extensions.dart'; +import 'package:date_picker_timeline/gestures/tap.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class MonthWidget extends StatelessWidget { + final double? width; + final DateTime month; + final TextStyle? monthTextStyle, dateTextStyle; + final Color selectionColor, iconColor; + final DateSelectionCallback? onDateSelected; + final String? locale; + final bool isSelected; + + const MonthWidget({ + required this.month, + required this.monthTextStyle, + required this.dateTextStyle, + required this.selectionColor, + required this.isSelected, + required this.iconColor, + this.width, + this.onDateSelected, + this.locale, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + child: Container( + width: width, + margin: const EdgeInsets.all(3.0), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + color: selectionColor, + ), + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top: 2.0), + child: Icon( + Icons.circle, + color: iconColor, + size: isSelected ? 12 : 8, + ), + ), + Text( + "${DateFormat("MMM", locale).format(month).replaceAll(RegExp(r'[.]+'), '').toUpperCase()}\n${DateFormat("y", locale).format(month).toUpperCase().lastChars(2)}", // Month + style: monthTextStyle, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + onTap: () { + // Check if onDateSelected is not null + if (onDateSelected != null) { + // Call the onDateSelected Function + onDateSelected?.call(month); + } + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 739f756..06c643f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" fake_async: dependency: transitive description: @@ -71,18 +71,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -95,18 +95,18 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" sky_engine: dependency: transitive description: flutter @@ -156,10 +156,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" vector_math: dependency: transitive description: @@ -177,4 +177,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.0.0-0 <4.0.0" From ef8989ad40b0700fdd2f7343f462a2c6f974989c Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 24 Jul 2023 21:25:07 +0200 Subject: [PATCH 2/5] year picker --- example/lib/main.dart | 15 +++ lib/date_picker_timeline.dart | 1 + lib/extra/color.dart | 1 + lib/extra/dimen.dart | 1 + lib/extra/style.dart | 6 + lib/year_picker_widget.dart | 204 ++++++++++++++++++++++++++++++++++ lib/year_widget.dart | 67 +++++++++++ 7 files changed, 295 insertions(+) create mode 100644 lib/year_picker_widget.dart create mode 100644 lib/year_widget.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 0d148d2..f571bca 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -120,6 +120,21 @@ class _MyHomePageState extends State { }, ), ), + Container( + child: YearPickerTimeline( + startDate: DateTime.now(), + height: 80, + initialSelectedDate: _selectedMonthValue, + selectionColor: Colors.black, + selectedTextColor: Colors.white, + onDateChange: (date) { + // New date selected + setState(() { + _selectedMonthValue = date; + }); + }, + ), + ) ], ), )); diff --git a/lib/date_picker_timeline.dart b/lib/date_picker_timeline.dart index 3cb7c4b..ae5a25b 100644 --- a/lib/date_picker_timeline.dart +++ b/lib/date_picker_timeline.dart @@ -2,3 +2,4 @@ library date_picker_timeline; export 'date_picker_widget.dart'; export 'month_picker_widget.dart'; +export 'year_picker_widget.dart'; diff --git a/lib/extra/color.dart b/lib/extra/color.dart index 243ab00..affdca6 100644 --- a/lib/extra/color.dart +++ b/lib/extra/color.dart @@ -13,6 +13,7 @@ class AppColors { static const Color defaultDateColor = Colors.black; static const Color defaultDayColor = Colors.black; static const Color defaultMonthColor = Colors.black; + static const Color defaultYearColor = Colors.black; static const Color defaultSelectionColor = Color(0x30000000); static const Color defaultDeactivatedColor = Color(0xFF666666); static const Color defaultSelectedMonthColor = Colors.white; diff --git a/lib/extra/dimen.dart b/lib/extra/dimen.dart index e76ea92..bb11a17 100644 --- a/lib/extra/dimen.dart +++ b/lib/extra/dimen.dart @@ -11,5 +11,6 @@ class Dimen { static const double dateTextSize = 24; static const double dayTextSize = 11; static const double monthTextSize = 11; + static const double yearTextSize = 18; static const double selectedMonthTextSize = 13; } diff --git a/lib/extra/style.dart b/lib/extra/style.dart index defd9c7..8f30137 100644 --- a/lib/extra/style.dart +++ b/lib/extra/style.dart @@ -2,6 +2,12 @@ import 'package:date_picker_timeline/extra/color.dart'; import 'package:date_picker_timeline/extra/dimen.dart'; import 'package:flutter/material.dart'; +const TextStyle defaultYearTextStyle = TextStyle( + color: AppColors.defaultYearColor, + fontSize: Dimen.yearTextSize, + fontWeight: FontWeight.w500, +); + const TextStyle defaultMonthTextStyle = TextStyle( color: AppColors.defaultMonthColor, fontSize: Dimen.monthTextSize, diff --git a/lib/year_picker_widget.dart b/lib/year_picker_widget.dart new file mode 100644 index 0000000..21858a4 --- /dev/null +++ b/lib/year_picker_widget.dart @@ -0,0 +1,204 @@ +import 'package:date_picker_timeline/extra/color.dart'; +import 'package:date_picker_timeline/extra/style.dart'; +import 'package:date_picker_timeline/year_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/date_symbol_data_local.dart'; + +import 'gestures/tap.dart'; + +class YearPickerTimeline extends StatefulWidget { + /// Height of the selector + final double height; + final double width; + final DateTime startDate; + final DateTime? initialSelectedDate; + + final int yearCount; + final String locale; + final TextStyle? yearTextStyle; + final YearPickerTimelineController? controller; + final DateChangeListener? onDateChange; + + final Color selectedTextColor; + final Color selectionColor; + final Color iconColor; + + const YearPickerTimeline( + {Key? key, + required this.startDate, + this.initialSelectedDate, + this.width = 60, + this.yearCount = 12, + this.height = 80, + this.locale = "pt_BR", + this.yearTextStyle = defaultYearTextStyle, + this.controller, + this.onDateChange, + this.selectedTextColor = Colors.white, + this.iconColor = Colors.white, + this.selectionColor = AppColors.defaultSelectionColor}) + : super(key: key); + + @override + State createState() => _YearPickerTimelineState(); +} + +class _YearPickerTimelineState extends State { + DateTime? _currentDate; + + final ScrollController _controller = ScrollController(); + + final yearTimelineController = YearPickerTimelineController(); + + late final TextStyle selectedYearStyle; + + @override + void initState() { + initializeDateFormatting(widget.locale, null); + + _currentDate = widget.initialSelectedDate; + + selectedYearStyle = widget.yearTextStyle! + .copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); + print(selectedYearStyle); + + if (widget.controller != null) { + widget.controller!.setMonthTimelineState(this); + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: widget.height, + width: widget.width * widget.yearCount, + child: ListView.builder( + itemCount: widget.yearCount, + scrollDirection: Axis.horizontal, + controller: _controller, + reverse: true, + itemBuilder: (context, index) { + DateTime date = + widget.startDate.subtract(Duration(days: index * 365)); + date = _firstDayOfMonth(date); + + // Check if this date is the one that is currently selected + bool isSelected = _currentDate != null + ? DateUtils.isSameMonth(date, _currentDate!) + : false; + + return YearWidget( + width: widget.width, + locale: widget.locale, + month: date, + isSelected: isSelected, + yearTextStyle: + isSelected ? selectedYearStyle : widget.yearTextStyle, + selectionColor: + isSelected ? widget.selectionColor : Colors.transparent, + iconColor: isSelected ? widget.iconColor : widget.selectionColor, + onDateSelected: (selectedDate) { + // A date is selected + if (widget.onDateChange != null) { + widget.onDateChange!(selectedDate); + } + setState(() { + _currentDate = selectedDate; + }); + }, + ); + }, + ), + ); + } + + // function to convert month to last day of month + DateTime _lastDayOfMonth(DateTime date) { + return DateTime(date.year, date.month + 1, 0); + } + + // function to convert month to fist day of month + DateTime _firstDayOfMonth(DateTime date) { + return DateTime(date.year, date.month, 1); + } + + bool _compareDate(DateTime date1, DateTime date2) { + return date1.day == date2.day && + date1.month == date2.month && + date1.year == date2.year; + } +} + +class YearPickerTimelineController { + _YearPickerTimelineState? _monthTimelineState; + + void setMonthTimelineState(_YearPickerTimelineState state) { + _monthTimelineState = state; + } + + void jumpToSelection() { + assert(_monthTimelineState != null, + 'DatePickerController is not attached to any DatePicker View.'); + + // jump to the current Date + _monthTimelineState!._controller + .jumpTo(_calculateDateOffset(_monthTimelineState!._currentDate!)); + } + + /// This function will animate the Timeline to the currently selected Date + void animateToSelection( + {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, + 'DatePickerController is not attached to any DatePicker View.'); + + // animate to the current date + _monthTimelineState!._controller.animateTo( + _calculateDateOffset(_monthTimelineState!._currentDate!), + duration: duration, + curve: curve); + } + + /// This function will animate to any date that is passed as an argument + /// In case a date is out of range nothing will happen + void animateToDate(DateTime date, + {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, + 'MonthTimelineController is not attached to any DatePicker View.'); + + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), + duration: duration, curve: curve); + } + + /// This function will animate to any date that is passed as an argument + /// this will also set that date as the current selected date + void setDateAndAnimate(DateTime date, + {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, + 'DatePickerController is not attached to any DatePicker View.'); + + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), + duration: duration, curve: curve); + + if (date.compareTo(_monthTimelineState!.widget.startDate) >= 0 && + date.compareTo(_monthTimelineState!.widget.startDate + .add(Duration(days: _monthTimelineState!.widget.yearCount))) <= + 0) { + // date is in the range + _monthTimelineState!._currentDate = date; + } + } + + /// Calculate the number of pixels that needs to be scrolled to go to the + /// date provided in the argument + double _calculateDateOffset(DateTime date) { + final startDate = DateTime( + _monthTimelineState!.widget.startDate.year, + _monthTimelineState!.widget.startDate.month, + _monthTimelineState!.widget.startDate.day); + + int offset = date.difference(startDate).inDays; + return (offset * _monthTimelineState!.widget.width) + (offset * 6); + } +} diff --git a/lib/year_widget.dart b/lib/year_widget.dart new file mode 100644 index 0000000..ddd77a5 --- /dev/null +++ b/lib/year_widget.dart @@ -0,0 +1,67 @@ +import 'package:date_picker_timeline/gestures/tap.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class YearWidget extends StatelessWidget { + final double? width; + final DateTime month; + final TextStyle? yearTextStyle; + final Color selectionColor, iconColor; + final DateSelectionCallback? onDateSelected; + final String? locale; + final bool isSelected; + + const YearWidget({ + required this.month, + required this.yearTextStyle, + required this.selectionColor, + required this.isSelected, + required this.iconColor, + this.width, + this.onDateSelected, + this.locale, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + child: Container( + width: width, + margin: const EdgeInsets.all(3.0), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + color: selectionColor, + ), + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top: 2.0), + child: Icon( + Icons.circle, + color: iconColor, + size: isSelected ? 12 : 8, + ), + ), + Text( + "${DateFormat("y", locale).format(month).toUpperCase()}", // Month + style: yearTextStyle, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + onTap: () { + // Check if onDateSelected is not null + if (onDateSelected != null) { + // Call the onDateSelected Function + onDateSelected?.call(month); + } + }, + ); + } +} From 9c5a04c777063d1a7029d3fff68d2752f84c6138 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 24 Jul 2023 21:33:18 +0200 Subject: [PATCH 3/5] year picker --- lib/year_picker_widget.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/year_picker_widget.dart b/lib/year_picker_widget.dart index 21858a4..0ed1977 100644 --- a/lib/year_picker_widget.dart +++ b/lib/year_picker_widget.dart @@ -60,7 +60,6 @@ class _YearPickerTimelineState extends State { selectedYearStyle = widget.yearTextStyle! .copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); - print(selectedYearStyle); if (widget.controller != null) { widget.controller!.setMonthTimelineState(this); From 57b139e5a842fe6dd4362ddc7ff920b643351093 Mon Sep 17 00:00:00 2001 From: Matheus Date: Fri, 28 Jun 2024 17:03:09 +0200 Subject: [PATCH 4/5] change intl version to any --- example/pubspec.lock | 75 +++++++++++++++++++++++++++++--------------- pubspec.lock | 75 +++++++++++++++++++++++++++++--------------- pubspec.yaml | 2 +- 3 files changed, 101 insertions(+), 51 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index fe6c5df..973c596 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: @@ -78,50 +78,66 @@ packages: dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" - js: + version: "0.19.0" + leak_tracker: dependency: transitive description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.12.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" sky_engine: dependency: transitive description: flutter @@ -131,26 +147,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -171,10 +187,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -183,5 +199,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + url: "https://pub.dev" + source: hosted + version: "14.2.4" sdks: - dart: ">=3.0.0-0 <4.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.lock b/pubspec.lock index 06c643f..8efa242 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" fake_async: dependency: transitive description: @@ -63,50 +63,66 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" - js: + version: "0.19.0" + leak_tracker: dependency: transitive description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.12.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" sky_engine: dependency: transitive description: flutter @@ -116,26 +132,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -156,10 +172,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -168,6 +184,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + url: "https://pub.dev" + source: hosted + version: "14.2.4" yaml: dependency: "direct dev" description: @@ -177,4 +201,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0-0 <4.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 704029c..4730156 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - intl: ^0.18.0 + intl: any dev_dependencies: flutter_test: From 989b676f40db2311baa64f0eae7a56f7ca9fb66a Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 1 Jul 2024 23:53:50 +0200 Subject: [PATCH 5/5] show icon --- example/lib/main.dart | 4 +- example/pubspec.lock | 67 ++++++++---------------- lib/month_picker_widget.dart | 72 +++++++++----------------- lib/month_widget.dart | 17 ++++--- lib/year_picker_widget.dart | 99 ++++++++++++++---------------------- lib/year_widget.dart | 17 ++++--- 6 files changed, 107 insertions(+), 169 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index f571bca..b2621bf 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -85,7 +85,7 @@ class _MyHomePageState extends State { Container( child: MonthPicker( startDate: DateTime.now(), - height: 80, + height: 50, initialSelectedDate: _selectedMonthValue, selectionColor: Colors.black, selectedTextColor: Colors.white, @@ -123,7 +123,7 @@ class _MyHomePageState extends State { Container( child: YearPickerTimeline( startDate: DateTime.now(), - height: 80, + height: 45, initialSelectedDate: _selectedMonthValue, selectionColor: Colors.black, selectedTextColor: Colors.white, diff --git a/example/pubspec.lock b/example/pubspec.lock index 973c596..8cfebe8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -82,62 +82,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://pub.dev" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.3" sky_engine: dependency: transitive description: flutter @@ -155,18 +131,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -187,10 +163,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.6.0" vector_math: dependency: transitive description: @@ -199,14 +175,13 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: + web: dependency: transitive description: - name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "0.1.4-beta" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.1.0-185.0.dev <4.0.0" diff --git a/lib/month_picker_widget.dart b/lib/month_picker_widget.dart index dde08d5..5a85680 100644 --- a/lib/month_picker_widget.dart +++ b/lib/month_picker_widget.dart @@ -22,6 +22,7 @@ class MonthPicker extends StatefulWidget { final Color selectedTextColor; final Color selectionColor; final Color iconColor; + final bool showIcon; const MonthPicker( {Key? key, @@ -37,7 +38,8 @@ class MonthPicker extends StatefulWidget { this.onDateChange, this.selectedTextColor = Colors.white, this.iconColor = Colors.white, - this.selectionColor = AppColors.defaultSelectionColor}) + this.selectionColor = AppColors.defaultSelectionColor, + this.showIcon = false}) : super(key: key); @override @@ -60,10 +62,8 @@ class _MonthPickerState extends State { _currentDate = widget.initialSelectedDate; - selectedDateStyle = - widget.dateTextStyle!.copyWith(color: widget.selectedTextColor); - selectedMonthStyle = widget.monthTextStyle! - .copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); + selectedDateStyle = widget.dateTextStyle!.copyWith(color: widget.selectedTextColor); + selectedMonthStyle = widget.monthTextStyle!.copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); if (widget.controller != null) { widget.controller!.setMonthTimelineState(this); @@ -87,21 +87,17 @@ class _MonthPickerState extends State { date = _firstDayOfMonth(date); // Check if this date is the one that is currently selected - bool isSelected = _currentDate != null - ? DateUtils.isSameMonth(date, _currentDate!) - : false; + bool isSelected = _currentDate != null ? DateUtils.isSameMonth(date, _currentDate!) : false; return MonthWidget( width: widget.width, locale: widget.locale, + showIcon: widget.showIcon, month: date, isSelected: isSelected, - monthTextStyle: - isSelected ? selectedMonthStyle : widget.monthTextStyle, - dateTextStyle: - isSelected ? selectedDateStyle : widget.dateTextStyle, - selectionColor: - isSelected ? widget.selectionColor : Colors.transparent, + monthTextStyle: isSelected ? selectedMonthStyle : widget.monthTextStyle, + dateTextStyle: isSelected ? selectedDateStyle : widget.dateTextStyle, + selectionColor: isSelected ? widget.selectionColor : Colors.transparent, iconColor: isSelected ? widget.iconColor : widget.selectionColor, onDateSelected: (selectedDate) { // A date is selected @@ -129,9 +125,7 @@ class _MonthPickerState extends State { } bool _compareDate(DateTime date1, DateTime date2) { - return date1.day == date2.day && - date1.month == date2.month && - date1.year == date2.year; + return date1.day == date2.day && date1.month == date2.month && date1.year == date2.year; } } @@ -143,52 +137,38 @@ class MonthPickerTimelineController { } void jumpToSelection() { - assert(_monthTimelineState != null, - 'DatePickerController is not attached to any DatePicker View.'); + assert(_monthTimelineState != null, 'DatePickerController is not attached to any DatePicker View.'); // jump to the current Date - _monthTimelineState!._controller - .jumpTo(_calculateDateOffset(_monthTimelineState!._currentDate!)); + _monthTimelineState!._controller.jumpTo(_calculateDateOffset(_monthTimelineState!._currentDate!)); } /// This function will animate the Timeline to the currently selected Date - void animateToSelection( - {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { - assert(_monthTimelineState != null, - 'DatePickerController is not attached to any DatePicker View.'); + void animateToSelection({duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, 'DatePickerController is not attached to any DatePicker View.'); // animate to the current date - _monthTimelineState!._controller.animateTo( - _calculateDateOffset(_monthTimelineState!._currentDate!), - duration: duration, - curve: curve); + _monthTimelineState!._controller + .animateTo(_calculateDateOffset(_monthTimelineState!._currentDate!), duration: duration, curve: curve); } /// This function will animate to any date that is passed as an argument /// In case a date is out of range nothing will happen - void animateToDate(DateTime date, - {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { - assert(_monthTimelineState != null, - 'MonthTimelineController is not attached to any DatePicker View.'); + void animateToDate(DateTime date, {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, 'MonthTimelineController is not attached to any DatePicker View.'); - _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), - duration: duration, curve: curve); + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), duration: duration, curve: curve); } /// This function will animate to any date that is passed as an argument /// this will also set that date as the current selected date - void setDateAndAnimate(DateTime date, - {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { - assert(_monthTimelineState != null, - 'DatePickerController is not attached to any DatePicker View.'); + void setDateAndAnimate(DateTime date, {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, 'DatePickerController is not attached to any DatePicker View.'); - _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), - duration: duration, curve: curve); + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), duration: duration, curve: curve); if (date.compareTo(_monthTimelineState!.widget.startDate) >= 0 && - date.compareTo(_monthTimelineState!.widget.startDate - .add(Duration(days: _monthTimelineState!.widget.monthCount))) <= - 0) { + date.compareTo(_monthTimelineState!.widget.startDate.add(Duration(days: _monthTimelineState!.widget.monthCount))) <= 0) { // date is in the range _monthTimelineState!._currentDate = date; } @@ -197,9 +177,7 @@ class MonthPickerTimelineController { /// Calculate the number of pixels that needs to be scrolled to go to the /// date provided in the argument double _calculateDateOffset(DateTime date) { - final startDate = DateTime( - _monthTimelineState!.widget.startDate.year, - _monthTimelineState!.widget.startDate.month, + final startDate = DateTime(_monthTimelineState!.widget.startDate.year, _monthTimelineState!.widget.startDate.month, _monthTimelineState!.widget.startDate.day); int offset = date.difference(startDate).inDays; diff --git a/lib/month_widget.dart b/lib/month_widget.dart index 2074562..8661169 100644 --- a/lib/month_widget.dart +++ b/lib/month_widget.dart @@ -11,6 +11,7 @@ class MonthWidget extends StatelessWidget { final DateSelectionCallback? onDateSelected; final String? locale; final bool isSelected; + final bool showIcon; const MonthWidget({ required this.month, @@ -22,6 +23,7 @@ class MonthWidget extends StatelessWidget { this.width, this.onDateSelected, this.locale, + this.showIcon = false, }); @override @@ -40,14 +42,15 @@ class MonthWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.only(top: 2.0), - child: Icon( - Icons.circle, - color: iconColor, - size: isSelected ? 12 : 8, + if (showIcon) + Padding( + padding: const EdgeInsets.only(top: 2.0), + child: Icon( + Icons.circle, + color: iconColor, + size: isSelected ? 12 : 8, + ), ), - ), Text( "${DateFormat("MMM", locale).format(month).replaceAll(RegExp(r'[.]+'), '').toUpperCase()}\n${DateFormat("y", locale).format(month).toUpperCase().lastChars(2)}", // Month style: monthTextStyle, diff --git a/lib/year_picker_widget.dart b/lib/year_picker_widget.dart index 0ed1977..d412a7c 100644 --- a/lib/year_picker_widget.dart +++ b/lib/year_picker_widget.dart @@ -22,22 +22,24 @@ class YearPickerTimeline extends StatefulWidget { final Color selectedTextColor; final Color selectionColor; final Color iconColor; - - const YearPickerTimeline( - {Key? key, - required this.startDate, - this.initialSelectedDate, - this.width = 60, - this.yearCount = 12, - this.height = 80, - this.locale = "pt_BR", - this.yearTextStyle = defaultYearTextStyle, - this.controller, - this.onDateChange, - this.selectedTextColor = Colors.white, - this.iconColor = Colors.white, - this.selectionColor = AppColors.defaultSelectionColor}) - : super(key: key); + final bool showIcon; + + const YearPickerTimeline({ + Key? key, + required this.startDate, + this.initialSelectedDate, + this.width = 60, + this.yearCount = 12, + this.height = 80, + this.locale = "pt_BR", + this.yearTextStyle = defaultYearTextStyle, + this.controller, + this.onDateChange, + this.selectedTextColor = Colors.white, + this.iconColor = Colors.white, + this.selectionColor = AppColors.defaultSelectionColor, + this.showIcon = false, + }) : super(key: key); @override State createState() => _YearPickerTimelineState(); @@ -58,8 +60,7 @@ class _YearPickerTimelineState extends State { _currentDate = widget.initialSelectedDate; - selectedYearStyle = widget.yearTextStyle! - .copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); + selectedYearStyle = widget.yearTextStyle!.copyWith(color: widget.selectedTextColor, fontWeight: FontWeight.bold); if (widget.controller != null) { widget.controller!.setMonthTimelineState(this); @@ -79,24 +80,20 @@ class _YearPickerTimelineState extends State { controller: _controller, reverse: true, itemBuilder: (context, index) { - DateTime date = - widget.startDate.subtract(Duration(days: index * 365)); + DateTime date = widget.startDate.subtract(Duration(days: index * 365)); date = _firstDayOfMonth(date); // Check if this date is the one that is currently selected - bool isSelected = _currentDate != null - ? DateUtils.isSameMonth(date, _currentDate!) - : false; + bool isSelected = _currentDate != null ? DateUtils.isSameMonth(date, _currentDate!) : false; return YearWidget( width: widget.width, locale: widget.locale, + showIcon: widget.showIcon, month: date, isSelected: isSelected, - yearTextStyle: - isSelected ? selectedYearStyle : widget.yearTextStyle, - selectionColor: - isSelected ? widget.selectionColor : Colors.transparent, + yearTextStyle: isSelected ? selectedYearStyle : widget.yearTextStyle, + selectionColor: isSelected ? widget.selectionColor : Colors.transparent, iconColor: isSelected ? widget.iconColor : widget.selectionColor, onDateSelected: (selectedDate) { // A date is selected @@ -124,9 +121,7 @@ class _YearPickerTimelineState extends State { } bool _compareDate(DateTime date1, DateTime date2) { - return date1.day == date2.day && - date1.month == date2.month && - date1.year == date2.year; + return date1.day == date2.day && date1.month == date2.month && date1.year == date2.year; } } @@ -138,52 +133,38 @@ class YearPickerTimelineController { } void jumpToSelection() { - assert(_monthTimelineState != null, - 'DatePickerController is not attached to any DatePicker View.'); + assert(_monthTimelineState != null, 'DatePickerController is not attached to any DatePicker View.'); // jump to the current Date - _monthTimelineState!._controller - .jumpTo(_calculateDateOffset(_monthTimelineState!._currentDate!)); + _monthTimelineState!._controller.jumpTo(_calculateDateOffset(_monthTimelineState!._currentDate!)); } /// This function will animate the Timeline to the currently selected Date - void animateToSelection( - {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { - assert(_monthTimelineState != null, - 'DatePickerController is not attached to any DatePicker View.'); + void animateToSelection({duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, 'DatePickerController is not attached to any DatePicker View.'); // animate to the current date - _monthTimelineState!._controller.animateTo( - _calculateDateOffset(_monthTimelineState!._currentDate!), - duration: duration, - curve: curve); + _monthTimelineState!._controller + .animateTo(_calculateDateOffset(_monthTimelineState!._currentDate!), duration: duration, curve: curve); } /// This function will animate to any date that is passed as an argument /// In case a date is out of range nothing will happen - void animateToDate(DateTime date, - {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { - assert(_monthTimelineState != null, - 'MonthTimelineController is not attached to any DatePicker View.'); + void animateToDate(DateTime date, {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, 'MonthTimelineController is not attached to any DatePicker View.'); - _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), - duration: duration, curve: curve); + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), duration: duration, curve: curve); } /// This function will animate to any date that is passed as an argument /// this will also set that date as the current selected date - void setDateAndAnimate(DateTime date, - {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { - assert(_monthTimelineState != null, - 'DatePickerController is not attached to any DatePicker View.'); + void setDateAndAnimate(DateTime date, {duration = const Duration(milliseconds: 500), curve = Curves.linear}) { + assert(_monthTimelineState != null, 'DatePickerController is not attached to any DatePicker View.'); - _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), - duration: duration, curve: curve); + _monthTimelineState!._controller.animateTo(_calculateDateOffset(date), duration: duration, curve: curve); if (date.compareTo(_monthTimelineState!.widget.startDate) >= 0 && - date.compareTo(_monthTimelineState!.widget.startDate - .add(Duration(days: _monthTimelineState!.widget.yearCount))) <= - 0) { + date.compareTo(_monthTimelineState!.widget.startDate.add(Duration(days: _monthTimelineState!.widget.yearCount))) <= 0) { // date is in the range _monthTimelineState!._currentDate = date; } @@ -192,9 +173,7 @@ class YearPickerTimelineController { /// Calculate the number of pixels that needs to be scrolled to go to the /// date provided in the argument double _calculateDateOffset(DateTime date) { - final startDate = DateTime( - _monthTimelineState!.widget.startDate.year, - _monthTimelineState!.widget.startDate.month, + final startDate = DateTime(_monthTimelineState!.widget.startDate.year, _monthTimelineState!.widget.startDate.month, _monthTimelineState!.widget.startDate.day); int offset = date.difference(startDate).inDays; diff --git a/lib/year_widget.dart b/lib/year_widget.dart index ddd77a5..19f752d 100644 --- a/lib/year_widget.dart +++ b/lib/year_widget.dart @@ -10,6 +10,7 @@ class YearWidget extends StatelessWidget { final DateSelectionCallback? onDateSelected; final String? locale; final bool isSelected; + final bool showIcon; const YearWidget({ required this.month, @@ -20,6 +21,7 @@ class YearWidget extends StatelessWidget { this.width, this.onDateSelected, this.locale, + this.showIcon = false, }); @override @@ -38,14 +40,15 @@ class YearWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.only(top: 2.0), - child: Icon( - Icons.circle, - color: iconColor, - size: isSelected ? 12 : 8, + if (showIcon) + Padding( + padding: const EdgeInsets.only(top: 2.0), + child: Icon( + Icons.circle, + color: iconColor, + size: isSelected ? 12 : 8, + ), ), - ), Text( "${DateFormat("y", locale).format(month).toUpperCase()}", // Month style: yearTextStyle,