How to prevent indicator dot disappearing on setState() in charts_flutter

Viewed 499

When updating the data series within a charts_flutter TimeSeriesChart the indicator dot disappears. This link suggests a possible workaround but so far I've no been able to implement it as the explanation doesn't come with a full example. Does anyone have any example whereby they are able to achieve this? Specifically I'm trying to maintain the indicator dot on the graph line whilst also updating Text widget.

Code for TimeSeriesChart:

new charts.TimeSeriesChart(
  getSeries(reading),
  dateTimeFactory: new charts.UTCDateTimeFactory(),
  animate: false,
  selectionModels: [
    new charts.SelectionModelConfig(
      changedListener: _onSelectionChanged,
    )
  ],
  behaviors: [
    new charts.LinePointHighlighter(
      defaultRadiusPx: 20.0,
      radiusPaddingPx: 20.0,
      showHorizontalFollowLine:
        charts.LinePointHighlighterFollowLineType.nearest,
      showVerticalFollowLine:
        charts.LinePointHighlighterFollowLineType.nearest
    ),
    new charts.SelectNearest(
      eventTrigger: charts.SelectionTrigger.tapAndDrag
    )
  ],
  primaryMeasureAxis: ...
  domainAxis: ...

And then the code for _onSelectionChanged:

_onSelectionChanged(charts.SelectionModel model) {
  final selectedDatum = model.selectedDatum;

  DateTime time;
  final measures = <String, num>{};
  num value;
  if (selectedDatum.isNotEmpty) {
    time = selectedDatum.first.datum.time;
    value = selectedDatum.first.datum.value;

    selectedDatum.forEach((charts.SeriesDatum datumPair) {
      measures[datumPair.series.displayName] = datumPair.datum.value;
      if (!datumPair.series.id.contains("min") && !datumPair.series.id.contains("max"))
        value = datumPair.datum.value;
    });
  }
  setState(() {
    _time = time;
    _measures = measures;
    _value = value; // This is the value that is used within the `Text` widget
  });
}
2 Answers

How I made it work:

class InnerStatefulWidget extends StatefulWidget {
    final List<charts.Series<T, DateTime>> seriesList; //where T is the class used to create the series

    InnerStatefulWidget(this.seriesList);

    factory InnerStatefulWidget.withSampleData(D apiData) { // where D is the class for the data from API
        return new InnerStatefulWidget(
          _createSeriesData(apiData) // the function that receives data from the API and converts it into series
        );
    }

    static List<charts.Series<T, DateTime>> _createSeriesData(D apiData) {
        return new charts.Series<T, DateTime>(
            id: CHART_ID,
            domainFn: (T x, _) => x.dateTime,
            measureFn: (T x, _) => x.value,
            data: apiData));
     }

    @override
    _InnerStatefulWidgetState createState() => _InnerStatefulWidgetState();
}

class _InnerStatefulWidgetState extends State<InnerStatefulWidget> {
    @override
    Widget build(BuildContext context) {
        return new charts.TimeSeriesChart(
          widget.seriesList,
          ...
          selectionModels: [
            new charts.SelectionModelConfig(
              type: charts.SelectionModelType.info,
              changedListener: _onSelectionChanged,
            )
          ],
        );
    }

    void _onSelectionChanged(charts.SelectionModel model) {
        ...
    }
}

class OuterStatefulWidget extends StatefulWidget {
  D apiData;

  OuterStatefulWidget(this.apiData);

  @override
  _OuterStatefulWidgetState createState() => _OuterStatefulWidgetState();
}

class _OuterStatefulWidgetState extends State<OuterStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return InnerStatefulWidget.withSampleData(widget.apiData); 
  }
}

Make sure to pass the same "series" instance to the chart widget on each invocation of the build method - e.g. by initializing it as a late instance field:

class _YourChartContainerState extends State<YourChartContainer> {
  
  late series = getSeries(reading);

  @override
  Widget build(BuildContext context) {
    return new charts.TimeSeriesChart(
      series,
      // further parameters omitted...
      );
  }
}

This way the chart widget is reused/recycled and maintains its selection state.

Related