From 779926bc982008d079c784983bb4cf8249d90377 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sun, 1 Oct 2023 20:22:47 +0200 Subject: [PATCH] Move nutritional plan diary chart to fl charts --- lib/models/nutrition/nutritional_plan.dart | 24 + lib/widgets/dashboard/widgets.dart | 166 +++--- lib/widgets/nutrition/charts.dart | 553 ++++++++---------- .../nutrition/nutritional_diary_detail.dart | 3 +- .../nutrition/nutritional_plan_detail.dart | 16 +- .../measurement_entries_screen_test.dart | 5 +- test/nutrition/nutritional_diary_test.dart | 2 +- test/weight/weight_screen_test.dart | 5 +- 8 files changed, 360 insertions(+), 414 deletions(-) diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index 053fecc28..978304c23 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -82,6 +82,30 @@ class NutritionalPlan { return out; } + NutritionalValues get nutritionalValuesToday { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + + return logEntriesValues.containsKey(today) ? logEntriesValues[today]! : NutritionalValues(); + } + + NutritionalValues get nutritionalValues7DayAvg { + final currentDate = DateTime.now(); + final sevenDaysAgo = currentDate.subtract(Duration(days: 7)); + + final entries = logs.where((obj) { + DateTime objDate = obj.datetime; + return objDate.isAfter(sevenDaysAgo) && objDate.isBefore(currentDate); + }).toList(); + + var out = NutritionalValues(); + entries.forEach((log) { + out = out + log.nutritionalValues; + }); + + return out; + } + /// Calculates the percentage each macro nutrient adds to the total energy BaseNutritionalValues energyPercentage(NutritionalValues values) { return BaseNutritionalValues( diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index b0dee91cc..fa11ebfd2 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -182,19 +182,15 @@ class _DashboardNutritionWidgetState extends State { }, ), if (_hasContent) - Container( - color: Colors.blue, - padding: const EdgeInsets.only(left: 10), - child: Column( - children: [ - ...getContent(), - Container( - padding: const EdgeInsets.all(15), - height: 180, - child: NutritionalPlanPieChartWidget(_plan!.nutritionalValues), - ) - ], - ), + Column( + children: [ + ...getContent(), + Container( + padding: const EdgeInsets.all(15), + height: 180, + child: FlNutritionalPlanPieChartWidget(_plan!.nutritionalValues), + ) + ], ) else NothingFound( @@ -345,85 +341,79 @@ class _DashboardMeasurementWidgetState extends State } return Consumer( builder: (context, workoutProvider, child) => Card( - child: Container( - color: Colors.amberAccent, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - title: Text( - AppLocalizations.of(context).measurements, - style: Theme.of(context).textTheme.headline4, - ), - leading: const FaIcon( - FontAwesomeIcons.weight, - color: Colors.black, - ), - trailing: IconButton( - icon: const Icon(Icons.arrow_forward), - onPressed: () => Navigator.pushNamed( - context, - MeasurementCategoriesScreen.routeName, - ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text( + AppLocalizations.of(context).measurements, + style: Theme.of(context).textTheme.headline4, + ), + leading: const FaIcon( + FontAwesomeIcons.weight, + color: Colors.black, + ), + trailing: IconButton( + icon: const Icon(Icons.arrow_forward), + onPressed: () => Navigator.pushNamed( + context, + MeasurementCategoriesScreen.routeName, ), ), - - //color: Colors.lightBlue, - Column( - children: [ - if (items.isNotEmpty) - Column(children: [ - CarouselSlider( - items: items, - carouselController: _controller, - options: CarouselOptions( - autoPlay: false, - enlargeCenterPage: false, - viewportFraction: 1, - enableInfiniteScroll: false, - aspectRatio: 1.1, - onPageChanged: (index, reason) { - setState(() { - _current = index; - }); - }), - ), - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: items.asMap().entries.map( - (entry) { - return GestureDetector( - onTap: () => _controller.animateToPage(entry.key), - child: Container( - width: 12.0, - height: 12.0, - margin: - EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: (Theme.of(context).brightness == Brightness.dark - ? Colors.white - : wgerPrimaryColor) - .withOpacity(_current == entry.key ? 0.9 : 0.4)), - ), - ); - }, - ).toList(), - ), + ), + Column( + children: [ + if (items.isNotEmpty) + Column(children: [ + CarouselSlider( + items: items, + carouselController: _controller, + options: CarouselOptions( + autoPlay: false, + enlargeCenterPage: false, + viewportFraction: 1, + enableInfiniteScroll: false, + aspectRatio: 1.1, + onPageChanged: (index, reason) { + setState(() { + _current = index; + }); + }), + ), + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: items.asMap().entries.map( + (entry) { + return GestureDetector( + onTap: () => _controller.animateToPage(entry.key), + child: Container( + width: 12.0, + height: 12.0, + margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: (Theme.of(context).brightness == Brightness.dark + ? Colors.white + : wgerPrimaryColor) + .withOpacity(_current == entry.key ? 0.9 : 0.4)), + ), + ); + }, + ).toList(), ), - ]) - else - NothingFound( - AppLocalizations.of(context).noMeasurementEntries, - AppLocalizations.of(context).newEntry, - MeasurementCategoryForm(), ), - ], - ), - ], - ), + ]) + else + NothingFound( + AppLocalizations.of(context).noMeasurementEntries, + AppLocalizations.of(context).newEntry, + MeasurementCategoryForm(), + ), + ], + ), + ], ), )); } diff --git a/lib/widgets/nutrition/charts.dart b/lib/widgets/nutrition/charts.dart index 6db07f004..2de6185ef 100644 --- a/lib/widgets/nutrition/charts.dart +++ b/lib/widgets/nutrition/charts.dart @@ -17,7 +17,6 @@ */ import 'package:charts_flutter/flutter.dart' as charts; -import 'package:collection/collection.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -34,16 +33,16 @@ class NutritionData { NutritionData(this.name, this.value); } -class NutritionalPlanPieChartWidget extends StatefulWidget { +class FlNutritionalPlanPieChartWidget extends StatefulWidget { final NutritionalValues nutritionalValues; - const NutritionalPlanPieChartWidget(this.nutritionalValues); + const FlNutritionalPlanPieChartWidget(this.nutritionalValues); @override State createState() => FlNutritionalPlanPieChartState(); } -class FlNutritionalPlanPieChartState extends State { +class FlNutritionalPlanPieChartState extends State { int touchedIndex = -1; @override @@ -128,7 +127,7 @@ class FlNutritionalPlanPieChartState extends State, DateTime>( - id: 'NutritionDiary', - colorFn: (datum, index) => wgerChartSecondaryColor, - domainFn: (datum, index) => datum[1], - measureFn: (datum, index) => datum[0].energy, - data: _nutritionalPlan.logEntriesValues.keys - .map((e) => [_nutritionalPlan.logEntriesValues[e], e]) - .toList(), - ) - ], - defaultRenderer: charts.BarRendererConfig(), - behaviors: [ - charts.RangeAnnotation([ - charts.LineAnnotationSegment( - _nutritionalPlan.nutritionalValues.energy, - charts.RangeAnnotationAxisType.measure, - strokeWidthPx: 2, - color: charts.MaterialPalette.gray.shade600, - ), - ]), - ], - ); - } + State createState() => NutritionalDiaryChartWidgetFlState(); } -/// Nutritional plan hatch bar chart widget -class NutritionalPlanHatchBarChartWidget extends StatelessWidget { - final NutritionalPlan _nutritionalPlan; - - /// [_nutritionalPlan] is current opened nutrition plan as plan detail. - const NutritionalPlanHatchBarChartWidget(this._nutritionalPlan); - - NutritionalValues nutritionalValuesFromPlanLogsSevenDayAvg() { - NutritionalValues sevenDaysAvg = NutritionalValues(); - int count = 0; - - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - - _nutritionalPlan.logEntriesValues.forEach((key, value) { - if (key.difference(today).inDays >= -7) { - sevenDaysAvg += value; - count++; - } - }); - - if (count != 0) { - sevenDaysAvg.energy = sevenDaysAvg.energy / count; - sevenDaysAvg.protein = sevenDaysAvg.protein / count; - sevenDaysAvg.carbohydrates = sevenDaysAvg.carbohydrates / count; - sevenDaysAvg.carbohydratesSugar = sevenDaysAvg.carbohydratesSugar / count; - sevenDaysAvg.fat = sevenDaysAvg.fat / count; - sevenDaysAvg.fatSaturated = sevenDaysAvg.fatSaturated / count; - sevenDaysAvg.fibres = sevenDaysAvg.fibres / count; - sevenDaysAvg.sodium = sevenDaysAvg.sodium / count; +class NutritionalDiaryChartWidgetFlState extends State { + Widget bottomTitles(double value, TitleMeta meta) { + const style = TextStyle(fontSize: 10); + String text; + switch (value.toInt()) { + case 0: + text = 'Protein'; + break; + case 1: + text = 'Carbohydrates'; + break; + case 2: + text = 'Sugars'; + break; + case 3: + text = 'Fat'; + break; + case 4: + text = 'Saturated fat'; + break; + + default: + text = ''; + break; } - - return sevenDaysAvg; + return SideTitleWidget( + axisSide: meta.axisSide, + child: Text(text, style: style), + ); } - NutritionalValues nutritionalValuesFromPlanLogsToday() { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - - return _nutritionalPlan.logEntriesValues[_nutritionalPlan.logEntriesValues.keys - .firstWhereOrNull((d) => d.difference(today).inDays == 0)] ?? - NutritionalValues(); + Widget leftTitles(double value, TitleMeta meta) { + if (value == meta.max) { + return Container(); + } + const style = TextStyle( + fontSize: 10, + ); + return SideTitleWidget( + axisSide: meta.axisSide, + child: Text( + meta.formattedValue, + style: style, + ), + ); } @override Widget build(BuildContext context) { - final NutritionalValues loggedNutritionalValues = nutritionalValuesFromPlanLogsToday(); - final NutritionalValues sevenDayAvg = nutritionalValuesFromPlanLogsSevenDayAvg(); - - if (_nutritionalPlan.nutritionalValues.energy == 0) { - return Container(); - } - - return Column( - children: [ - Container( - padding: const EdgeInsets.all(15), - height: 220, - child: charts.BarChart( - [ - charts.Series( - id: 'Planned', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - data: [ - NutritionData(AppLocalizations.of(context).energy, - _nutritionalPlan.nutritionalValues.energy), - ], - ), - charts.Series( - id: 'Logged', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - fillPatternFn: (nutritionEntry, index) => charts.FillPatternType.forwardHatch, - data: [ - NutritionData( - AppLocalizations.of(context).energy, loggedNutritionalValues.energy), - ], - ), - charts.Series( - id: 'Avg', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - data: [ - NutritionData(AppLocalizations.of(context).energy, sevenDayAvg.energy), - ], - ), - ], - animate: true, - domainAxis: const charts.OrdinalAxisSpec( - ///labelRotation was added to rotate text of X Axis. Without that, - ///titles would overlap each other - renderSpec: charts.SmallTickRendererSpec(labelRotation: 60), - ), - barGroupingType: charts.BarGroupingType.grouped, - defaultRenderer: charts.BarRendererConfig( - groupingType: charts.BarGroupingType.grouped, strokeWidthPx: 0.0, maxBarWidthPx: 8), - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: charts.BasicNumericTickProviderSpec(desiredTickCount: 5), - ), - ), - ), - Container( - padding: const EdgeInsets.all(15), - height: 300, - child: charts.BarChart( - [ - charts.Series( - id: 'Planned', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - data: [ - // NutritionData( - // AppLocalizations.of(context).energy, - // _nutritionalPlan.nutritionalValues.energy, - // ), - NutritionData( - AppLocalizations.of(context).protein, - _nutritionalPlan.nutritionalValues.protein, + final NutritionalValues planned = widget._nutritionalPlan.nutritionalValues; + final NutritionalValues loggedToday = widget._nutritionalPlan.nutritionalValuesToday; + final NutritionalValues logged7DayAvg = widget._nutritionalPlan.nutritionalValues7DayAvg; + + final colorPlanned = LIST_OF_COLORS3[0]; + final colorLoggedToday = LIST_OF_COLORS3[1]; + final colorLogged7Day = LIST_OF_COLORS3[2]; + + return AspectRatio( + aspectRatio: 1.66, + child: Padding( + padding: const EdgeInsets.only(top: 16), + child: LayoutBuilder( + builder: (context, constraints) { + final barsSpace = 4.0 * constraints.maxWidth / 400; + final barsWidth = 8.0 * constraints.maxWidth / 400; + return BarChart( + BarChartData( + alignment: BarChartAlignment.center, + barTouchData: BarTouchData( + enabled: false, + ), + titlesData: FlTitlesData( + show: true, + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 48, + getTitlesWidget: bottomTitles, + ), ), - NutritionData( - AppLocalizations.of(context).carbohydrates, - _nutritionalPlan.nutritionalValues.carbohydrates, + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 40, + getTitlesWidget: leftTitles, + ), ), - NutritionData( - AppLocalizations.of(context).sugars, - _nutritionalPlan.nutritionalValues.carbohydratesSugar, + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), ), - NutritionData( - AppLocalizations.of(context).fat, - _nutritionalPlan.nutritionalValues.fat, + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), ), - NutritionData( - AppLocalizations.of(context).saturatedFat, - _nutritionalPlan.nutritionalValues.fatSaturated, + ), + gridData: FlGridData( + show: true, + checkToShowHorizontalLine: (value) => value % 10 == 0, + getDrawingHorizontalLine: (value) => FlLine( + color: Colors.black, + strokeWidth: 1, ), - NutritionData( - AppLocalizations.of(context).fibres, - _nutritionalPlan.nutritionalValues.fibres, + drawVerticalLine: false, + ), + borderData: FlBorderData( + show: false, + ), + groupsSpace: 30, + // groupsSpace: barsSpace, + barGroups: [ + BarChartGroupData( + x: 0, + barsSpace: barsSpace, + barRods: [ + BarChartRodData( + toY: planned.protein, + color: colorPlanned, + width: barsWidth, + ), + BarChartRodData( + toY: loggedToday.protein, + color: colorLoggedToday, + width: barsWidth, + ), + BarChartRodData( + toY: logged7DayAvg.protein, + color: colorLogged7Day, + width: barsWidth, + ), + ], ), - NutritionData( - AppLocalizations.of(context).sodium, - _nutritionalPlan.nutritionalValues.sodium, + BarChartGroupData( + x: 1, + barsSpace: barsSpace, + barRods: [ + BarChartRodData( + toY: planned.carbohydrates, + color: colorPlanned, + width: barsWidth, + ), + BarChartRodData( + toY: loggedToday.carbohydrates, + color: colorLoggedToday, + width: barsWidth, + ), + BarChartRodData( + toY: logged7DayAvg.carbohydrates, + color: colorLogged7Day, + width: barsWidth, + ), + ], + ), + BarChartGroupData( + x: 2, + barsSpace: barsSpace, + barRods: [ + BarChartRodData( + toY: planned.carbohydratesSugar, + color: colorPlanned, + width: barsWidth, + ), + BarChartRodData( + toY: loggedToday.carbohydratesSugar, + color: colorLoggedToday, + width: barsWidth, + ), + BarChartRodData( + toY: logged7DayAvg.carbohydratesSugar, + color: colorLogged7Day, + width: barsWidth, + ), + ], + ), + BarChartGroupData( + x: 3, + barsSpace: barsSpace, + barRods: [ + BarChartRodData( + toY: planned.fat, + color: colorPlanned, + width: barsWidth, + ), + BarChartRodData( + toY: loggedToday.fat, + color: colorLoggedToday, + width: barsWidth, + ), + BarChartRodData( + toY: logged7DayAvg.fat, + color: colorLogged7Day, + width: barsWidth, + ), + ], + ), + BarChartGroupData( + x: 4, + barsSpace: barsSpace, + barRods: [ + BarChartRodData( + toY: planned.fatSaturated, + color: colorPlanned, + width: barsWidth, + ), + BarChartRodData( + toY: loggedToday.fatSaturated, + color: colorLoggedToday, + width: barsWidth, + ), + BarChartRodData( + toY: logged7DayAvg.fatSaturated, + color: colorLogged7Day, + width: barsWidth, + ), + ], ), ], + // barGroups: getData(barsWidth, barsSpace), ), - charts.Series( - id: 'Logged', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - fillPatternFn: (nutritionEntry, index) => charts.FillPatternType.forwardHatch, - data: [ - // NutritionData( - // AppLocalizations.of(context).energy, - // loggedNutritionalValues.energy - // ), - - NutritionData( - AppLocalizations.of(context).protein, loggedNutritionalValues.protein), - NutritionData(AppLocalizations.of(context).carbohydrates, - loggedNutritionalValues.carbohydrates), - NutritionData(AppLocalizations.of(context).sugars, - loggedNutritionalValues.carbohydratesSugar), - NutritionData(AppLocalizations.of(context).fat, loggedNutritionalValues.fat), - NutritionData(AppLocalizations.of(context).saturatedFat, - loggedNutritionalValues.fatSaturated), - NutritionData( - AppLocalizations.of(context).fibres, loggedNutritionalValues.fibres), - NutritionData( - AppLocalizations.of(context).sodium, loggedNutritionalValues.sodium), - ], - ), - charts.Series( - id: 'Avg', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - data: [ - // NutritionData(AppLocalizations.of(context).energy, sevenDayAvg.energy), - NutritionData(AppLocalizations.of(context).protein, sevenDayAvg.protein), - NutritionData( - AppLocalizations.of(context).carbohydrates, sevenDayAvg.carbohydrates), - NutritionData( - AppLocalizations.of(context).sugars, sevenDayAvg.carbohydratesSugar), - NutritionData(AppLocalizations.of(context).fat, sevenDayAvg.fat), - NutritionData( - AppLocalizations.of(context).saturatedFat, sevenDayAvg.fatSaturated), - NutritionData(AppLocalizations.of(context).fibres, sevenDayAvg.fibres), - NutritionData(AppLocalizations.of(context).sodium, sevenDayAvg.sodium), - ], - ), - ], - animate: true, - domainAxis: const charts.OrdinalAxisSpec( - ///labelRotation was added to rotate text of X Axis. Without that, - ///titles would overlap each other - renderSpec: charts.SmallTickRendererSpec(labelRotation: 60), - ), - barGroupingType: charts.BarGroupingType.grouped, - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: charts.BasicNumericTickProviderSpec( - desiredTickCount: 5, - ), - ), - ), + ); + }, ), - ], + ), ); } } -//creating a seperate chart for energy as the energy value and other nutrient's value is not compatable to show in one graph -class EnergyChart extends StatelessWidget { - const EnergyChart({Key? key, required this.nutritionalPlan}) : super(key: key); - final NutritionalPlan nutritionalPlan; - - NutritionalValues nutritionalValuesFromPlanLogsSevenDayAvg() { - NutritionalValues sevenDaysAvg = NutritionalValues(); - int count = 0; - - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - - nutritionalPlan.logEntriesValues.forEach((key, value) { - if (key.difference(today).inDays >= -7) { - sevenDaysAvg += value; - count++; - } - }); - - if (count != 0) { - sevenDaysAvg.energy = sevenDaysAvg.energy / count; - sevenDaysAvg.protein = sevenDaysAvg.protein / count; - sevenDaysAvg.carbohydrates = sevenDaysAvg.carbohydrates / count; - sevenDaysAvg.carbohydratesSugar = sevenDaysAvg.carbohydratesSugar / count; - sevenDaysAvg.fat = sevenDaysAvg.fat / count; - sevenDaysAvg.fatSaturated = sevenDaysAvg.fatSaturated / count; - sevenDaysAvg.fibres = sevenDaysAvg.fibres / count; - sevenDaysAvg.sodium = sevenDaysAvg.sodium / count; - } - - return sevenDaysAvg; - } - - NutritionalValues nutritionalValuesFromPlanLogsToday() { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); +class NutritionalDiaryChartWidget extends StatelessWidget { + const NutritionalDiaryChartWidget({ + Key? key, + required NutritionalPlan nutritionalPlan, + }) : _nutritionalPlan = nutritionalPlan, + super(key: key); - return nutritionalPlan.logEntriesValues[nutritionalPlan.logEntriesValues.keys - .firstWhereOrNull((d) => d.difference(today).inDays == 0)] ?? - NutritionalValues(); - } + final NutritionalPlan _nutritionalPlan; @override Widget build(BuildContext context) { - final NutritionalValues loggedNutritionalValues = nutritionalValuesFromPlanLogsToday(); - final NutritionalValues sevenDayAvg = nutritionalValuesFromPlanLogsSevenDayAvg(); - - if (nutritionalPlan.nutritionalValues.energy == 0) { - return Container(); - } - - return charts.BarChart( + return charts.TimeSeriesChart( [ - charts.Series( - id: 'Planned', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - data: [ - NutritionData( - AppLocalizations.of(context).energy, - nutritionalPlan.nutritionalValues.energy, - ), - ], - ), - charts.Series( - id: 'Logged', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - fillPatternFn: (nutritionEntry, index) => charts.FillPatternType.forwardHatch, - data: [ - NutritionData(AppLocalizations.of(context).energy, loggedNutritionalValues.energy), - ], - ), - charts.Series( - id: 'Avg', - domainFn: (nutritionEntry, index) => nutritionEntry.name, - measureFn: (nutritionEntry, index) => nutritionEntry.value, - data: [ - NutritionData(AppLocalizations.of(context).energy, sevenDayAvg.energy), - ], - ), + charts.Series, DateTime>( + id: 'NutritionDiary', + colorFn: (datum, index) => wgerChartSecondaryColor, + domainFn: (datum, index) => datum[1], + measureFn: (datum, index) => datum[0].energy, + data: _nutritionalPlan.logEntriesValues.keys + .map((e) => [_nutritionalPlan.logEntriesValues[e], e]) + .toList(), + ) + ], + defaultRenderer: charts.BarRendererConfig(), + behaviors: [ + charts.RangeAnnotation([ + charts.LineAnnotationSegment( + _nutritionalPlan.nutritionalValues.energy, + charts.RangeAnnotationAxisType.measure, + strokeWidthPx: 2, + color: charts.MaterialPalette.gray.shade600, + ), + ]), ], - animate: true, - domainAxis: const charts.OrdinalAxisSpec( - ///labelRotation was added to rotate text of X Axis. Without that, - ///titles would overlap each other - renderSpec: charts.SmallTickRendererSpec(labelRotation: 60), - ), - barGroupingType: charts.BarGroupingType.grouped, - defaultRenderer: charts.BarRendererConfig( - groupingType: charts.BarGroupingType.grouped, strokeWidthPx: 0.0, maxBarWidthPx: 8), - primaryMeasureAxis: const charts.NumericAxisSpec( - tickProviderSpec: charts.BasicNumericTickProviderSpec(desiredTickCount: 5), - ), ); } } diff --git a/lib/widgets/nutrition/nutritional_diary_detail.dart b/lib/widgets/nutrition/nutritional_diary_detail.dart index fff02c400..6368fbe78 100644 --- a/lib/widgets/nutrition/nutritional_diary_detail.dart +++ b/lib/widgets/nutrition/nutritional_diary_detail.dart @@ -32,6 +32,7 @@ class NutritionalDiaryDetailWidget extends StatelessWidget { final NutritionalPlan _nutritionalPlan; final DateTime _date; static const double tablePadding = 7; + const NutritionalDiaryDetailWidget(this._nutritionalPlan, this._date); Widget getTable( @@ -223,7 +224,7 @@ class NutritionalDiaryDetailWidget extends StatelessWidget { Container( padding: const EdgeInsets.all(15), height: 220, - child: NutritionalPlanPieChartWidget(valuesDate), + child: FlNutritionalPlanPieChartWidget(valuesDate), ), Padding( padding: const EdgeInsets.all(8.0), diff --git a/lib/widgets/nutrition/nutritional_plan_detail.dart b/lib/widgets/nutrition/nutritional_plan_detail.dart index 671d0b4fe..ab2026c50 100644 --- a/lib/widgets/nutrition/nutritional_plan_detail.dart +++ b/lib/widgets/nutrition/nutritional_plan_detail.dart @@ -32,7 +32,9 @@ import 'package:wger/widgets/nutrition/meal.dart'; class NutritionalPlanDetailWidget extends StatelessWidget { final NutritionalPlan _nutritionalPlan; + const NutritionalPlanDetailWidget(this._nutritionalPlan); + static const double tablePadding = 7; @override @@ -70,7 +72,7 @@ class NutritionalPlanDetailWidget extends StatelessWidget { Container( padding: const EdgeInsets.all(15), height: 220, - child: NutritionalPlanPieChartWidget(nutritionalValues), // chart + child: FlNutritionalPlanPieChartWidget(nutritionalValues), // chart ), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), @@ -210,13 +212,11 @@ class NutritionalPlanDetailWidget extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline6, ), - - NutritionalPlanHatchBarChartWidget(_nutritionalPlan), - // Container( - // padding: const EdgeInsets.all(15), - // height: 300, - // child: NutritionalPlanHatchBarChartWidget(_nutritionalPlan), // chart - // ), + Container( + padding: const EdgeInsets.all(15), + height: 300, + child: NutritionalDiaryChartWidgetFl(nutritionalPlan: _nutritionalPlan), // chart + ), const Padding(padding: EdgeInsets.all(8.0)), Text( AppLocalizations.of(context).nutritionalDiary, diff --git a/test/measurements/measurement_entries_screen_test.dart b/test/measurements/measurement_entries_screen_test.dart index 58572d614..6182ab373 100644 --- a/test/measurements/measurement_entries_screen_test.dart +++ b/test/measurements/measurement_entries_screen_test.dart @@ -82,8 +82,9 @@ void main() { await tester.tap(find.byType(TextButton)); await tester.pumpAndSettle(); - expect(find.text('8/1/2021'), findsOneWidget); - expect(find.text('8/10/2021'), findsOneWidget); + // From the entries list and from the chart + expect(find.text('8/1/2021'), findsNWidgets(3)); + expect(find.text('8/10/2021'), findsNWidgets(2)); }); testWidgets('Tests the localization of dates - DE', (WidgetTester tester) async { diff --git a/test/nutrition/nutritional_diary_test.dart b/test/nutrition/nutritional_diary_test.dart index d65029a76..a1372b7db 100644 --- a/test/nutrition/nutritional_diary_test.dart +++ b/test/nutrition/nutritional_diary_test.dart @@ -41,7 +41,7 @@ void main() { testWidgets('Test the detail view for the nutritional plan', (WidgetTester tester) async { await tester.pumpWidget(getWidget()); - expect(find.byType(NutritionalPlanPieChartWidget), findsOneWidget); + expect(find.byType(FlNutritionalPlanPieChartWidget), findsOneWidget); expect(find.byType(Table), findsOneWidget); expect(find.text('519kcal'), findsOneWidget, reason: 'find total energy'); diff --git a/test/weight/weight_screen_test.dart b/test/weight/weight_screen_test.dart index 881d16b9d..c95c1c049 100644 --- a/test/weight/weight_screen_test.dart +++ b/test/weight/weight_screen_test.dart @@ -83,8 +83,9 @@ void main() { testWidgets('Tests the localization of dates - EN', (WidgetTester tester) async { await tester.pumpWidget(createWeightScreen()); - expect(find.text('1/1/2021'), findsOneWidget); - expect(find.text('1/10/2021'), findsOneWidget); + // One in the entries list, one in the chart + expect(find.text('1/1/2021'), findsNWidgets(2)); + expect(find.text('1/10/2021'), findsNWidgets(2)); }); testWidgets('Tests the localization of dates - DE', (WidgetTester tester) async {