diff --git a/lib/ui/dashboard/dashboard_chart.dart b/lib/ui/dashboard/dashboard_chart.dart new file mode 100644 index 000000000..651d38420 --- /dev/null +++ b/lib/ui/dashboard/dashboard_chart.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/ui/app/form_card.dart'; +import 'package:charts_flutter/flutter.dart' as charts; +import 'package:invoiceninja_flutter/utils/formatting.dart'; + +class DashboardChart extends StatefulWidget { + const DashboardChart( + {this.series, this.amount, this.previousAmount, this.title}); + + final List series; + final double previousAmount; + final double amount; + final String title; + + @override + _DashboardChartState createState() => _DashboardChartState(); +} + +class _DashboardChartState extends State { + String _title; + String _subtitle; + + void _onSelectionChanged(charts.SelectionModel model) { + final selectedDatum = model.selectedDatum; + + DateTime date; + double total = 0.0; + final measures = {}; + + if (selectedDatum.isNotEmpty) { + date = selectedDatum.first.datum.date; + selectedDatum.forEach((charts.SeriesDatum datumPair) { + total += datumPair.datum.amount; + measures[datumPair.series.displayName] = datumPair.datum.amount; + }); + } + + setState(() { + if (date != null) { + _title = formatNumber(total, context); + _subtitle = formatDate(date.toIso8601String(), context); + } else { + _title = null; + _subtitle = null; + } + }); + } + + @override + Widget build(BuildContext context) { + final chart = charts.TimeSeriesChart( + widget.series, + animate: true, + selectionModels: [ + charts.SelectionModelConfig( + type: charts.SelectionModelType.info, + listener: _onSelectionChanged, + ) + ], + behaviors: [ + charts.SeriesLegend( + outsideJustification: charts.OutsideJustification.endDrawArea, + ) + ], + ); + + final bool isIncrease = widget.amount >= widget.previousAmount; + final String changeAmount = (isIncrease ? '+' : '-') + + formatNumber(widget.amount - widget.previousAmount, context); + final String changePercent = (isIncrease ? '+' : '-') + + formatNumber(widget.previousAmount / widget.amount * 100, context, + formatNumberType: FormatNumberType.percent); + final String changeString = '$changeAmount ($changePercent)'; + + return FormCard( + children: [ + Padding( + padding: EdgeInsets.all(14.0), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Column( + children: [ + Text(widget.title, + style: Theme.of(context).textTheme.subhead), + Row( + children: [ + Text(formatNumber(widget.amount, context), + style: Theme.of(context).textTheme.headline), + SizedBox(width: 10.0), + Text( + changeString, + style: TextStyle( + fontSize: 16.0, + color: isIncrease ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + crossAxisAlignment: CrossAxisAlignment.start, + ), + ), + _title != null + ? Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text(_subtitle, + style: Theme.of(context).textTheme.subhead), + Text(_title, + style: Theme.of(context).textTheme.headline), + ], + ) + : Container(), + ], + ), + SizedBox( + height: 200.0, + child: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: chart, + ), + ), + ], + ), + ) + ], + ); + } +} diff --git a/lib/ui/dashboard/dashboard_panels.dart b/lib/ui/dashboard/dashboard_panels.dart index b85e3e18f..43db95aeb 100644 --- a/lib/ui/dashboard/dashboard_panels.dart +++ b/lib/ui/dashboard/dashboard_panels.dart @@ -1,8 +1,8 @@ import 'package:invoiceninja_flutter/redux/dashboard/dashboard_selectors.dart'; -import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:invoiceninja_flutter/ui/app/forms/date_range_picker.dart'; +import 'package:invoiceninja_flutter/ui/dashboard/dashboard_chart.dart'; import 'package:invoiceninja_flutter/ui/dashboard/dashboard_vm.dart'; import 'package:charts_flutter/flutter.dart' as charts; import 'package:invoiceninja_flutter/utils/formatting.dart'; @@ -92,6 +92,7 @@ class DashboardPanels extends StatelessWidget { ]; double total = 0.0; + double previousTotal = 0.0; data.forEach((dynamic item) { total += item.amount; }); @@ -118,16 +119,16 @@ class DashboardPanels extends StatelessWidget { ), ); - double previousTotal = 0.0; - data.forEach((dynamic item) { + previousData.forEach((dynamic item) { previousTotal += item.amount; }); } return DashboardChart( series: series, - heading: formatNumber(total, context), - subheading: localization.invoices); + amount: total, + previousAmount: previousTotal, + title: localization.invoices); } @override @@ -141,108 +142,3 @@ class DashboardPanels extends StatelessWidget { } } -class DashboardChart extends StatefulWidget { - const DashboardChart({this.series, this.heading, this.subheading}); - - final List series; - final String heading; - final String subheading; - - @override - _DashboardChartState createState() => _DashboardChartState(); -} - -class _DashboardChartState extends State { - String _title; - String _subtitle; - - void _onSelectionChanged(charts.SelectionModel model) { - final selectedDatum = model.selectedDatum; - - DateTime date; - double total = 0.0; - final measures = {}; - - if (selectedDatum.isNotEmpty) { - date = selectedDatum.first.datum.date; - selectedDatum.forEach((charts.SeriesDatum datumPair) { - total += datumPair.datum.amount; - measures[datumPair.series.displayName] = datumPair.datum.amount; - }); - } - - setState(() { - if (date != null) { - _title = formatNumber(total, context); - _subtitle = formatDate(date.toIso8601String(), context); - } else { - _title = null; - _subtitle = null; - } - }); - } - - @override - Widget build(BuildContext context) { - final chart = charts.TimeSeriesChart( - widget.series, - animate: true, - selectionModels: [ - charts.SelectionModelConfig( - type: charts.SelectionModelType.info, - listener: _onSelectionChanged, - ) - ], - behaviors: [ - charts.SeriesLegend( - outsideJustification: charts.OutsideJustification.endDrawArea, - ) - ], - ); - - return FormCard( - children: [ - Padding( - padding: EdgeInsets.all(14.0), - child: Column( - children: [ - Row( - children: [ - Expanded( - child: Column( - children: [ - Text(widget.subheading, - style: Theme.of(context).textTheme.subhead), - Text(widget.heading, - style: Theme.of(context).textTheme.headline), - ], - crossAxisAlignment: CrossAxisAlignment.start, - ), - ), - _title != null - ? Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text(_subtitle, - style: Theme.of(context).textTheme.subhead), - Text(_title, - style: Theme.of(context).textTheme.headline), - ], - ) - : Container(), - ], - ), - SizedBox( - height: 200.0, - child: Padding( - padding: const EdgeInsets.only(top: 10.0), - child: chart, - ), - ), - ], - ), - ) - ], - ); - } -}