108 lines
3.3 KiB
Dart
108 lines
3.3 KiB
Dart
import 'package:charts_flutter/flutter.dart' as charts;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
import '../model/product_model.dart';
|
|
|
|
typedef void ProductClick(DateTime date, Map<String, int> measures);
|
|
|
|
class ProductsChart extends StatelessWidget {
|
|
static final numberFormatter = new NumberFormat("#,###");
|
|
final ChartData chartData;
|
|
final ProductClick productClick;
|
|
const ProductsChart(this.chartData, {Key key, this.productClick})
|
|
: super(key: key);
|
|
factory ProductsChart.fromModel(ProductModel productModel,
|
|
{ProductClick productClick}) {
|
|
return new ProductsChart(_createData(productModel),
|
|
productClick: productClick);
|
|
}
|
|
|
|
static ChartData _createData(ProductModel productModel) {
|
|
List<charts.Series<TimeSeriesSales, DateTime>> list = [];
|
|
var min = 9999, max = 0;
|
|
productModel.products.forEach((p) {
|
|
List<TimeSeriesSales> data = [];
|
|
if (p.priceHistory != null) {
|
|
var dateKeys = {};
|
|
p.priceHistory.entries.forEach((e) {
|
|
dateKeys[DateTime.parse(e.key)] = e.value;
|
|
});
|
|
|
|
var sortedKeys = dateKeys.keys.toList()..sort((a, b) => b.compareTo(a));
|
|
sortedKeys.forEach((k) {
|
|
var v = dateKeys[k];
|
|
data.add(new TimeSeriesSales(k, v));
|
|
if (v < min) min = v;
|
|
if (v > max) max = v;
|
|
});
|
|
}
|
|
|
|
list.add(new charts.Series<TimeSeriesSales, DateTime>(
|
|
id: p.name,
|
|
colorFn: (_, __) => charts.ColorUtil.fromDartColor(Color(p.color)),
|
|
domainFn: (TimeSeriesSales sales, _) => sales.time,
|
|
measureFn: (TimeSeriesSales sales, _) => sales.sales,
|
|
data: data,
|
|
labelAccessorFn: (TimeSeriesSales series, _) =>
|
|
'${numberFormatter.format(series.sales)}',
|
|
measureFormatterFn: (TimeSeriesSales series, _) => (n) => "s",
|
|
));
|
|
});
|
|
var chartData = ChartData(list, min, max);
|
|
return chartData;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return charts.TimeSeriesChart(
|
|
chartData.seriesList,
|
|
animate: true,
|
|
defaultRenderer: new charts.LineRendererConfig(
|
|
includePoints: true,
|
|
),
|
|
primaryMeasureAxis: new charts.NumericAxisSpec(
|
|
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
|
zeroBound: false, desiredTickCount: 10),
|
|
renderSpec: new charts.GridlineRendererSpec(
|
|
lineStyle: charts.LineStyleSpec(
|
|
dashPattern: [4, 4],
|
|
))),
|
|
selectionModels: [
|
|
new charts.SelectionModelConfig(
|
|
type: charts.SelectionModelType.info,
|
|
updatedListener: _onSelectionChanged,
|
|
)
|
|
],
|
|
);
|
|
}
|
|
|
|
_onSelectionChanged(charts.SelectionModel model) {
|
|
final selectedDatum = model.selectedDatum;
|
|
if (selectedDatum.isNotEmpty) {
|
|
var _time = selectedDatum.first.datum.time;
|
|
Map<String, int> _measures = <String, int>{};
|
|
|
|
selectedDatum.forEach((charts.SeriesDatum datumPair) {
|
|
_measures[datumPair.series.displayName] = datumPair.datum.sales;
|
|
});
|
|
if (productClick != null) {
|
|
productClick(_time, _measures);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class TimeSeriesSales {
|
|
final DateTime time;
|
|
final int sales;
|
|
|
|
TimeSeriesSales(this.time, this.sales);
|
|
}
|
|
|
|
class ChartData {
|
|
final List<charts.Series> seriesList;
|
|
final num min, max;
|
|
ChartData(this.seriesList, this.min, this.max);
|
|
}
|