syncfusion / flutter-widgets

Syncfusion Flutter widgets libraries include high quality UI widgets and file-format packages to help you create rich, high-quality applications for iOS, Android, and web from a single code base.
1.6k stars 783 forks source link

syncfusion_flutter_charts Stacked bar series bug - not stacked properly #2155

Open Justus-M opened 2 weeks ago

Justus-M commented 2 weeks ago

Bug description

Stacked bar series with categorical x axis shows bars overlapping and with space between them

Screenshot 2024-11-03 at 16 39 45

Steps to reproduce

  1. Create a bar graph with a categorical x column

Code sample

Code sample ```dart var request = ref.watch(queryRequestProvider(query.id)); var result = request.getFilteredResult(query.sliders, query)!; result = result.map((key, value) => MapEntry( key, value.firstOrNull is String && !value.any((v) => num.tryParse(v.toString()) == null) ? value.map((e) => num.parse(e.toString())).toList() : value)); var xAxis = result[query.xAxisVariable] ?.firstWhere((element) => element != null) is DateTime ? DateTimeAxis( title: AxisTitle( text: query.xAxisName ?? query.xAxisVariable?.replaceAll('_', ' '))) : CategoryAxis( maximumLabelWidth: 240, labelIntersectAction: AxisLabelIntersectAction.rotate45, title: AxisTitle( text: query.xAxisName ?? query.xAxisVariable?.replaceAll('_', ' ') ?? query.yAxisVariable.keys.firstOrNull)); var dataSeries = ref.watch(queryRequestProvider(query.id)).getDataSeries(query, result); var legendOverflow = dataSeries.keys.any((element) => element.length > 20); var colorPalette = Theme.of(context).brightness == Brightness.light ? lightPalette : palette; return SfCartesianChart( axes: query.createAxes(dataSeries)?.sublist(1), tooltipBehavior: TooltipBehavior( enable: true, ), isTransposed: query.flipAxes != true, legend: Legend( position: legendOverflow ? LegendPosition.bottom : null, title: LegendTitle( text: request.safeGroupByVariable(query.groupByVariable) ?? 'groups'), isVisible: dataSeries.keys.length > 1), primaryXAxis: xAxis, primaryYAxis: query.createAxes(dataSeries)?.firstOrNull, series: [ ...dataSeries.keys.map((series) { return StackedBarSeries( name: series, sortFieldValueMapper: query.sortOn == null ? null : (data, index) => query.sortOn == 'x' ? data.x : data.y, sortingOrder: query.sortOn == null ? null : query.sortAscending == true ? SortingOrder.ascending : SortingOrder.descending, color: colorPalette[dataSeries.keys.length > 1 && dataSeries.keys.toList().indexOf(series) < colorPalette.length ? dataSeries.keys.toList().indexOf(series) : (dataSeries.keys.toList().indexOf(series) % colorPalette.length)], pointColorMapper: (DataPoint data, i) => colorPalette[ dataSeries.keys.length > 1 && dataSeries.keys.toList().indexOf(series) < colorPalette.length ? dataSeries.keys.toList().indexOf(series) : xAxis is DateTimeAxis ? 0 : (i % colorPalette.length)], legendItemText: series, yAxisName: query.yAxisNameForSeries(series, dataSeries), xAxisName: query.xAxisName, dataSource: dataSeries[series]!, xValueMapper: (DataPoint data, _) => data.x, yValueMapper: (DataPoint data, _) => data.y); }) ]);```

Screenshots or Video

Screenshots / Video demonstration Screenshot 2024-11-03 at 16 39 45

Stack Traces

Stack Traces ```dart [Add the Stack Traces here] ```

On which target platforms have you observed this bug?

macOS

Flutter Doctor output

Doctor output ```console [Add your output here] ```
Baranibharathip commented 2 weeks ago

Hi @Justus-M ,

We have checked the mentioned issue 'stacked bar series with categorical X-axis showing gap between bars' and tried to replicate it using the attached code snippet in SfCartesianChart with version 27.1.57 by,

  1. Ensured on Windows, Android, and web platforms.
  2. Ensured with empty point for Y value.
  3. Ensured with empty point for X value.
  4. Ensured with empty point for X,Y value.
  5. Ensured with series xAxisName and yAxisName property with multiple axes.

However, we were unable to reproduce it on our end. Please check the attached sample, and if you are still experiencing the issue, we request that you replicate it in the attached sample and provide us with more details regarding the specific scenario in which you are encountering this issue. This will help us to assist you more effectively.

Sample: GH_2155.zip

Regards, Baranibharathi P.

Justus-M commented 4 days ago

@Baranibharathip Simply remove any of the datapoints and there will be gaps between. Ex. use this code

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';

// Mock DataPoint class
class DataPoint {
  final dynamic x;
  final double? y;

  DataPoint(this.x, this.y);
}

// Mock Query class with default values
class Query {
  String? xAxisVariable;
  String? yAxisVariable;
  String? xAxisName;
  Map<String, List<DataPoint>> sliders = {};
  bool? flipAxes;
  String? sortOn;
  bool? sortAscending;

  Query({
    this.xAxisVariable,
    this.yAxisVariable,
    this.xAxisName,
    this.flipAxes,
    this.sortOn,
    this.sortAscending,
  });

  String? yAxisNameForSeries(
      String series, Map<String, List<DataPoint>> dataSeries) {
    return series;
  }

  String? createAxes(Map<String, List<DataPoint>> dataSeries) {
    return null;
  }
}

class StackedBarPage extends StatefulWidget {
  const StackedBarPage({super.key});

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

class StackedBarPageState extends State<StackedBarPage> {
  Query query = Query(
    xAxisVariable: 'X Axis',
    yAxisVariable: 'Y Axis',
    flipAxes: false,
    sortOn: 'x',
    sortAscending: true,
  );

  // Mock request to return filtered results
  Map<String, List<DataPoint>> getFilteredResult() {
    return {
      'Series1': [
        DataPoint('Jan', 10),
        DataPoint('Feb', 15),
        DataPoint('Mar', 20),
        DataPoint('May', 30),
      ],
      'Series2': [
        DataPoint('Jan', 12),
        DataPoint('Feb', 18),
        DataPoint('Apr', 22),
        DataPoint('May', 16),
      ],
      'Series3': [
        DataPoint('Jan', 11),
        DataPoint('Feb', 18),
        DataPoint('Mar', 25),
        DataPoint('Apr', 26),
        DataPoint('May', 12),
      ],
      'Series4': [
        DataPoint('Jan', 40),
        DataPoint('Feb', 18),
        DataPoint('Mar', 25),
        DataPoint('Apr', 18),
        DataPoint('May', 30),
      ],
    };
  }

  @override
  Widget build(BuildContext context) {
    var result = getFilteredResult();

    var xAxis = CategoryAxis(
      maximumLabelWidth: 240,
      labelIntersectAction: AxisLabelIntersectAction.rotate45,
      title: AxisTitle(
        text: query.xAxisName ?? query.xAxisVariable?.replaceAll('_', ' '),
      ),
    );

    var dataSeries = result;
    var legendOverflow = dataSeries.keys.any((element) => element.length > 20);
    var colorPalette = Theme.of(context).brightness == Brightness.light
        ? [Colors.blue, Colors.purple, Colors.yellow]
        : [Colors.purple, Colors.blue, Colors.red];

    return SfCartesianChart(
      tooltipBehavior: TooltipBehavior(enable: true),
      isTransposed: query.flipAxes != true,
      legend: Legend(
        position: LegendPosition.auto,
        title: const LegendTitle(text: 'Groups'),
        isVisible: dataSeries.keys.length > 1,
      ),
      primaryXAxis: xAxis,
      primaryYAxis: NumericAxis(
        isVisible: false,
      ),
      axes: [
        // Define secondary Y-axis
        NumericAxis(
          name: 'secondaryYAxis',
          // isVisible: false, // Set the secondary axis visibility to false
        ),
      ],
      series: <CartesianSeries<DataPoint, dynamic>>[
        ...dataSeries.keys.map((series) {
          return StackedBarSeries<DataPoint, dynamic>(
            name: series,
            color: colorPalette[dataSeries.keys.length > 1 &&
                    dataSeries.keys.toList().indexOf(series) <
                        colorPalette.length
                ? dataSeries.keys.toList().indexOf(series)
                : (dataSeries.keys.toList().indexOf(series) %
                    colorPalette.length)],
            pointColorMapper: (DataPoint data, i) => colorPalette[
                dataSeries.keys.length > 1 &&
                        dataSeries.keys.toList().indexOf(series) <
                            colorPalette.length
                    ? dataSeries.keys.toList().indexOf(series)
                    : xAxis is DateTimeAxis
                        ? 0
                        : (i % colorPalette.length)],
            dataSource: dataSeries[series]!,
            yAxisName: 'secondaryYAxis',
            xAxisName: query.xAxisName,
            xValueMapper: (DataPoint data, _) => data.x,
            yValueMapper: (DataPoint data, _) => data.y,
          );
        }),
      ],
    );
  }
}
Screenshot 2024-11-19 at 15 03 53
Justus-M commented 2 days ago

@LavanyaGowtham2021 please Note the waiting for customer response tag is no longer relevant