Fix Column range

Hi,

When using a `ColumnSeries`in a `SfCartesionChart`with a `primaryXAxis: DateTimeAxis()`, is there a way to fix the start and end of the columns, or their width?

They seem to center by default on the provided date value from `xValueMapper`, and be as wide as the smallest `x` distance between data points, which is fine, until I try to add another Serie to stack on the same graph. At that point, the second serie messes up the columns width.

Example code:


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

class GraphDataPoint {
final DateTime date;
final int value;
const GraphDataPoint(this.date, this.value);
}

class GrowthGraphTest extends StatelessWidget {
const GrowthGraphTest({
super.key,
});

@override
Widget build(BuildContext context) {
final now = DateTime.now();
final columnData = <GraphDataPoint>[
GraphDataPoint(now, 7),
GraphDataPoint(now.subtract(const Duration(days: 7)), 12),
GraphDataPoint(now.subtract(const Duration(days: 14)), 5),
GraphDataPoint(now.subtract(const Duration(days: 21)), 7),
];
final pointsData = <GraphDataPoint>[
// GraphDataPoint(now.subtract(const Duration(days: 3)), 10),
// GraphDataPoint(now.subtract(const Duration(days: 9)), 8),
// GraphDataPoint(now.subtract(const Duration(days: 11)), 6),
];
return SizedBox(
height: 300,
child: SfCartesianChart(
series: <ChartSeries>[
ColumnSeries(
width: 0.9,
dataSource: columnData,
xValueMapper: (datum, _) => datum.date,
yValueMapper: (datum, _) => datum.value,
),
BubbleSeries<GraphDataPoint, DateTime>(
dataSource: pointsData,
xValueMapper: (datum, _) => datum.date,
yValueMapper: (datum, _) => datum.value,
),
],
primaryXAxis: DateTimeAxis(),
primaryYAxis: NumericAxis(),
),
);
}
}


In this example code, if you uncomment one or several lines from the `pointsData`, you will see the columns width changing. Why is this happening and how can we solve this?

Thank you,

Louis


Just the Column Serie:

just-columns.jpg


Same Column Serie with a BubbleSerie added:

columns-with-a-bubble.jpg



7 Replies 1 reply marked as answer

HK Hariharasudhan Kanagaraj Syncfusion Team July 20, 2023 02:59 PM UTC

Hi Louis Deveseleer,


We have analyzed your query and to achieve your requirement, you can customize the rendering of the column series with the fixed width using the ColumnSeriesRenderer and ColumnSegment.


In the ColumnSeries, the width of the column will be calculated based on the available size and axis range and it is the current behavior of the SfCartesianChart. However, To render all the column with equal width, you can render the column with the fixed width in the ColumnSeriesRenderer by creating a renderer using the onCreateRenderer
callback.


Disclaimer: However, while rendering the column with the fixed width, the column segment might overlap with the other column segment.


Kindly refer the code snippet below:

class GrowthGraphTest extends StatelessWidget {

  const GrowthGraphTest({super.key});

 

  @override

  Widget build(BuildContext context) {

    final now = DateTime.now();

    final columnData = <GraphDataPoint>[

      GraphDataPoint(now, 7),

      GraphDataPoint(now.subtract(const Duration(days: 7)), 12),

      GraphDataPoint(now.subtract(const Duration(days: 14)), 5),

      GraphDataPoint(now.subtract(const Duration(days: 21)), 7),

    ];

    final pointsData = <GraphDataPoint>[

      GraphDataPoint(now.subtract(const Duration(days: 10)), 10),

      GraphDataPoint(now.subtract(const Duration(days: 9)), 8),

      GraphDataPoint(now.subtract(const Duration(days: 11)), 6),

    ];

    return MaterialApp(

      home: Scaffold(

        body: SizedBox(

          height: 300,

          child: SfCartesianChart(

            series: <ChartSeries>[

              ColumnSeries(

                dataSource: columnData,

                xValueMapper: (datum, _) => datum.date,

                yValueMapper: (datum, _) => datum.value,

                onCreateRenderer: (series) {

                  return CustomColumnSeriesRenderer();

                },

              ),

              BubbleSeries<GraphDataPoint, DateTime>(

                dataSource: pointsData,

                xValueMapper: (datum, _) => datum.date,

                yValueMapper: (datum, _) => datum.value,

              ),

            ],

            primaryXAxis: DateTimeAxis(),

            primaryYAxis: NumericAxis(),

          ),

        ),

      ),

    );

  }

}

 

class CustomColumnSeriesRenderer extends ColumnSeriesRenderer {

  @override

  ChartSegment createSegment() {

    return CustomColumnSegment();

  }

}

 

class CustomColumnSegment extends ColumnSegment {

  final double width = 50;

  @override

  void onPaint(Canvas canvas) {

    final double centerX = segmentRect.center.dx;

    final double left = centerX - (width / 2);

    final double right = centerX + (width / 2);

    final Paint paint = Paint();

 

    paint

      ..color = Colors.blue

      ..style = PaintingStyle.fill;

 

    final Rect rect =

        Rect.fromLTRB(left, segmentRect.top, right, segmentRect.bottom);

 

    final RRect customRect = RRect.fromRectAndRadius(rect, Radius.zero);

 

    canvas.drawRRect(customRect, paint);

  }

}


Snapshot:


Column Series:


Column Series with Bubble Series:



Also attached the sample below for your reference.


Regards,

Hari Hara Sudhan. K


Attachment: 183567_4a23fb40.zip


LD Louis Deveseleer July 20, 2023 04:35 PM UTC

Hello Hari Hara Sudhan. K,


Thank you for your answer! This should work as a workaround but the Custom renderer is overriding all the styling that we would otherwise put inside the `ColumnSeries`, and the calculation of the column width in pixels will be complicated, even with fixed `visibleMinimum` and `visibleMaximum` for the `XAxis`, since `SfCartesianChart` determines itself the width taken by the axis labels...


Is there no way to force the graph to respect the `ColumnSeries.width` parameter with respect to the `ColumnSeries.dataSource`, instead of letting data from other series mess things up? This would make things a lot easier. Even an extra parameter to use a fixed width with respect to the `XAxis` (in my case a DateTime range) would be a lot easier than override everything at once.


Please let me know if there is a solution like this available!

Thank you,

Louis



LD Louis Deveseleer July 20, 2023 05:09 PM UTC

Another question related to your answer:

I am calculating the column width in the widget containing the graph, and passing that value down to the `ColumnSeriesRender`. But if the width value gets updated (and the widget rebuilds), the `ColumnSeriesRender` does not update itself: it keeps the first width value that was passed to it.

Is there access to some sort of `shouldRepaint` method, like we have in the `CustomPainter`, to tell the Renderer to refresh when some parameter changes? I could not find any but maybe I missed it.



LD Louis Deveseleer July 20, 2023 05:15 PM UTC

Alternatively, would there be a way to overlay widgets on the graph, at specified (x,y) positions ? This would also solve my original issue.



HK Hariharasudhan Kanagaraj Syncfusion Team July 21, 2023 10:40 AM UTC

Hi Louis Deveseleer,


To achieve your requirement, you can render the column series in the primaryXAxis and primaryYAxis, and you can render another series in the secondary x and y axes using the axes property. You can also render the secondary x and y axis in the opposite direction using the opposedPosition property.


While rendering the column series in the primaryXAxis and primaryYAxis, the actual range of the primary x and y axes will be calculated based on the data source of the column series only. Hence, the axis range of the column series will not be changed while adding another respective series and thus the width of the column will be static.


You can overlay the widgets on the graph, at the specified x and y position using the annotations property in the SfCartesianChart.


If you have further queries, please get back to us.


Regards,
Hari Hara Sudhan. K


Marked as answer

LD Louis Deveseleer July 24, 2023 05:50 PM UTC

Thank you!



HK Hariharasudhan Kanagaraj Syncfusion Team July 25, 2023 05:37 AM UTC

Hi Louis Deveseleer,


Most Welcome. Kindly get back to us if you have further queries. We are always happy to assist you.


Regards,

Hari Hara Sudhan. K


Loader.
Up arrow icon