Grouping Date Axis

Hello,

I have chart that display Month Year as shown below



Would It be possible for sfCartesianChart to group month within a year as screenshot below




Best regards


Sao





4 Replies

HK Hariharasudhan Kanagaraj Syncfusion Team July 5, 2023 12:45 PM UTC

Hi Sao,


We have analyzed your query, you can achieve your requirement with help of the multiLevelLabels property in the primaryXAxis. Here, you can use DateTimeAxis as primaryXAxis and use the multiLevelLabels property to show the specific year in the chart as per the following code snippet.


SfCartesianChart(

  primaryXAxis: DateTimeAxis(

    intervalType: DateTimeIntervalType.months,

    dateFormat: DateFormat.MMM(),

    minimum: DateTime(2021, 01),

    maximum: DateTime(2023, 12),

    interval: 02,

    labelAlignment: LabelAlignment.end,

    multiLevelLabels: [

      DateTimeMultiLevelLabel(

        start: DateTime(2021, 01),

        end: DateTime(2021, 12),

        level: 0,

        text: '2021',

      ),

      DateTimeMultiLevelLabel(

        start: DateTime(2021, 12),

        end: DateTime(2022, 12),

        level: 0,

        text: '2022',

      ),

      DateTimeMultiLevelLabel(

        start: DateTime(2022, 12),

        end: DateTime(2023, 12),

        level: 0,

        text: '2023',

      ),

    ],

    multiLevelLabelStyle: const MultiLevelLabelStyle(

      borderColor: Colors.grey,

      borderType: MultiLevelBorderType.rectangle,

      borderWidth: 2,

      textStyle: TextStyle(

        color: Colors.red,

        fontSize: 12,

      ),

    ),

  ),

  primaryYAxis: NumericAxis(),

  series: <CartesianSeries<ChartSampleData, DateTime>>[

    ColumnSeries(

      dataSource: data,

      xValueMapper: (ChartSampleData sales, int index) => sales.x,

      yValueMapper: (ChartSampleData sales, int index) => sales.y,

    )

  ],

),


Snapshot:



Also shared the sample below for your reference.


Regards,

Hari Hara Sudhan. K


Attachment: 183290_1ab576ca.zip


SA Sao July 6, 2023 07:10 AM UTC

Dear  Hari Hara Sudhan. K,


Thank you for provide me the solution, However, if the chart is connected to data dynamically, how can we assign the  multiLevelLabels: []?

Thank you.


Sao



HK Hariharasudhan Kanagaraj Syncfusion Team July 10, 2023 02:32 PM UTC

Hi Sao,

We are currently working on the workaround sample to provide the best possible solution from our end and will share further details within one business day on July 11, 2023. We appreciate your patience until then.

Regards,

Hari Hara Sudhan. K



HK Hariharasudhan Kanagaraj Syncfusion Team July 13, 2023 01:09 PM UTC

Hi Sao,


We have analyzed your query and to update the multilevel labels dynamically you can use public method updateDataSource using ChartSeriesController and onActualRangeChanged callback. In SfCartesianChart, the multilevel labels will be rendered at the given position at load time itself, so while updating the data points dynamically, the visible minimum and visible maximum of the DateTimeAxis will be changed and thus the multilevel labels will be visible.


For example, here we have rendered the 5 segments with the data points for the 2021 at load time itself. But the multilevel labels for all the 2021, 2022, and 2023 will be rendered and the multilevel label for 2021 will be visible on the screen. While we update the data points dynamically, actual range of the DateTimeAxis changes and thus the multilevel label for the respective data points will be visible on the screen.


Modify the below shared code snippet based on your requirement to achieve the expected output.


class _UsagePatternChartState extends State<UsagePatternChart> {

  ValueNotifier<bool> isRangeExists = ValueNotifier(false);

  late ChartSeriesController _chartSeriesController;

  late List<ChartSampleData> data;

  late Random random;

  int count = 10;

 

  @override

  void initState() {

    data = [

      ChartSampleData(DateTime(2021, 01, 01), 60),

      ChartSampleData(DateTime(2021, 03, 01), 50),

      ChartSampleData(DateTime(2021, 06, 01), 20),

      ChartSampleData(DateTime(2021, 09, 01), 80),

      ChartSampleData(DateTime(2021, 12, 01), 60),

    ];

    random = Random();

    super.initState();

  }

 

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      body: Center(

        child: SfCartesianChart(

          onActualRangeChanged: (ActualRangeChangedArgs rangeChangedArgs) {

            if (rangeChangedArgs.orientation == AxisOrientation.horizontal) {

              if (rangeChangedArgs.actualMax > DateTime(2023, 12).millisecondsSinceEpoch) {

                isRangeExists.value = true;

              }

            }

          },

          primaryXAxis: DateTimeAxis(

            intervalType: DateTimeIntervalType.months,

            dateFormat: DateFormat.MMM(),

            interval: 02,

            multiLevelLabels: [

              DateTimeMultiLevelLabel(

                start: DateTime(2021, 01),

                end: DateTime(2021, 12),

                level: 02,

                text: '2021',

              ),

              DateTimeMultiLevelLabel(

                start: DateTime(2022, 01),

                end: DateTime(2022, 12),

                level: 02,

                text: '2022',

              ),

              DateTimeMultiLevelLabel(

                start: DateTime(2023, 01),

                end: DateTime(2023, 12),

                level: 02,

                text: '2023',

              ),

            ],

            multiLevelLabelStyle: const MultiLevelLabelStyle(

              borderColor: Colors.grey,

              borderType: MultiLevelBorderType.rectangle,

              borderWidth: 2,

              textStyle: TextStyle(

                color: Colors.red,

                fontSize: 12,

              ),

            ),

          ),

          primaryYAxis: NumericAxis(),

          series: <CartesianSeries<ChartSampleData, DateTime>>[

            ColumnSeries(

              dataSource: data,

              xValueMapper: (ChartSampleData sales, int index) => sales.x,

              yValueMapper: (ChartSampleData sales, int index) => sales.y,

              onRendererCreated: (ChartSeriesController controller) {

                _chartSeriesController = controller;

              },

            ),

          ],

        ),

      ),

      floatingActionButton: Padding(

        padding: const EdgeInsets.only(left: 20.0, top: 20),

        child: Align(

          alignment: Alignment.topLeft,

          child: ValueListenableBuilder(

            valueListenable: isRangeExists,

            builder: (context, value, child) {

              return FloatingActionButton.small(

                onPressed: () {

                  if (!isRangeExists.value) {

                    addData();

                    _chartSeriesController.updateDataSource(addedDataIndex: data.length - 1);

                  }

                },

                child: const Icon(Icons.add),

              );

            },

          ),

        ),

      ),

    );

  }

 

  void addData() {

    data.add(

      ChartSampleData(

        DateTime(2022, 03).add(

          Duration(days: count),

        ),

        random.nextInt(100),

      ),

    );

    count = count + 60;

  }

}


Also attached the recording below for your reference and if you have further queries, please get back to us.


Regards,

Hari Hara Sudhan. K


Attachment: 183290_ce3395cd.zip

Loader.
Up arrow icon