diff --git a/example/lib/pages/heatmap_calendar_example.dart b/example/lib/pages/heatmap_calendar_example.dart index d0e91af..235ced9 100644 --- a/example/lib/pages/heatmap_calendar_example.dart +++ b/example/lib/pages/heatmap_calendar_example.dart @@ -16,6 +16,7 @@ class _HeatMapCalendarExample extends State { bool isOpacityMode = true; Map heatMapDatasets = {}; + DateTime _focusDate = DateTime.now(); @override void dispose() { @@ -61,6 +62,7 @@ class _HeatMapCalendarExample extends State { child: HeatMapCalendar( flexible: true, datasets: heatMapDatasets, + focusDate: _focusDate, colorMode: isOpacityMode ? ColorMode.opacity : ColorMode.color, colorsets: const { @@ -72,6 +74,11 @@ class _HeatMapCalendarExample extends State { 11: Colors.indigo, 13: Colors.purple, }, + onClick: (date) { + setState(() { + _focusDate = date; + }); + }, ), ), ), diff --git a/example/lib/pages/heatmap_example.dart b/example/lib/pages/heatmap_example.dart index cda9da9..6454e8f 100644 --- a/example/lib/pages/heatmap_example.dart +++ b/example/lib/pages/heatmap_example.dart @@ -17,6 +17,8 @@ class _HeatMapExample extends State { Map heatMapDatasets = {}; + DateTime _focusDate = DateTime.now(); + @override void dispose() { super.dispose(); @@ -58,6 +60,7 @@ class _HeatMapExample extends State { padding: const EdgeInsets.all(20), child: HeatMap( scrollable: true, + focusDate: _focusDate, colorMode: isOpacityMode ? ColorMode.opacity : ColorMode.color, datasets: heatMapDatasets, @@ -71,8 +74,13 @@ class _HeatMapExample extends State { 13: Colors.purple, }, onClick: (value) { + setState(() { + _focusDate = value; + }); + }, + onLongPress: (value) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(value.toString()))); + SnackBar(content: Text('Long pressed: $value'))); }, ), ), diff --git a/lib/src/heatmap.dart b/lib/src/heatmap.dart index e884860..86bf59e 100644 --- a/lib/src/heatmap.dart +++ b/lib/src/heatmap.dart @@ -53,6 +53,11 @@ class HeatMap extends StatefulWidget { /// Parameter gives clicked [DateTime] value. final Function(DateTime)? onClick; + /// Function that will be called when a block is long pressed. + /// + /// Parameter gives pressed [DateTime] value. + final Function(DateTime)? onLongPress; + /// The margin value for every block. final EdgeInsets? margin; @@ -87,16 +92,20 @@ class HeatMap extends StatefulWidget { /// The double value of [HeatMapColorTip]'s tip container's size. final double? colorTipSize; + final DateTime? focusDate; + const HeatMap({ Key? key, required this.colorsets, this.colorMode = ColorMode.opacity, this.startDate, this.endDate, + this.focusDate, this.textColor, this.size = 20, this.fontSize, this.onClick, + this.onLongPress, this.margin, this.borderRadius, this.datasets, @@ -135,6 +144,7 @@ class _HeatMap extends State { endDate: widget.endDate ?? DateTime.now(), startDate: widget.startDate ?? DateUtil.oneYearBefore(widget.endDate ?? DateTime.now()), + focusDate: widget.focusDate, colorMode: widget.colorMode, size: widget.size, fontSize: widget.fontSize, @@ -144,6 +154,7 @@ class _HeatMap extends State { colorsets: widget.colorsets, borderRadius: widget.borderRadius, onClick: widget.onClick, + onLongPress: widget.onLongPress, margin: widget.margin, showText: widget.showText, )), diff --git a/lib/src/heatmap_calendar.dart b/lib/src/heatmap_calendar.dart index b325e4e..40409d8 100644 --- a/lib/src/heatmap_calendar.dart +++ b/lib/src/heatmap_calendar.dart @@ -64,6 +64,11 @@ class HeatMapCalendar extends StatefulWidget { /// Paratmeter gives clicked [DateTime] value. final Function(DateTime)? onClick; + /// Function that will be called when a block is long pressed. + /// + /// Paratmeter gives pressed [DateTime] value. + final Function(DateTime)? onLongPress; + /// Function that will be called when month is changed. /// /// Paratmeter gives [DateTime] value of current month. @@ -87,12 +92,16 @@ class HeatMapCalendar extends StatefulWidget { /// The double value of [HeatMapColorTip]'s tip container's size. final double? colorTipSize; + /// The DateTime value of focus date. + final DateTime? focusDate; + const HeatMapCalendar({ Key? key, required this.colorsets, this.colorMode = ColorMode.opacity, this.defaultColor, this.datasets, + this.focusDate, this.initDate, this.size = 42, this.fontSize, @@ -104,6 +113,7 @@ class HeatMapCalendar extends StatefulWidget { this.flexible = false, this.margin, this.onClick, + this.onLongPress, this.onMonthChange, this.showColorTip = true, this.colorTipHelper, @@ -218,6 +228,7 @@ class _HeatMapCalendar extends State { HeatMapCalendarPage( baseDate: _currentDate ?? DateTime.now(), colorMode: widget.colorMode, + focusDate: widget.focusDate, flexible: widget.flexible, size: widget.size, fontSize: widget.fontSize, @@ -228,6 +239,7 @@ class _HeatMapCalendar extends State { colorsets: widget.colorsets, borderRadius: widget.borderRadius, onClick: widget.onClick, + onLongPress: widget.onLongPress, ), if (widget.showColorTip == true) HeatMapColorTip( diff --git a/lib/src/util/date_util.dart b/lib/src/util/date_util.dart index 555fcd9..8a70204 100644 --- a/lib/src/util/date_util.dart +++ b/lib/src/util/date_util.dart @@ -128,3 +128,9 @@ class DateUtil { //#endregion } + +extension DateTimeExtension on DateTime { + bool isOnSameDayAs(DateTime other) { + return year == other.year && month == other.month && day == other.day; + } +} diff --git a/lib/src/widget/heatmap_calendar_page.dart b/lib/src/widget/heatmap_calendar_page.dart index cea2c5a..c3601bf 100644 --- a/lib/src/widget/heatmap_calendar_page.dart +++ b/lib/src/widget/heatmap_calendar_page.dart @@ -61,10 +61,18 @@ class HeatMapCalendarPage extends StatelessWidget { /// Paratmeter gives clicked [DateTime] value. final Function(DateTime)? onClick; + /// Function that will be called when a block is long pressed. + /// + /// Paratmeter gives pressed [DateTime] value. + final Function(DateTime)? onLongPress; + + final DateTime? focusDate; + HeatMapCalendarPage({ Key? key, required this.baseDate, required this.colorMode, + this.focusDate, this.flexible, this.size, this.fontSize, @@ -75,6 +83,7 @@ class HeatMapCalendarPage extends StatelessWidget { this.colorsets, this.borderRadius, this.onClick, + this.onLongPress, }) : separatedDate = DateUtil.separatedMonth(baseDate), maxValue = DatasetsUtil.getMaxValue( DatasetsUtil.filterMonth(datasets, baseDate)), @@ -89,6 +98,7 @@ class HeatMapCalendarPage extends StatelessWidget { HeatMapCalendarRow( startDate: date.keys.first, endDate: date.values.first, + focusDate: focusDate, colorMode: colorMode, size: size, fontSize: fontSize, @@ -100,6 +110,7 @@ class HeatMapCalendarPage extends StatelessWidget { margin: margin, maxValue: maxValue, onClick: onClick, + onLongPress: onLongPress, datasets: Map.from(datasets ?? {}) ..removeWhere( (key, value) => !(key.isAfter(date.keys.first) && diff --git a/lib/src/widget/heatmap_calendar_row.dart b/lib/src/widget/heatmap_calendar_row.dart index 10f4bd3..167240a 100644 --- a/lib/src/widget/heatmap_calendar_row.dart +++ b/lib/src/widget/heatmap_calendar_row.dart @@ -65,11 +65,19 @@ class HeatMapCalendarRow extends StatelessWidget { /// Paratmeter gives clicked [DateTime] value. final Function(DateTime)? onClick; + /// Function that will be called when a block is long pressed. + /// + /// Paratmeter gives pressed [DateTime] value. + final Function(DateTime)? onLongPress; + + final DateTime? focusDate; + HeatMapCalendarRow({ Key? key, required this.startDate, required this.endDate, required this.colorMode, + this.focusDate, this.size, this.fontSize, this.defaultColor, @@ -81,6 +89,7 @@ class HeatMapCalendarRow extends StatelessWidget { this.datasets, this.maxValue, this.onClick, + this.onLongPress, }) : dayContainers = List.generate( 7, // If current week has first day of the month and @@ -109,12 +118,21 @@ class HeatMapCalendarRow extends StatelessWidget { date: DateTime(startDate.year, startDate.month, startDate.day - startDate.weekday % 7 + i), backgroundColor: defaultColor, + focusColor: focusDate != null && + DateTime(startDate.year, startDate.month, + startDate.day - startDate.weekday % 7 + i) + .isOnSameDayAs(focusDate) + ? colorMode == ColorMode.opacity + ? colorsets?.entries.first.value + : colorsets?.entries.last.value + : null, size: size, fontSize: fontSize, textColor: textColor, borderRadius: borderRadius, margin: margin, onClick: onClick, + onLongPress: onLongPress, // If datasets has DateTime key which is equal to this HeatMapContainer's date, // we have to color the matched HeatMapContainer. // diff --git a/lib/src/widget/heatmap_column.dart b/lib/src/widget/heatmap_column.dart index 17221f4..0b9f932 100644 --- a/lib/src/widget/heatmap_column.dart +++ b/lib/src/widget/heatmap_column.dart @@ -63,6 +63,11 @@ class HeatMapColumn extends StatelessWidget { /// Paratmeter gives clicked [DateTime] value. final Function(DateTime)? onClick; + /// Function that will be called when a block is long pressed. + /// + /// Paratmeter gives pressed [DateTime] value. + final Function(DateTime)? onLongPress; + /// The integer value of the maximum value for the highest value of the month. final int? maxValue; @@ -73,12 +78,16 @@ class HeatMapColumn extends StatelessWidget { // current week. final int numDays; + /// The date value of the focused date. + final DateTime? focusDate; + HeatMapColumn({ Key? key, required this.startDate, required this.endDate, required this.colorMode, required this.numDays, + this.focusDate, this.size, this.fontSize, this.defaultColor, @@ -88,52 +97,64 @@ class HeatMapColumn extends StatelessWidget { this.margin, this.colorsets, this.onClick, + this.onLongPress, this.maxValue, this.showText, }) : // Init list. dayContainers = List.generate( numDays, - (i) => HeatMapContainer( - date: DateUtil.changeDay(startDate, i), - backgroundColor: defaultColor, - size: size, - fontSize: fontSize, - textColor: textColor, - borderRadius: borderRadius, - margin: margin, - onClick: onClick, - showText: showText, - // If datasets has DateTime key which is equal to this HeatMapContainer's date, - // we have to color the matched HeatMapContainer. - // - // If datasets is null or doesn't contains the equal DateTime value, send null. - selectedColor: datasets?.keys.contains(DateTime( - startDate.year, - startDate.month, - startDate.day - startDate.weekday % 7 + i)) ?? - false - // If colorMode is ColorMode.opacity, - ? colorMode == ColorMode.opacity - // Color the container with first value of colorsets - // and set opacity value to current day's datasets key - // devided by maxValue which is the maximum value of the month. - ? colorsets?.values.first.withOpacity((datasets?[DateTime( - startDate.year, - startDate.month, - startDate.day + i - (startDate.weekday % 7))] ?? - 1) / - (maxValue ?? 1)) - // Else if colorMode is ColorMode.Color. - // - // Get color value from colorsets which is filtered with DateTime value - // Using DatasetsUtil.getColor() - : DatasetsUtil.getColor( - colorsets, - datasets?[DateTime(startDate.year, startDate.month, - startDate.day + i - (startDate.weekday % 7))]) - : null, - ), + (i) { + final date = DateUtil.changeDay(startDate, i); + return HeatMapContainer( + date: date, + backgroundColor: defaultColor, + size: size, + fontSize: fontSize, + textColor: textColor, + borderRadius: borderRadius, + margin: margin, + onClick: onClick, + onLongPress: onLongPress, + showText: showText, + // If datasets has DateTime key which is equal to this HeatMapContainer's date, + // we have to color the matched HeatMapContainer. + // + // If datasets is null or doesn't contains the equal DateTime value, send null. + selectedColor: datasets?.keys.contains(DateTime( + startDate.year, + startDate.month, + startDate.day - startDate.weekday % 7 + i)) ?? + false + // If colorMode is ColorMode.opacity, + ? colorMode == ColorMode.opacity + // Color the container with first value of colorsets + // and set opacity value to current day's datasets key + // devided by maxValue which is the maximum value of the month. + ? colorsets?.values.first.withOpacity((datasets?[DateTime( + startDate.year, + startDate.month, + startDate.day + + i - + (startDate.weekday % 7))] ?? + 1) / + (maxValue ?? 1)) + // Else if colorMode is ColorMode.Color. + // + // Get color value from colorsets which is filtered with DateTime value + // Using DatasetsUtil.getColor() + : DatasetsUtil.getColor( + colorsets, + datasets?[DateTime(startDate.year, startDate.month, + startDate.day + i - (startDate.weekday % 7))]) + : null, + focusColor: focusDate != null && date.isOnSameDayAs(focusDate) + ? colorMode == ColorMode.opacity + ? colorsets?.entries.first.value + : colorsets?.entries.last.value + : null, + ); + }, ), // Fill emptySpace list only if given wek doesn't have 7 days. emptySpace = (numDays != 7) diff --git a/lib/src/widget/heatmap_container.dart b/lib/src/widget/heatmap_container.dart index 46b8f40..d9f7836 100644 --- a/lib/src/widget/heatmap_container.dart +++ b/lib/src/widget/heatmap_container.dart @@ -8,10 +8,12 @@ class HeatMapContainer extends StatelessWidget { final double? borderRadius; final Color? backgroundColor; final Color? selectedColor; + final Color? focusColor; final Color? textColor; final EdgeInsets? margin; final bool? showText; final Function(DateTime dateTime)? onClick; + final Function(DateTime dateTime)? onLongPress; const HeatMapContainer({ Key? key, @@ -24,7 +26,9 @@ class HeatMapContainer extends StatelessWidget { this.selectedColor, this.textColor, this.onClick, + this.onLongPress, this.showText, + this.focusColor, }) : super(key: key); @override @@ -52,7 +56,11 @@ class HeatMapContainer extends StatelessWidget { ) : null, decoration: BoxDecoration( - color: selectedColor, + color: selectedColor ?? Colors.transparent, + border: Border.all( + color: focusColor ?? Colors.transparent, + width: 2, + ), borderRadius: BorderRadius.all(Radius.circular(borderRadius ?? 5)), ), @@ -61,6 +69,9 @@ class HeatMapContainer extends StatelessWidget { onTap: () { onClick != null ? onClick!(date) : null; }, + onLongPress: () { + onLongPress != null ? onLongPress!(date) : null; + }, ), ); } diff --git a/lib/src/widget/heatmap_page.dart b/lib/src/widget/heatmap_page.dart index e1c2984..7640f32 100644 --- a/lib/src/widget/heatmap_page.dart +++ b/lib/src/widget/heatmap_page.dart @@ -73,13 +73,21 @@ class HeatMapPage extends StatelessWidget { /// Paratmeter gives clicked [DateTime] value. final Function(DateTime)? onClick; + /// Function that will be called when a block is long pressed. + /// + /// Paratmeter gives clicked [DateTime] value. + final Function(DateTime)? onLongPress; + final bool? showText; + final DateTime? focusDate; + HeatMapPage({ Key? key, required this.colorMode, required this.startDate, required this.endDate, + this.focusDate, this.size, this.fontSize, this.datasets, @@ -88,6 +96,7 @@ class HeatMapPage extends StatelessWidget { this.colorsets, this.borderRadius, this.onClick, + this.onLongPress, this.margin, this.showText, }) : _dateDifferent = endDate.difference(startDate).inDays, @@ -117,6 +126,7 @@ class HeatMapPage extends StatelessWidget { endDate: datePos <= _dateDifferent - 7 ? DateUtil.changeDay(startDate, datePos + 6) : endDate, + focusDate: focusDate, colorMode: colorMode, numDays: min(endDate.difference(_firstDay).inDays + 1, 7), size: size, @@ -128,6 +138,7 @@ class HeatMapPage extends StatelessWidget { margin: margin, maxValue: maxValue, onClick: onClick, + onLongPress: onLongPress, datasets: datasets, showText: showText, ));