clicking on legend causes Unexpected null value charts_flutter - flutter

When I click on a specific item in the legend like '1 week out (25)' the chart goes red and gives an error Unexpected null value. The relevant error-causing widget was BarChart. I don't even need the legend to be clickable, but I don't see an override to the callback that's happening here. Desired output is that the legend is not clickable, or the legend is clickable, but the UI doesn't bug out when selecting a specific legend item. Cheers!
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
class StackedHorizontalBarChartCurrentCapacity extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
final CapableShop capableShop;
StackedHorizontalBarChartCurrentCapacity(this.seriesList, this.capableShop,
{this.animate});
factory StackedHorizontalBarChartCurrentCapacity.withCurrentCapacityEnrouteData(
CapableShop capableShop) {
return new StackedHorizontalBarChartCurrentCapacity(
_createCurrentCapacityEnrouteData(
currentEnroute: capableShop.currentEnroute,
currentEnrouteOptimal: capableShop.currentEnrouteOptimal,
currentEnrouteWeek1: capableShop.currentEnrouteWeek1,
currentEnrouteWeek2: capableShop.currentEnrouteWeek2,
currentEnrouteWeek3: capableShop.currentEnrouteWeek3,
currentEnrouteWeek3plus: capableShop.currentEnrouteWeek3plus,
currentFlexMax: capableShop.currentFlexMax,
currentOptimal: capableShop.currentOptimal),
capableShop,
animate: false,
);
}
#override
Widget build(BuildContext context) {
// print(capableShop.toJson());
return new charts.BarChart(
seriesList,
animate: animate,
barGroupingType: charts.BarGroupingType.stacked,
vertical: false,
behaviors: [
new charts.SeriesLegend(
desiredMaxColumns: 2,
cellPadding: EdgeInsets.all(2),
showMeasures: true,
desiredMaxRows: 3,
position: charts.BehaviorPosition.top,
horizontalFirst: false,
measureFormatter: (measure) => "(" + measure.toString() + ")",
legendDefaultMeasure: charts.LegendDefaultMeasure.firstValue,
)
],
primaryMeasureAxis: new charts.NumericAxisSpec(
renderSpec: charts.SmallTickRendererSpec(
minimumPaddingBetweenLabelsPx: 0,
labelAnchor: charts.TickLabelAnchor.after,
labelStyle: charts.TextStyleSpec(
fontSize: 10,
),
labelRotation: 30,
lineStyle:
charts.LineStyleSpec(color: charts.MaterialPalette.black)),
tickProviderSpec: charts.StaticNumericTickProviderSpec([
charts.TickSpec(capableShop.currentOptimal,
label: capableShop.currentOptimal.toString() + ' Optimal'),
charts.TickSpec(capableShop.currentFlexMax,
label: capableShop.currentFlexMax.toString() + ' Flex Max'),
charts.TickSpec(capableShop.currentEnrouteOptimal,
label: capableShop.currentEnrouteOptimal.toString() +
' Enroute Optimal'),
]),
viewport:
charts.NumericExtents(0, capableShop.currentEnrouteOptimal)),
);
}
static List<charts.Series<ChartAmount, String>>
_createCurrentCapacityEnrouteData({
int currentEnroute,
int currentEnrouteWeek1,
int currentEnrouteWeek2,
int currentEnrouteWeek3,
int currentEnrouteWeek3plus,
int currentFlexMax,
int currentOptimal,
int currentEnrouteOptimal,
}) {
final canonicalEnroute = [
new ChartAmount(
"${currentEnroute ?? 10} Canonical Enroute", currentEnroute ?? 10),
];
final oneWeekOut = [
new ChartAmount(
"${(currentEnrouteWeek1 ?? 2) + (currentEnrouteWeek2 ?? 3) + (currentEnrouteWeek3 ?? 1) + (currentEnrouteWeek3plus ?? 1)} AVSO Enroute",
currentEnrouteWeek1 ?? 2),
];
final twoWeeksOut = [
new ChartAmount(
"${(currentEnrouteWeek1 ?? 2) + (currentEnrouteWeek2 ?? 3) + (currentEnrouteWeek3 ?? 1) + (currentEnrouteWeek3plus ?? 1)} AVSO Enroute",
currentEnrouteWeek2 ?? 3),
];
final threeWeeksOut = [
new ChartAmount(
"${(currentEnrouteWeek1 ?? 2) + (currentEnrouteWeek2 ?? 3) + (currentEnrouteWeek3 ?? 1) + (currentEnrouteWeek3plus ?? 1)} AVSO Enroute",
currentEnrouteWeek3 ?? 1),
];
final plus = [
new ChartAmount(
"${(currentEnrouteWeek1 ?? 2) + (currentEnrouteWeek2 ?? 3) + (currentEnrouteWeek3 ?? 1) + (currentEnrouteWeek3plus ?? 1)} AVSO Enroute",
currentEnrouteWeek3plus ?? 1),
];
return [
new charts.Series<ChartAmount, String>(
id: '1 Week Out',
domainFn: (ChartAmount amount, _) => amount.label,
measureFn: (ChartAmount amount, _) => amount.enroute,
data: oneWeekOut,
measureUpperBoundFn: (_, __) => currentEnrouteOptimal,
measureLowerBoundFn: (_, __) => 0,
),
new charts.Series<ChartAmount, String>(
id: '2 Weeks Out',
domainFn: (ChartAmount amount, _) => amount.label,
measureFn: (ChartAmount amount, _) => amount.enroute,
data: twoWeeksOut,
measureUpperBoundFn: (_, __) => currentEnrouteOptimal,
measureLowerBoundFn: (_, __) => 0,
),
new charts.Series<ChartAmount, String>(
id: '3 Weeks Out',
domainFn: (ChartAmount amount, _) => amount.label,
measureFn: (ChartAmount amount, _) => amount.enroute,
data: threeWeeksOut,
measureUpperBoundFn: (_, __) => currentEnrouteOptimal,
measureLowerBoundFn: (_, __) => 0,
),
new charts.Series<ChartAmount, String>(
id: '3+ Weeks Out',
domainFn: (ChartAmount amount, _) => amount.label,
measureFn: (ChartAmount amount, _) => amount.enroute,
data: plus,
measureUpperBoundFn: (_, __) => currentEnrouteOptimal,
measureLowerBoundFn: (_, __) => 0,
),
new charts.Series<ChartAmount, String>(
id: 'Canonical Enroute',
domainFn: (ChartAmount amount, _) => amount.label,
measureFn: (ChartAmount amount, _) => amount.enroute,
data: canonicalEnroute,
measureUpperBoundFn: (_, __) => currentEnrouteOptimal,
measureLowerBoundFn: (_, __) => 0,
colorFn: (datum, index) =>
charts.ColorUtil.fromDartColor(Colors.blue[800]),
),
];
}
}

Related

Flutter Charts - marker color not matching series color

I am using TimeSeriesChart from the flutter_charts package, and the colors of the selected markers are not matching up with their corresponding line color. There is also no data from other series hiding behind these data points.
Since the series is dynamic, I am setting to colorFn using math.Random to generate random colors for each series.
Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0))
I'm not sure how this would affect the markers, though, since the series is compiled before the markers are used. Here is the function I'm using to build my series, along with the chart
List<charts.Series<ResultSummary, DateTime>> _createChartData() {
// create chart map
Map<String, List<ResultSummary>> chartMap = {};
ConsumerTargetResults selectedTargetResults = getConsumerTargetResults(resultsPool, [selectedTarget])[0];
Map<String, Color> promptColorMap = {};
promptColorMap['Independent'] = Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
promptColorMap.addAll(Map.fromIterable(selectedTargetResults.target.prompts, key: (p) =>
p.title, value: (p) => Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0))
);
promptColorMap.forEach((key, value) {
List<ResultSummary> resultSummaries = [];
for (Session s in selectedTargetResults.sessions) {
ResultSummary tResultSummary = ResultSummary(s.date, 0, value);
if (s.promptStatsMap[key] != null) tResultSummary.measure = s.promptStatsMap[key];
resultSummaries.add(tResultSummary);
}
chartMap[key] = resultSummaries;
});
// implement to chart
List<charts.Series<ResultSummary, DateTime>> chartDataList = [];
chartMap.forEach((key, value) {
chartDataList.add(
charts.Series<ResultSummary, DateTime>(
id: key,
data: value,
domainFn: (ResultSummary r, _) => r.domain,
measureFn: (ResultSummary r, _) => r.measure,
fillColorFn: (ResultSummary r, _) => r.color,
));
});
return chartDataList;
}
Expanded(
child: Container(
padding: EdgeInsets.fromLTRB(40.0, 10.0, 40.0, 10.0),
child: charts.TimeSeriesChart(
widget.seriesList,
// animate: widget.animate,
animate: false,
behaviors: [ charts.SeriesLegend(
position: charts.BehaviorPosition.end,
outsideJustification: charts.OutsideJustification.endDrawArea,
horizontalFirst: false,
desiredMaxColumns: 1,
cellPadding: EdgeInsets.only(right: 4.0, bottom: 4.0),
entryTextStyle: charts.TextStyleSpec(
color: charts.MaterialPalette.purple.shadeDefault,
fontFamily: 'Georgia',
fontSize: 11),
)],
selectionModels: [
charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: _onSelectionChanged,
updatedListener: _onSelectionChanged,
)
],
)),
),
I removed the colorFn field from my chart and that seemed to have done the trick
// implement to chart series
List<charts.Series<ResultSummary, DateTime>> chartDataList = [];
chartMap.forEach((key, value) {
chartDataList.add(
charts.Series<ResultSummary, DateTime>(
id: key,
data: value,
domainFn: (ResultSummary r, _) => r.domain,
measureFn: (ResultSummary r, _) => r.measure,
// fillColorFn: (ResultSummary r, _) => r.color,
));
});

How to add many line in Time Series Chart in Flutter

I am trying to add another line in the time series chart. And currently I did not find anyway to do this. I am using chart_flutter dependencies.
Here is my chart code look like:
charts.TimeSeriesChart(
series,
animate: true,
defaultRenderer: charts.LineRendererConfig(
includeArea: true,
includeLine: true,
),
dateTimeFactory: const charts.LocalDateTimeFactory(),
behaviors: [
charts.PanAndZoomBehavior(),
charts.SeriesLegend(
position: charts.BehaviorPosition.top,
horizontalFirst: false,
cellPadding: EdgeInsets.only(left: 80, top: 10, bottom: 4.0),
),
charts.SelectNearest(
eventTrigger: charts.SelectionTrigger.tap
),
charts.LinePointHighlighter(
symbolRenderer: CustomCircleSymbolRenderer(size: size),
),
],
selectionModels: [
charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: (charts.SelectionModel model) {
if(model.hasDatumSelection) {
final tankVolumeValue = model.selectedSeries[0].measureFn(model.selectedDatum[0].index).round();
final dateValue = model.selectedSeries[0].domainFn(model.selectedDatum[0].index);
CustomCircleSymbolRenderer.value = '$dateValue \n $tankVolumeValue';
}
})
]),
Any idea on how to add another line is welcome.
The answer above is simply that you can add more data in the variable that hold the data. Like below:
charts.Series(
id: 'Tank 1',
data: some data here,
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (TankPing ping, _) => some data,
measureFn: (TankPing ping, _) => some data
),
charts.Series(
id: 'Tank 1',
data: some data here,
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (TankPing ping, _) => some data,
measureFn: (TankPing ping, _) => some data
)

How to apply an opacity to a scatter plot flutter

I am using charts_flutter to draw a scatter plot.
So far, this is the relevant code I wrote:
class _VennState extends State<VennDiagramWidget> {
List<charts.Series<VennCircle, int>> circlesList;
static List<charts.Series<VennCircle, int>> _createRandomCircles() {
final circles = [
VennCircle('red', 1, 50, 0.8, 'Venn1', 15, 35),
VennCircle('black', 2, 150, 0.5, 'Venn2', 18, 20),
VennCircle('green', 3, 200, 1, 'Venn3', 25, 25),
];
return [
new charts.Series(
id: 'circles',
data: circles,
domainFn: (VennCircle venn, _) => venn.x_value,
measureFn: (VennCircle venn, _) => venn.y_value,
colorFn: (VennCircle venn, _) {
if (venn.circleColor == 'red') {
return charts.MaterialPalette.red.shadeDefault;
} else if (venn.circleColor == 'black') {
return charts.MaterialPalette.black;
}
return charts.MaterialPalette.green.shadeDefault;
},
radiusPxFn: (VennCircle venn, _) => venn.circleSize,
domainUpperBoundFn: (VennCircle venn, _) => 50,
domainLowerBoundFn: (VennCircle venn, _) => 0,
measureUpperBoundFn: (VennCircle venn, _) => 50,
measureLowerBoundFn: (VennCircle venn, _) => 0,
),
];
}
scatterPlot() {
return new charts.ScatterPlotChart(
circlesList,
animate: true,
primaryMeasureAxis: new charts.NumericAxisSpec(
showAxisLine: false,
renderSpec: new charts.NoneRenderSpec(),
),
domainAxis: new charts.NumericAxisSpec(
showAxisLine: false,
renderSpec: new charts.NoneRenderSpec(),
),
);
}
I couldn't find a way to change the opacity of each circle. (each circle can have a different opacity)
I came across this Opacity widget but I wasn't able to apply it - probably because it should be used on a widget.
On that note, how can I define VennCircle as widget and not an object?
EDIT: I was able to do that simply by modifying the colorFn function:
colorFn: (VennCircle venn, _) {
if (venn.circleColor == 'red') {
return charts.Color.fromOther(
color: charts.ColorUtil.fromDartColor(
Color.fromARGB(100, 255, 0, 0)));
} else if (venn.circleColor == 'black') {
return charts.ColorUtil.fromDartColor(Color.fromARGB(95, 0, 0, 0));
}
return charts.ColorUtil.fromDartColor(Color.fromARGB(95, 0, 255, 0));
},

Flutter Chart label text overlapping

Working on Flutter Chart, the domainFn lists the days in a month, 1...31, but they are overlapping, I would like to set the label in a scale of 5 i.e 5, 10, 15, 20, 25, 30.
List<charts.Series<EventChart, String>> _createChart(
List<EventChart> eventChart) {
return [
charts.Series<EventChart, String>(
id: widget.activity,
domainFn: (EventChart eventChart, _) => eventChart.label,
measureFn: (EventChart eventChart, _) => eventChart.count,
data: eventChart,
measureUpperBoundFn: (EventChart eventChart, _) =>
eventChart.count < 4 ? eventChart.count + 4 : eventChart.count,
measureLowerBoundFn: (EventChart eventChart, _) => 0,
fillColorFn: (EventChart eventChart, _) {
return charts.MaterialPalette.blue.shadeDefault;
},
)
];
}
);
BARCHART WIDGET:
Flexible(
child: charts.BarChart(
_createChart(pro.eventChart(widget.activity)
),
animate: true,
behaviors: [
charts.LinePointHighlighter(
drawFollowLinesAcrossChart: true,
showHorizontalFollowLine:
charts.LinePointHighlighterFollowLineType.all
)
],
),
You can select the ticks to be displayed by providing a tickProviderSpec in the domainAxis:
#override
Widget build(BuildContext context) {
return new charts.BarChart(
seriesList,
animate: animate,
domainAxis: new charts.OrdinalAxisSpec(
tickProviderSpec: charts.StaticOrdinalTickProviderSpec(
[
charts.TickSpec('10'),
charts.TickSpec('15'),
charts.TickSpec('20'),
...
]
)),
);
}

Flutter Charts - coloring and adding axis on a line chart

I'm currently trying to restyle a design layout. I got most of it through try and error. The only part which I can't figure out are:
1.) The black axis on the left and bottom. So that only those are black
2.) The light grey axis inside. How can I apply opacity on the color or use my own color? I can only use charts.MaterialPalette and I can't figure out how to define my own.
3.) Also how to add the light grey axis in the middle. I only got the vertical ones.
4.) Is there a way to add a corner radius for the lines?
This is what it looks like right now:
This is what I want to achieve:
Here's my code so far:
/// Example of a line chart rendered with dash patterns.
class DashPatternLineChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
DashPatternLineChart(this.seriesList, {this.animate});
/// Creates a [LineChart] with sample data and no transition.
factory DashPatternLineChart.withSampleData() {
return new DashPatternLineChart(
_createSampleData(),
// Disable animations for image tests.
animate: false,
);
}
#override
Widget build(BuildContext context) {
return new charts.LineChart(seriesList,
animate: animate,
layoutConfig: charts.LayoutConfig(
leftMarginSpec: charts.MarginSpec.fixedPixel(0),
topMarginSpec: charts.MarginSpec.fixedPixel(75),
rightMarginSpec: charts.MarginSpec.fixedPixel(0),
bottomMarginSpec: charts.MarginSpec.fixedPixel(175)
),
flipVerticalAxis: false,
defaultInteractions: false,
domainAxis: new charts.NumericAxisSpec(
renderSpec: charts.GridlineRendererSpec(
lineStyle: charts.LineStyleSpec(
color: charts.MaterialPalette.white,
thickness: 1,
)
),
tickProviderSpec: new charts.StaticNumericTickProviderSpec(
// Create the ticks to be used the domain axis.
<charts.TickSpec<num>>[
new charts.TickSpec(0, label: '0', style: charts.TextStyleSpec(fontSize: 14)),
new charts.TickSpec(50, label: '50', style: charts.TextStyleSpec(fontSize: 14)),
new charts.TickSpec(100, label: '100', style: charts.TextStyleSpec(fontSize: 14)),
],
)),
primaryMeasureAxis: new charts.NumericAxisSpec(
renderSpec: charts.GridlineRendererSpec(
labelOffsetFromAxisPx: -20,
labelAnchor: charts.TickLabelAnchor.after,
lineStyle: charts.LineStyleSpec(
color: charts.MaterialPalette.transparent,
thickness: 0,
)
),
tickProviderSpec: new charts.StaticNumericTickProviderSpec(
// Create the ticks to be used the domain axis.
<charts.TickSpec<num>>[
new charts.TickSpec(100, label: '100%', style: charts.TextStyleSpec(fontSize: 14)),
],
)
));
}
/// Create three series with sample hard coded data.
/// Create three series with sample hard coded data.
static List<charts.Series<LinearSales, int>> _createSampleData() {
final myFakeDesktopData = [
new LinearSales(0, 100),
new LinearSales(25, 85),
new LinearSales(30, 80),
new LinearSales(45, 60),
new LinearSales(30, 40),
new LinearSales(25, 20),
new LinearSales(0, 0),
];
return [
new charts.Series<LinearSales, int>(
id: 'Desktop',
colorFn: (_, __) => charts.MaterialPalette.white,
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
domainUpperBoundFn: (LinearSales sales, _) => sales.domainUpper,
domainLowerBoundFn: (LinearSales sales, _) => sales.domainLower,
measureUpperBoundFn: (LinearSales sales, _) => sales.measureUpper,
measureLowerBoundFn: (LinearSales sales, _) => sales.measureLower,
strokeWidthPxFn: (_, __) => 4,
data: myFakeDesktopData,
)
];
}
}
/// Sample linear data type.
class LinearSales {
final int year;
final int sales;
final int domainUpper = 100;
final int domainLower = 0;
final int measureUpper = 100;
final int measureLower = 0;
LinearSales(this.year, this.sales);
}
I don't have an answer for all your questions, but I was able to supply my own colors this way:
charts.Color.fromHex(code: "#ff0000")
or semitransparent like this:
charts.Color(r: 255, g: 0, b: 0, a: 128)