update method

  1. @override
void update(
  1. List<ImmutableSeries<D>> seriesList,
  2. bool isAnimatingThisDraw
)
override

Generates rendering data needed to paint the data on the chart.

This is called during the post layout phase of the chart draw cycle.

Implementation

@override
void update(List<ImmutableSeries<D>> seriesList, bool isAnimatingThisDraw) {
  _currentKeys.clear();

  final bounds = chart!.drawAreaBounds;

  final center = Point<double>(
    bounds.left + bounds.width / 2,
    bounds.top + bounds.height / 2,
  );

  final radius =
      bounds.height < bounds.width ? (bounds.height / 2) : (bounds.width / 2);

  if (config.arcRatio != null &&
      (config.arcRatio! < 0 || config.arcRatio! > 1)) {
    throw ArgumentError('arcRatio must be between 0 and 1');
  }

  final innerRadius = _calculateInnerRadius(radius);

  for (final series in seriesList) {
    final colorFn = series.colorFn;
    final arcListKey = series.id;

    final arcList =
        _seriesArcMap.putIfAbsent(arcListKey, AnimatedArcList.new);

    final elementsList =
        series.getAttr(arcElementsKey)! as List<ArcRendererElement<D>>;

    if (series.data.isEmpty) {
      // If the series is empty, set up the "no data" arc element. This should
      // occupy the entire chart, and use the chart style's no data color.
      final details = elementsList[0];

      const arcKey = '__no_data__';

      // If we already have an AnimatingArc for that index, use it.
      var animatingArc =
          arcList.arcs.firstWhereOrNull((arc) => arc.key == arcKey);

      arcList
        ..center = center
        ..radius = radius
        ..innerRadius = innerRadius
        ..series = series
        ..stroke = config.noDataColor
        ..strokeWidthPx = 0.0;

      // If we don't have any existing arc element, create a new arc. Unlike
      // real arcs, we should not animate the no data state in from 0.
      if (animatingArc == null) {
        animatingArc = AnimatedArc<D>(arcKey, null, null);
        arcList.arcs.add(animatingArc);
      } else {
        animatingArc
          ..datum = null
          ..domain = null;
      }

      // Update the set of arcs that still exist in the series data.
      _currentKeys.add(arcKey);

      // Get the arcElement we are going to setup.
      // Optimization to prevent allocation in non-animating case.
      final arcElement = ArcRendererElement<D>(
        color: config.noDataColor,
        startAngle: details.startAngle,
        endAngle: details.endAngle,
        series: series,
      );

      animatingArc.setNewTarget(arcElement);
    } else {
      var previousEndAngle = config.startAngle;

      for (var arcIndex = 0; arcIndex < series.data.length; arcIndex++) {
        final Object? datum = series.data[arcIndex];
        final details = elementsList[arcIndex];
        final domainValue = details.domain;

        final arcKey = '${series.id}__$domainValue';

        // If we already have an AnimatingArc for that index, use it.
        var animatingArc =
            arcList.arcs.firstWhereOrNull((arc) => arc.key == arcKey);

        arcList
          ..center = center
          ..radius = radius
          ..innerRadius = innerRadius
          ..series = series
          ..stroke = config.stroke
          ..strokeWidthPx = config.strokeWidthPx;

        // If we don't have any existing arc element, create a new arc and
        // have it animate in from the position of the previous arc's end
        // angle. If there were no previous arcs, then animate everything in
        // from 0.
        if (animatingArc == null) {
          animatingArc = AnimatedArc<D>(arcKey, datum, domainValue)
            ..setNewTarget(
              ArcRendererElement<D>(
                color: colorFn!(arcIndex),
                startAngle: previousEndAngle,
                endAngle: previousEndAngle,
                index: arcIndex,
                series: series,
              ),
            );

          arcList.arcs.add(animatingArc);
        } else {
          animatingArc.datum = datum;

          previousEndAngle = animatingArc.previousArcEndAngle ?? 0.0;
        }

        animatingArc.domain = domainValue;

        // Update the set of arcs that still exist in the series data.
        _currentKeys.add(arcKey);

        // Get the arcElement we are going to setup.
        // Optimization to prevent allocation in non-animating case.
        final arcElement = ArcRendererElement<D>(
          color: colorFn!(arcIndex),
          startAngle: details.startAngle,
          endAngle: details.endAngle,
          index: arcIndex,
          series: series,
        );

        animatingArc.setNewTarget(arcElement);
      }
    }
  }

  // Animate out arcs that don't exist anymore.
  _seriesArcMap.forEach((key, arcList) {
    for (var arcIndex = 0; arcIndex < arcList.arcs.length; arcIndex++) {
      final arc = arcList.arcs[arcIndex];
      final arcStartAngle = arc.previousArcStartAngle;

      if (!_currentKeys.contains(arc.key)) {
        // Default to animating out to the top of the chart, clockwise, if
        // there are no arcs that start past this arc.
        var targetArcAngle = (2 * pi) + config.startAngle;

        // Find the nearest start angle of the next arc that still exists in
        // the data.
        for (final nextArc
            in arcList.arcs.where((arc) => _currentKeys.contains(arc.key))) {
          final nextArcStartAngle = nextArc.newTargetArcStartAngle;

          if (arcStartAngle! < nextArcStartAngle! &&
              nextArcStartAngle < targetArcAngle) {
            targetArcAngle = nextArcStartAngle;
          }
        }

        arc.animateOut(targetArcAngle);
      }
    }
  });
}