import 'package:easy_localization/easy_localization.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:scrobbles/config/app_colors.dart'; import 'package:scrobbles/models/heatmap.dart'; import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartHeatmap extends CustomChart { final HeatmapData chartData; const ChartHeatmap({super.key, required this.chartData}); final double chartHeight = 150.0; final double titleFontSize = 9; final Color baseColor = AppColors.contentColorPink; final double scale = 8.0; final double darkenAmount = 50; @override Widget build(BuildContext context) { if (this.chartData.data.keys.length == 0) { return SizedBox( height: this.chartHeight, ); } return AspectRatio( aspectRatio: 3, child: ScatterChart( ScatterChartData( scatterSpots: getSpots(), minX: 0, maxX: 24, minY: 0, maxY: 7, borderData: FlBorderData(show: false), gridData: FlGridData(show: false), titlesData: getTitlesData(), scatterTouchData: ScatterTouchData(enabled: false), ), ), ); } Color getColorFromNormalizedValue(double value) { return baseColor.darken(1 + (darkenAmount * (1 - value)).toInt()); } int getMaxCount() { int maxValue = 0; this.chartData.data.forEach((day, hours) { hours.forEach((hour, count) { if (count > maxValue) { maxValue = count; } }); }); return maxValue; } List<ScatterSpot> getSpots() { List<ScatterSpot> spots = []; final int maxCount = getMaxCount(); this.chartData.data.forEach((day, hours) { hours.forEach((hour, count) { double normalizedValue = count / maxCount; spots.add(ScatterSpot( hour.toDouble(), 8 - day.toDouble(), color: getColorFromNormalizedValue(normalizedValue), radius: scale * normalizedValue, )); }); }); return spots; } FlTitlesData getTitlesData() { const AxisTitles none = const AxisTitles( sideTitles: SideTitles(showTitles: false), ); final AxisTitles verticalTitles = AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 40, getTitlesWidget: getVerticalTitlesWidget, interval: 1, ), ); final AxisTitles horizontalTitles = AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 20, getTitlesWidget: getHorizontalTitlesWidgetWithHour, interval: 4, ), ); return FlTitlesData( show: true, bottomTitles: horizontalTitles, leftTitles: verticalTitles, topTitles: none, rightTitles: none, ); } Widget getVerticalTitlesWidget(double value, TitleMeta meta) { final String dayShortName = getDayShortName(8 - value.toInt()); return SideTitleWidget( axisSide: meta.axisSide, space: 10, child: Text( tr(dayShortName), style: TextStyle( color: AppColors.mainTextColor1, fontSize: this.titleFontSize, ), ), ); } }