How to create custom cartesian graph like below screenshot with gradient background?



Hi Team,
I have been trying the build a graph like below using the sync fusion sfcartesian package. but not able to bring the exact style. can you please help with the same?
you can find my code below.

SfCartesianChart(
primaryYAxis: NumericAxis(
labelFormat: '{value}%',
majorGridLines:
const MajorGridLines(color: Color(0xFF364254)),
),
primaryXAxis: CategoryAxis(
majorGridLines:
const MajorGridLines(color: Color(0xFF364254)),
),
series: <LineSeries<GoalHistoryData, String>>[
LineSeries<GoalHistoryData, String>(
dataSource: goalHistoryState.data,
color: tabData.progressColor,
dataLabelSettings: const DataLabelSettings(
color: Color(0xFF8EA2BC),
),
xValueMapper: (GoalHistoryData data, _) =>
data.getDateRangeString(),
yValueMapper: (GoalHistoryData data, _) =>
data.percentage,
),
],
)


 


7 Replies

HK Hariharasudhan Kanagaraj Syncfusion Team September 4, 2023 02:08 PM UTC

Hi Sabarinathan,


You can achieve the mentioned requirement by applying the gradient to the Area Series using the gradient property and by setting the BorderDrawMode.top to the borderDrawMode property. This ensures that the border will be drawn only on the top of the Area Series. Additionally, you can enable and customize the markers according to your needs using the markerSettings property in the Area Series.


Also shared the user guide documentation below to render the Area Series with gradient for your reference.

UG: https://help.syncfusion.com/flutter/cartesian-charts/chart-types/area-chart#area-with-gradients.


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


Regards,

Hari Hara Sudhan. K.



SB Sabarinathan B September 4, 2023 04:50 PM UTC

hi thanks for the reply. i tried with the area series.

still have few questions.

how do we start the area grap from start of the interval axis like in above screenshot?

right now it starts with center of the each interval.

how do we bring double line label for the x axis? like in the above screenshot?

How do we make the y axis label as const from 0 to 100 % as my data will always be less than 100 and i want to show the labels till 100.   ?  

thanks.




Screenshot 2023-09-04 at 10.14.20 PM.png



HK Hariharasudhan Kanagaraj Syncfusion Team September 5, 2023 02:17 PM UTC

Hi Sabarinathan,


Query 1: how do we start the area gap from start of the interval axis like in above screenshot?


You can set the labelPlacement property as LabelPlacement.onTicks in the primaryXAxis property to render the Area Series and x-axis labels from start of the interval axis as mentioned.


Shared the User Guide documentation and Sample Browser links below regarding the LabelPlacement of the Category Axis for your reference.


UG: https://help.syncfusion.com/flutter/cartesian-charts/axis-types#placing-labels-between-the-ticks.

SB: https://flutter.syncfusion.com/#/cartesian-charts/axis-types/category/label-placement.


Query 2: how do we bring double line label for the x axis? like in the above screenshot?


We have given the newline symbol ‘/n’ to the data values of the x axis to render the labels in next line. However, you can also use the axisLabelFormatter callback to modify and customize the axis labels according to your needs.


Shared the User Guide documentation link below regarding the axisLabelFormatter callback for your reference.


UG: https://help.syncfusion.com/flutter/cartesian-charts/callbacks#axislabelformatter.


Query 3: How do we make the y axis label as const from 0 to 100 % as my data will always be less than 100 and I want to show the labels till 100?


You can use the visibleMaximum and interval properties of the primaryYAxis to achieve the mentioned requirement. Here, we have set visibleMaximum property and interval property as 100 and 25 respectively, to achieve the output as mentioned in the provided snapshot.


Kindly refer the code snippet below:

class _MainAppState extends State<MainApp> {

  late List<GoalHistoryData> _chartData;

 

  @override

  void initState() {

    _chartData = [

      GoalHistoryData('Jul \n 11 - 17', 75),

      GoalHistoryData('Jul \n 18 - 14', 55),

      GoalHistoryData('Jul \n 25 - 31', 75),

      GoalHistoryData('Aug \n 01 - 07', 50),

      GoalHistoryData('Aug \n 08 - 14', 75),

      GoalHistoryData('Aug \n 15 - 20', 45),

      GoalHistoryData('Aug \n 21 - 38', 100),

    ];

    super.initState();

  }

 

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      debugShowCheckedModeBanner: false,

      home: Scaffold(

          body: Padding(

        padding: const EdgeInsets.all(10.0),

        child: SfCartesianChart(

          primaryXAxis: CategoryAxis(

            labelPlacement: LabelPlacement.onTicks,

            majorGridLines: const MajorGridLines(

              color: Color(0xFF364254),

            ),

          ),

          primaryYAxis: NumericAxis(

            interval: 25,

            visibleMaximum: 100,

            labelFormat: '{value}%',

            majorGridLines: const MajorGridLines(

              color: Color(0xFF364254),

            ),

          ),

          series: <CartesianSeries<GoalHistoryData, String>>[

            AreaSeries<GoalHistoryData, String>(

              dataSource: _chartData,

              markerSettings: const MarkerSettings(

                isVisible: true,

                borderColor: Color.fromRGBO(07, 177, 165, 100),

                height: 10,

                width: 10,

              ),

              xValueMapper: (GoalHistoryData data, _) => data.dateRange,

              yValueMapper: (GoalHistoryData data, _) => data.percentage,

              borderColor: const Color.fromRGBO(07, 177, 165, 100),

              gradient: const LinearGradient(

                begin: Alignment.topCenter,

                end: Alignment.bottomCenter,

                colors: [

                  Color.fromRGBO(25, 97, 102, 90),

                  Color.fromRGBO(29, 82, 90, 50),

                  Color.fromRGBO(37, 48, 62, 05),

                ],

                stops: [0.5, 0.75, 1],

              ),

              borderWidth: 3,

              borderDrawMode: BorderDrawMode.top,

            ),

          ],

        ),

      )),

    );

  }

}


Snapshot:


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


Regards,
Hari Hara Sudhan. K.



SB Sabarinathan B September 8, 2023 10:14 AM UTC

Hi Hari,

Thanks,

Can you help with one another request?

How to show the marker dot only if there is a value other than 0? I do not want to show dots where the values are 0.


Regards,
Sabari



HK Hariharasudhan Kanagaraj Syncfusion Team September 11, 2023 12:17 PM UTC

Hi Sabarinathan,


You can achieve the mentioned requirement using the onMarkerRender callback by setting the height and width of the marker as zero only when the y values are equal to zero. If the y values are not zero, then marker will render based on the height and width of the marker given in the markerSettings property.


Kindly refer the code snippet below:

class _MainAppState extends State<MainApp> {

  late List<GoalHistoryData> _chartData;

 

  @override

  void initState() {

    _chartData = [

      GoalHistoryData('Jul \n 11 - 17', 75),

      GoalHistoryData('Jul \n 18 - 14', 00),

      GoalHistoryData('Jul \n 25 - 31', 75),

      GoalHistoryData('Aug \n 01 - 07', 50),

      GoalHistoryData('Aug \n 08 - 14', 75),

      GoalHistoryData('Aug \n 15 - 20', 00),

      GoalHistoryData('Aug \n 21 - 38', 100),

    ];

 

    super.initState();

  }

 

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      debugShowCheckedModeBanner: false,

      home: Scaffold(

        body: Padding(

          padding: const EdgeInsets.all(10.0),

          child: SfCartesianChart(

            onMarkerRender: (markerArgs) {

              if (_chartData[markerArgs.pointIndex!].percentage == 0) {

                markerArgs.markerHeight = 0;

                markerArgs.markerWidth = 0;

              }

            },

            primaryXAxis: CategoryAxis(

              labelPlacement: LabelPlacement.onTicks,

              majorGridLines: const MajorGridLines(

                color: Color(0xFF364254),

              ),

            ),

            primaryYAxis: NumericAxis(

              interval: 25,

              visibleMaximum: 100,

              labelFormat: '{value}%',

              majorGridLines: const MajorGridLines(

                color: Color(0xFF364254),

              ),

            ),

            series: <CartesianSeries<GoalHistoryData, String>>[

              AreaSeries<GoalHistoryData, String>(

                dataSource: _chartData,

                markerSettings: const MarkerSettings(

                  isVisible: true,

                  borderColor: Color.fromRGBO(07, 177, 165, 100),

                  height: 10,

                  width: 10,

                ),

                xValueMapper: (GoalHistoryData data, _) => data.dateRange,

                yValueMapper: (GoalHistoryData data, _) => data.percentage,

                borderColor: const Color.fromRGBO(07, 177, 165, 100),

                gradient: const LinearGradient(

                  begin: Alignment.topCenter,

                  end: Alignment.bottomCenter,

                  colors: [

                    Color.fromRGBO(25, 97, 102, 90),

                    Color.fromRGBO(29, 82, 90, 50),

                    Color.fromRGBO(37, 48, 62, 05),

                  ],

                  stops: [0.5, 0.75, 1],

                ),

                borderWidth: 3,

                borderDrawMode: BorderDrawMode.top,

              ),

            ],

          ),

        ),

      ),

    );

  }

}

 

class GoalHistoryData {

  GoalHistoryData(this.dateRange, this.percentage);

  final String dateRange;

  final double percentage;

}


Snapshot:


You can modify the shared code snippet according to your needs and if you have further queries, please get back to us.


Regards,

Hari Hara Sudhan. K.



SB Sabarinathan B October 5, 2023 10:30 AM UTC

Hi Hari,

Can you help with making the graph scrollable to the left?

Basically, I want to fit 6 weeks in the screen and should be able to scroll left to the past weeks.


Thanks.



HK Hariharasudhan Kanagaraj Syncfusion Team October 6, 2023 12:27 PM UTC

Hi Sabarinathan,


You can achieve the mentioned requirement using the autoScrollingDelta property and autoScrollingMode property along with the ZoomPanBehavior. We have prepared a sample by setting the autoScrollingDelta as 6 and autoScrollingMode as AutoScrollingMode.end in the primaryXAxis to render the Area Segments for only the last six weeks at load time. The remaining Area Segments will become visible while scrolling the chart to left by enabling the enablePanning property and by setting the zoomMode property as ZoomMode.x in the ZoomPanBehavior to scroll the chart only horizontally.


Kindly refer the code snippet below :

class MainApp extends StatefulWidget {

  const MainApp({super.key});

 

  @override

  State<MainApp> createState() => _MainAppState();

}

 

class _MainAppState extends State<MainApp> {

  late List<GoalHistoryData> _chartData;

  late ZoomPanBehavior _zoomPanBehavior;

 

  @override

  void initState() {

    _chartData = [

      GoalHistoryData('Jul \n 11 - 17', 75),

      GoalHistoryData('Jul \n 18 - 14', 00),

      GoalHistoryData('Jul \n 25 - 31', 75),

      GoalHistoryData('Aug \n 01 - 07', 50),

      GoalHistoryData('Aug \n 08 - 14', 75),

      GoalHistoryData('Aug \n 15 - 20', 00),

      GoalHistoryData('Aug \n 21 - 28', 100),

      GoalHistoryData('Sep \n 01 - 07', 50),

      GoalHistoryData('Sep \n 08 - 14', 75),

      GoalHistoryData('Sep \n 15 - 21', 50),

      GoalHistoryData('Sep \n 22 - 28', 75),

      GoalHistoryData('Oct \n 01 - 07', 50),

      GoalHistoryData('Oct \n 08 - 14', 25),

      GoalHistoryData('Oct \n 15 - 21', 50),

      GoalHistoryData('Oct \n 22 - 28', 100),

    ];

    _zoomPanBehavior = ZoomPanBehavior(

      enablePanning: true,

      zoomMode: ZoomMode.x,

    );

    super.initState();

  }

 

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      debugShowCheckedModeBanner: false,

      home: Scaffold(

        body: Padding(

          padding: const EdgeInsets.all(10.0),

          child: SfCartesianChart(

            onMarkerRender: (markerArgs) {

              if (_chartData[markerArgs.pointIndex!].percentage == 0) {

                markerArgs.markerHeight = 0;

                markerArgs.markerWidth = 0;

              }

            },

            primaryXAxis: CategoryAxis(

              autoScrollingDelta: 6,

              autoScrollingMode: AutoScrollingMode.end,

              labelPlacement: LabelPlacement.onTicks,

              majorGridLines: const MajorGridLines(

                color: Color(0xFF364254),

              ),

            ),

            primaryYAxis: NumericAxis(

              interval: 25,

              visibleMaximum: 100,

              labelFormat: '{value}%',

              majorGridLines: const MajorGridLines(

                color: Color(0xFF364254),

              ),

            ),

            series: <CartesianSeries<GoalHistoryData, String>>[

              AreaSeries<GoalHistoryData, String>(

                dataSource: _chartData,

                markerSettings: const MarkerSettings(

                  isVisible: true,

                  borderColor: Color.fromRGBO(07, 177, 165, 100),

                  height: 10,

                  width: 10,

                ),

                xValueMapper: (GoalHistoryData data, _) => data.dateRange,

                yValueMapper: (GoalHistoryData data, _) => data.percentage,

                borderColor: const Color.fromRGBO(07, 177, 165, 100),

                gradient: const LinearGradient(

                  begin: Alignment.topCenter,

                  end: Alignment.bottomCenter,

                  colors: [

                    Color.fromRGBO(25, 97, 102, 90),

                    Color.fromRGBO(29, 82, 90, 50),

                    Color.fromRGBO(37, 48, 62, 05),

                  ],

                  stops: [0.5, 0.75, 1],

                ),

                borderWidth: 3,

                borderDrawMode: BorderDrawMode.top,

              ),

            ],

            zoomPanBehavior: _zoomPanBehavior,

          ),

        ),

      ),

    );

  }

}

 

class GoalHistoryData {

  GoalHistoryData(this.dateRange, this.percentage);

 

  final String dateRange;

  final double percentage;

}


If we set an autoScrollingMode property as AutoScrollingMode.start, only the first set of data points from the data collection, based on the value specified in the autoScrollingDelta property, will be rendered at load time. The remaining segments will become visible while scrolling the chart from right to left.


If we set an autoScrollingMode property as AutoScrollingMode.end, only the last set of data points from the data collection, based on the value specified in the autoScrollingDelta property, will be rendered at load time. The remaining segments will become visible while scrolling the chart from left to right.


Shared the User Guide Documentation link below regarding the Auto Scrolling feature for your reference.

UG : https://help.syncfusion.com/flutter/cartesian-charts/axis-customization#auto-scrolling.


Also shared the recording below for your reference and you can modify the shared code snippet according to your needs. If you have further details, please get back to us.


Regards,
Hari Hara Sudhan. K.


Attachment: _184398_f308f061.zip

Loader.
Up arrow icon