assignMissingColors method

  1. @protected
void assignMissingColors(
  1. Iterable<MutableSeries<D>> seriesList, {
  2. required bool emptyCategoryUsesSinglePalette,
})
inherited

Assigns colors to series that are missing their colorFn.

emptyCategoryUsesSinglePalette Flag indicating whether having all series with no categories will use the same or separate palettes. Setting it to true uses various Blues for each series. Setting it to false used different palettes (ie: s1 uses Blue500, s2 uses Red500),

Implementation

@protected
void assignMissingColors(Iterable<MutableSeries<D>> seriesList,
    {required bool emptyCategoryUsesSinglePalette}) {
  const defaultCategory = '__default__';

  // Count up the number of missing series per category, keeping a max across
  // categories.
  final missingColorCountPerCategory = <String, int>{};
  var maxMissing = 0;
  var hasSpecifiedCategory = false;

  seriesList.forEach((MutableSeries<D> series) {
    // Assign the seriesColor as the color of every datum if no colorFn was
    // provided.
    if (series.colorFn == null && series.seriesColor != null) {
      series.colorFn = (_) => series.seriesColor!;
    }

    // This series was missing both seriesColor and a colorFn. Add it to the
    // "missing" set.
    if (series.colorFn == null) {
      // If there is no category, give it a default category to match logic.
      var category = series.seriesCategory;
      if (category == null) {
        category = defaultCategory;
      } else {
        hasSpecifiedCategory = true;
      }

      // Increment the missing counts for the category.
      final missingCnt = (missingColorCountPerCategory[category] ?? 0) + 1;
      missingColorCountPerCategory[category] = missingCnt;
      maxMissing = max(maxMissing, missingCnt);
    }
  });

  if (maxMissing > 0) {
    // Special handling of only series with empty categories when we want
    // to use different palettes.
    if (!emptyCategoryUsesSinglePalette && !hasSpecifiedCategory) {
      final palettes = StyleFactory.style.getOrderedPalettes(maxMissing);
      var index = 0;
      seriesList.forEach((series) {
        if (series.colorFn == null) {
          final color = palettes[index % palettes.length].shadeDefault;
          index++;
          series.colorFn = (_) => color;
          series.seriesColor ??= color;
        } else {
          // Fill in missing seriesColor values with the color of the first
          // datum in the series. Note that [Series.colorFn] should always
          // return a color.
          if (series.seriesColor == null) {
            try {
              series.seriesColor = series.colorFn!(0);
            } catch (exception) {
              series.seriesColor = StyleFactory.style.defaultSeriesColor;
            }
          }
        }
      });
      return;
    }

    // Get a list of palettes to use given the number of categories we've
    // seen. One palette per category (but might need to repeat).
    final colorPalettes = StyleFactory.style
        .getOrderedPalettes(missingColorCountPerCategory.length);

    // Create a map of Color palettes for each category. Each Palette uses
    // the max for any category to ensure that the gradients look appropriate.
    final colorsByCategory = <String, List<Color>>{};
    var index = 0;
    missingColorCountPerCategory.keys.forEach((String category) {
      colorsByCategory[category] =
          colorPalettes[index % colorPalettes.length].makeShades(maxMissing);
      index++;

      // Reset the count so we can use it to count as we set the colorFn.
      missingColorCountPerCategory[category] = 0;
    });

    seriesList.forEach((series) {
      if (series.colorFn == null) {
        final category = series.seriesCategory ?? defaultCategory;

        // Get the current index into the color list.
        final colorIndex = missingColorCountPerCategory[category]!;
        missingColorCountPerCategory[category] = colorIndex + 1;

        final color = colorsByCategory[category]![colorIndex];
        series.colorFn = (_) => color;
      }

      // Fill color defaults to the series color if no accessor is provided.
      series.fillColorFn ??= (int? index) => series.colorFn!(index);
    });
  } else {
    seriesList.forEach((series) {
      // Fill color defaults to the series color if no accessor is provided.
      series.fillColorFn ??= (int? index) => series.colorFn!(index);
    });
  }

  // Fill in any missing seriesColor values with the color of the first datum
  // in the series. Note that [Series.colorFn] should always return a color.
  seriesList.forEach((series) {
    if (series.seriesColor == null) {
      try {
        series.seriesColor = series.colorFn!(0);
      } catch (exception) {
        series.seriesColor = StyleFactory.style.defaultSeriesColor;
      }
    }
  });
}