mzimmerm / flutter_charts

Charts Library for Flutter, written in Dart with Flutter.
Other
251 stars 42 forks source link

Bad state: claims that data row legend size is different from data row size... #38

Closed RoarGronmo closed 1 year ago

RoarGronmo commented 1 year ago

I stumbled upon this problem:

Bad state: If row legends are defined, their number must be the same as number of data rows.  [dataRows length: 8] != [dataRowsLegends length: 8].

But as far as I can see both of them are at the same length... 8 vs 8?

The code is defined like this:

  Widget _barChart(List<GetObjectTypeCountResult?>? objectList){

    if(objectList==null) return const SizedBox.shrink();

    List<List<double>> dataRows = [];
    List<String> xUserLabels = [];
    for(var i=0; i<objectList.length; i++){
      List<double> dataRow = [];
      for(var j=0; j<objectList.length; j++){
        if(i==j) {
          dataRow.add(objectList[j]?.cnt.toDouble()??0.0);
        } else {
          dataRow.add(0.0);
        }
      }
      dataRows.add(dataRow);
      xUserLabels.add(objectList[i]?.typeTxt??"<not set>");
    }

    LabelLayoutStrategy? xContainerLabelLayoutStrategy;
    ChartData chartData;

    ChartOptions chartOptions = const ChartOptions(
      dataContainerOptions: DataContainerOptions(),
      iterativeLayoutOptions: IterativeLayoutOptions(labelTiltRadians: -math.pi/2),
      xContainerOptions: XContainerOptions(),
      yContainerOptions: YContainerOptions(),
      legendOptions: LegendOptions(isLegendContainerShown: true),
    );

    chartData = ChartData(
        dataRows: dataRows,
        xUserLabels: xUserLabels,
        dataRowsLegends: xUserLabels,
        chartOptions: chartOptions);

    var verticalBarChartContainer = VerticalBarChartTopContainer(
      chartData: chartData,
      xContainerLabelLayoutStrategy: xContainerLabelLayoutStrategy
    );

    var verticalBarChart = VerticalBarChart(
        painter: VerticalBarChartPainter(
          verticalBarChartContainer: verticalBarChartContainer,
        ));

    return verticalBarChart;

  }

Is there any obvious reason for this error ?

RoarGronmo commented 1 year ago

I tried to rework my code to use static data, but the error still persists:

Widget _barChart(List<GetObjectTypeCountResult?>? objectList){

    if(objectList==null) return const SizedBox.shrink();

    //List<List<double>> dataRows = [];
    //List<String> xUserLabels = [];
    //List<String> dataRowsLegends = [];
    /*
    for(var i=0; i<objectList.length; i++){
      List<double> dataRow = [];
      for(var j=0; j<objectList.length; j++){
        if(i==j) {
          dataRow.add(objectList[j]?.cnt.toDouble()??0.0);
        } else {
          dataRow.add(0.0);
        }
      }
      dataRows.add(dataRow);
      xUserLabels.add(objectList[i]?.typeTxt??"<not set>");
      dataRowsLegends.add(objectList[i]?.typeTxt??"<not set>");
    }
    */

    List<List<double>> dataRows = [
      [1, 0, 0, 0, 0, 0, 0, 0], 
      [0, 2, 0, 0, 0, 0, 0, 0], 
      [0, 0, 1, 0, 0, 0, 0, 0], 
      [0, 0, 0, 3, 0, 0, 0, 0], 
      [0, 0, 0, 0, 44, 0, 0, 0], 
      [0, 0, 0, 0, 0, 111, 0, 0], 
      [0, 0, 0, 0, 0, 0, 32, 0], 
      [0, 0, 0, 0, 0, 0, 0, 5]];

    var xUserLabels = ["A","B","C","D","E","F","G","H"];

    print("dataRows = $dataRows");

    LabelLayoutStrategy? xContainerLabelLayoutStrategy;
    ChartData chartData;

    ChartOptions chartOptions = const ChartOptions(
      dataContainerOptions: DataContainerOptions(),
      iterativeLayoutOptions: IterativeLayoutOptions(labelTiltRadians: -math.pi/2),
      xContainerOptions: XContainerOptions(),
      yContainerOptions: YContainerOptions(),
      legendOptions: LegendOptions(isLegendContainerShown: true),
    );

    chartData = ChartData(
        dataRows: dataRows,
        xUserLabels: xUserLabels,
        dataRowsLegends: xUserLabels,
        chartOptions: chartOptions);

    var verticalBarChartContainer = VerticalBarChartTopContainer(
      chartData: chartData,
      xContainerLabelLayoutStrategy: xContainerLabelLayoutStrategy
    );

    var verticalBarChart = VerticalBarChart(
        painter: VerticalBarChartPainter(
          verticalBarChartContainer: verticalBarChartContainer,
        ));

    return verticalBarChart;

  }

Is there a limit of rows or columns int the chart?

RoarGronmo commented 1 year ago

I took a dive in your code and found an ambigouity in your error messeage referenced above:

In the file lib/src/chart/data.dart at line 71 you also test the color count, AFAIK there is no requirements that colors also should be in, and it isn't referenced in the error message either.

  void validate() {
    //                      But that would require ChartOptions available in ChartData.
    if (!(dataRows.length == dataRowsLegends.length && dataRows.length == dataRowsColors.length)) {
      throw StateError('If row legends are defined, their '
          'number must be the same as number of data rows. '
          ' [dataRows length: ${dataRows.length}] '
          '!= [dataRowsLegends length: ${dataRowsLegends.length}]. ');
    }
    for (List<double> dataRow in dataRows) {
      if (!(dataRow.length == xUserLabels.length)) {
        throw StateError('If xUserLabels are defined, their '
            'length must be the same as length of each dataRow'
            ' [dataRow length: ${dataRow.length}] '
            '!= [xUserLabels length: ${xUserLabels.length}]. ');
      }
    }
    // Check explicit log10 used in options. This test does not cover user's explicitly declared transforms.
    if (log10 == chartOptions.dataContainerOptions.yTransform) {
      if (!(dataYMin > 0.0)) {
        throw StateError('Using logarithmic Y scale requires only positive Y data');
      }
    }
  }

You should explain that color array size also are needed...

mzimmerm commented 1 year ago

Hi,

Thanks for reporting this, and taking the time to analyze the issue clearly.

I have now changed the validation messages as follows:

    if (!(dataRows.length == dataRowsLegends.length)) {
      throw StateError('The number of legend labels provided in parameter "dataRowsLegends", '
          'does not equal the number of data rows provided in parameter "dataRows":\n'
          'Detail reason: Row legend labels must be provided in parameter "dataRowsLegends", '
          'and their number must be the same as number of data rows. '
          'However, in your data definition, that is not the case:\n'
          '   [number of dataRows: ${dataRows.length}] != [number of dataRowsLegends: ${dataRowsLegends.length}].\n'
          'To fix this: provide ${dataRows.length} "dataRowsLegends".');
    }
    if (!(dataRows.length == dataRowsColors.length)) {
      throw StateError('The number of legend colors provided in parameter "dataRowsColors", '
          'does not equal the number of data rows provided in parameter "dataRows":\n'
          'Detail reason: If not provided in "dataRowsColors", legend colors are generated. '
          'If the parameter "dataRowsColors" is provided, '
          'the number of colors must be the same as number of data rows. '
          'However, in your data definition, that is not the case:\n'
          '   [number of dataRows: ${dataRows.length}] != [number of dataRowsColors: ${dataRowsColors.length}].\n'
          'To fix this: provide ${dataRows.length} "dataRowsColors".');
    }

This is not released yet, I am thinking of a new release this weekend that would include this change. However, that would, for unrelated reasons include other changes including small API changes. So maybe I will do just a maintenance release.... Will let you know here either way.

Thanks, Milan

mzimmerm commented 1 year ago

I included the change mentioned in the above comment in today's release 0.5.1

Thanks for reporting this issue and using this library.

Milan

mzimmerm commented 1 year ago

Fixed in release 0.5.1 by providing a better (hopefully no longer misleading) error message.