Methods in Flutter Cartesian Charts (SfCartesianChart)

11 Jun 202424 minutes to read

Methods in tooltipBehavior

Show method in tooltipBehavior

The show method is used to activate the tooltip at the specified x and y point values.

late SfCartesianChart chart;
    late TooltipBehavior _tooltipBehavior;
    @override
    void initState(){
      _tooltipBehavior = TooltipBehavior(enable: true);
      super.initState();
    }

    @override
    Widget build(BuildContext context) {

      final List<ChartData> chartData = [
          ChartData(10, 17),
          ChartData(20, 34),
          // Add the required data
      ];
      chart = SfCartesianChart(
        tooltipBehavior: _tooltipBehavior,
          series: <CartesianSeries>[
            ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ]
      );

      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Show'),
                onPressed: show
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void show() {
        _tooltipBehavior.show(20, 34);
    }

    class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

showByIndex method in tooltipBehavior

The showByIndex method is used to display the tooltip at the specified series and point index.

The below mentioned arguments are given to the showByIndex method:

  • seriesIndex - index of the series for which the pointIndex is specified.

  • pointIndex - index of the point for which the tooltip should be shown.

late SfCartesianChart chart;
    late TooltipBehavior _tooltipBehavior;
    
    @override
    void initState(){
      _tooltipBehavior = TooltipBehavior(enable: true);
      super.initState();
    }

    @override
    Widget build(BuildContext context) {

      final List<ChartData> chartData = [
          ChartData(10, 17),
          ChartData(20, 34),
          // Add the required data
      ];
      chart = SfCartesianChart(
        tooltipBehavior: _tooltipBehavior,
          series: <CartesianSeries>[
            ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ]
      );

      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Show'),
                onPressed:(){
                    _tooltipBehavior.showByIndex(0,1);
                }
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

showByPixel method in tooltipBehavior

The showByPixel method is used to display the tooltip at the specified x and y-positions.

x & y - logical pixel values to position the tooltip.

late SfCartesianChart chart;
    late TooltipBehavior _tooltipBehavior;
    
    @override
    void initState(){
      _tooltipBehavior = TooltipBehavior(enable: true);
      super.initState();
    }

    @override
    Widget build(BuildContext context) {

      final List<ChartData> chartData = [
          ChartData(10, 17),
          ChartData(20, 34),
          // Add the required data
      ];
      chart = SfCartesianChart(
        tooltipBehavior: _tooltipBehavior,
          series: <CartesianSeries>[
            ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ]
      );

      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Show'),
                onPressed:(){
                  _tooltipBehavior.showByPixel(230.0,470.0);
                }
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Hide method in tooltipBehavior

The hide method is used to hide the displaying tooltip programmatically.

late SfCartesianChart chart;
    late TooltipBehavior _tooltipBehavior;
    
    @override
    void initState(){
      _tooltipBehavior = TooltipBehavior(enable: true);
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
      final List<ChartData> chartData = [
          ChartData(10, 17),
          ChartData(20, 34)
      // Add the required data  
      ];
      chart = SfCartesianChart(
        tooltipBehavior: _tooltipBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
          ]
      );

      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Hide'),
                onPressed: hide
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void hide(){
      _tooltipBehavior.hide();
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Methods in trackballBehavior

Show method in trackballBehavior

The show method is used to activate the trackball at the specified location.

late SfCartesianChart chart;
  late TrackballBehavior _trackballBehavior;

  @override
  void initState() {
    _trackballBehavior = TrackballBehavior(enable: true);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    final List<ChartData> chartData = [
      ChartData(10, 17),
      ChartData(20, 34)
      // Add the required data
    ];

    chart = SfCartesianChart(
        trackballBehavior: _trackballBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ]);

    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
             TextButton(
              child: Text('Show'),
              onPressed: () {
                      _trackballBehavior.show(10, 17);
              }
             ),
             Container(child: chart)
          ]
        )
      )
    );
  }

    class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

showByIndex method in trackballBehavior

The showByIndex method is used to activate the trackball at the specified point index.

The pointIndex specifies the index of the trackball point.

late SfCartesianChart chart;
    late TrackballBehavior _trackballBehavior;
    
    @override
    void initState(){
      _trackballBehavior = TrackballBehavior(enable: true);
      super.initState();
    }
  
    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34)
        // Add the required data
      ];
      
      chart = SfCartesianChart(
        trackballBehavior: _trackballBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Show'),
                onPressed: () {
                  _trackballBehavior.showByIndex(3);
                },
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Hide method in trackballBehavior

The hide method is used to hide the displaying trackball programmatically.

late SfCartesianChart chart;
    late TrackballBehavior _trackballBehavior;
    
    @override
    void initState(){
      _trackballBehavior = TrackballBehavior(enable: true);
      super.initState();
    }
  
    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
        // Add the required data
      ];
      
      chart = SfCartesianChart(
        trackballBehavior: _trackballBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ],
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Hide'),
                onPressed: hide
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void hide() {
        _trackballBehavior.hide();
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Events in trackballBehavior

Provided the following methods for handling pointer and gesture events and allowing customizations for various pointer events such as long-press, tap, double-tap, pointer enter, and exit.

  • handleEvent - Specifies to customize the necessary pointer events.
  • handleLongPressStart - Specifies to customize the pointer event when a long-press begins.
  • handleLongPressMoveUpdate - Specifies to customize the pointer event during movement after a long-press.
  • handleLongPressEnd - Specifies to customize the pointer event when the pointer stops contacting the screen after a long-press.
  • handleTapDown - Specifies to customize the pointer event when a tap has contacted the screen once.
  • handleTapUp - Specifies to customize the pointer event when it has stopped contacting the screen after a tap.
  • handleDoubleTap - Specifies to customize the pointer event when a tap has contacted the screen twice.
  • handlePointerEnter -Specifies to customize the pointer event when the mouse enter on the screen.
  • handlePointerExit - Specifies to customize the pointer event when the mouse exit on the screen.

This following code sample defines how to enable or disable specific pointer events and customize to show trackball in double tap and disabled trackball showing in pointer enter and exit.

SfCartesianChart(
      trackballBehavior: CustomTrackball(),
      // Add series.
    );
    
    class CustomTrackball extends TrackballBehavior {
      @override
      bool get enable => true;

      // Disable the behavior of pointer events(move, cancel, hover and up).
      @override
      void handleEvent(PointerEvent event, BoxHitTestEntry entry) {}

      // Disable the behavior of the pointer enter on the screen.
      @override
      void handlePointerEnter(PointerEnterEvent details) {}

      // Disable the behavior of the pointer exit on the screen.
      @override
      void handlePointerExit(PointerExitEvent details) {}

      @override
      void handleDoubleTap(Offset position) {
        if (activationMode == ActivationMode.doubleTap) {
          Offset localPosition = parentBox!.globalToLocal(position);
          show(localPosition.dx, localPosition.dy, 'pixel');
        }
      }
    }

onPaint in trackballBehavior

The onPaint method is used to customize the label, position, and style of trackball tooltip.

SfCartesianChart(
      trackballBehavior: CustomTrackball(),
      // Add series.
    );

    class CustomTrackball extends TrackballBehavior {
      @override
      bool get enable => true;

      @override
      ActivationMode get activationMode => ActivationMode.singleTap;

      Offset? customPosition;

      @override
      void show(x, double y, [String coordinateUnit = 'point']) {
        if (coordinateUnit == 'pixel') {
          customPosition = Offset(x.toDouble(), y);
        }
        super.show(x, y, 'pixel');
      }

      @override
      void onPaint(PaintingContext context, Offset offset,
      SfChartThemeData chartThemeData, ThemeData themeData) {
        _drawCustomTrackballLine(context, offset, chartThemeData, themeData);
        TextStyle style = TextStyle(
          color: Colors.white,
          fontSize: 12,
          fontWeight: FontWeight.bold,
          background: Paint()
            ..color = Colors.blueGrey
            ..strokeWidth = 17
            ..strokeJoin = StrokeJoin.round
            ..strokeCap = StrokeCap.round
            ..style = PaintingStyle.stroke,
          );

        if (chartPointInfo.isNotEmpty) {
          _drawText(
            context.canvas,
            chartPointInfo[0].label!,
            Offset(chartPointInfo[0].xPosition!, chartPointInfo[0].yPosition!),
            style);
        }
      }

      void _drawCustomTrackballLine(PaintingContext context, Offset offset, SfChartThemeData chartThemeData, ThemeData themeData) {
        if (chartPointInfo.isNotEmpty && lineType != TrackballLineType.none) {
          final Rect plotAreaBounds = parentBox!.paintBounds;
          final double x = chartPointInfo[0].xPosition!;
          final Offset start = Offset(x, chartPointInfo[0].yPosition!);
          final Offset end = Offset(x, plotAreaBounds.bottom);
          final Paint paint = Paint()
            ..isAntiAlias = true
            ..color = Colors.red
            ..strokeWidth = 2
            ..style = PaintingStyle.stroke
            ..shader = ui.Gradient.linear(
              start,
              end,
              <Color>[Colors.blueGrey, Colors.white],
              <double>[0.75, 1],
            );

          context.canvas.drawLine(end, start, paint);
          context.canvas.drawCircle(start, 5,
              Paint()
                ..color = Colors.blueGrey
                ..style = PaintingStyle.fill);
          context.canvas.drawCircle(start, 5,
              Paint()
                ..color = Colors.blueGrey
                ..strokeWidth = 2.0
                ..style = PaintingStyle.stroke
                ..isAntiAlias = true);
        }
      }

      void _drawText(Canvas canvas, String text, Offset point,  TextStyle style) {
        final TextPainter tp = TextPainter(
          text: TextSpan(text: text, style: style),
          textDirection: TextDirection.ltr,
          textAlign: TextAlign.center,
          maxLines: getMaxLinesContent(text),
        );

        tp.layout();
        canvas.save();
        canvas.translate(point.dx - tp.width / 2, point.dy - (tp.height * 2));
        Offset labelOffset = Offset.zero;
        tp.paint(canvas, labelOffset);
        canvas.restore();
      }
    }

Customized trackball

Methods in crosshairBehavior

Show method in crosshairBehavior

The show method is used to activate the crosshair at the specified location.

late SfCartesianChart chart;
    late CrosshairBehavior _crosshairBehavior;

    @override
    void initState(){
      _crosshairBehavior = CrosshairBehavior(enable: true);
      super.initState();
    }
    
    @override
    Widget build(BuildContext context) {
      // Add the required data 
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
      ];

      chart = SfCartesianChart(
        crosshairBehavior: _crosshairBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y
            )
        ]
      );

      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Show'),
                onPressed: ()
                {
                  _crosshairBehavior.show(121, 164);
                }
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

showByIndex method in crosshairBehavior

The showByIndex method is used to activate the crosshair at the specified point index.

[pointIndex] - index of point at which the crosshair needs to be shown.

late SfCartesianChart chart;
    late CrosshairBehavior _crosshairBehavior;

    @override
    void initState(){
      _crosshairBehavior = CrosshairBehavior(enable: true);
      super.initState();
    }
    
    @override
    Widget build(BuildContext context) {
      // Add the required data 
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
      ];
      chart = SfCartesianChart(
        crosshairBehavior: _crosshairBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y
            )
        ]
      );

      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Show'),
                onPressed: ()
                {
                  _crosshairBehavior.showByIndex(2);
                }
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Hide method in crosshairBehavior

The hide method is used to hide the displaying crosshair programmatically.

late SfCartesianChart chart;
    late CrosshairBehavior _crosshairBehavior;

    @override
    void initState(){
      _crosshairBehavior = CrosshairBehavior(enable: true);
      super.initState();
    }
  
    @override
    Widget build(BuildContext context) {  
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34)
      // Add the required data  
      ];
      chart = SfCartesianChart(
        crosshairBehavior: _crosshairBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              enableTooltip: true,
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y)
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Hide'),
                onPressed: hide
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void hide() {
        _crosshairBehavior.hide();
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Events in crosshairBehavior

Provided the following methods for handling pointer and gesture events and allowing customizations for various pointer events such as long-press, tap, double-tap, pointer enter, and exit.

  • handleEvent - Specifies to customize the necessary pointer events.
  • handleLongPressStart - Specifies to customize the pointer event when a long-press begins.
  • handleLongPressMoveUpdate - Specifies to customize the pointer event during movement after a long-press.
  • handleLongPressEnd - Specifies to customize the pointer event when the pointer stops contacting the screen after a long-press.
  • handleTapDown - Specifies to customize the pointer event when a tap has contacted the screen once.
  • handleTapUp - Specifies to customize the pointer event when it has stopped contacting the screen after a tap.
  • handleDoubleTap - Specifies to customize the pointer event when a tap has contacted the screen twice.
  • handlePointerEnter -Specifies to customize the pointer event when the mouse enter on the screen.
  • handlePointerExit - Specifies to customize the pointer event when the mouse exit on the screen.

This following code sample defines how to enable or disable specific pointer events and customize to show crosshair in double tap and disabled crosshair showing in pointer enter and exit.

SfCartesianChart(
      crosshairBehavior: CustomCrosshair(),
      // Add series.
    );
    
    class CustomCrosshair extends crosshairBehavior {
      @override
      bool get enable => true;

      // Disable the behavior of pointer events(move, cancel, hover and up).
      @override
      void handleEvent(PointerEvent event, BoxHitTestEntry entry) {}

      // Disable the behavior of the pointer enter on the screen.
      @override
      void handlePointerEnter(PointerEnterEvent details) {}

      // Disable the behavior of the pointer exit on the screen.
      @override
      void handlePointerExit(PointerExitEvent details) {}

      @override
      void handleDoubleTap(Offset position) {
        if (activationMode == ActivationMode.doubleTap) {
          Offset localPosition = parentBox!.globalToLocal(position);
          show(localPosition.dx, localPosition.dy, 'pixel');
        }
      }
    }

DrawVerticalAxisLine method in crosshairBehavior

The drawVerticalAxisLine method is used to customize the stroke drawing and styling of vertical crosshair line.

  • context - Specifies the painting context.
  • offset - Specifies the crosshair position.
  • dashArray - Specifies the crosshair lineDashArray.
  • strokePaint - Specifies the crosshair line stroke paint.
class CustomCrosshair extends CrosshairBehavior {
      @override
      void drawVerticalAxisLine(PaintingContext context, Offset offset,
        List<double>? dashArray, Paint strokePaint) {
        strokePaint
          ..isAntiAlias = true
          ..color = Colors.green
          ..strokeWidth = 1;
        dashArray = [5, 10, 5];
        super.drawVerticalAxisLine(context, offset, dashArray, strokePaint);
      }
    }

DrawHorizontalAxisLine method in crosshairBehavior

The drawHorizontalAxisLine method is used to customize the stroke drawing and styling of horizontal crosshair line.

class CustomCrosshair extends CrosshairBehavior {
      @override
      void drawHorizontalAxisLine(PaintingContext context, Offset offset, List<double>? dashArray, Paint strokePaint) {
        strokePaint
          ..isAntiAlias = true
          ..strokeCap = StrokeCap.round
          ..color = Colors.red
          ..strokeWidth = 2;
        dashArray = [10, 15, 10];
        super.drawHorizontalAxisLine(context, offset, dashArray, strokePaint);
      }
    }

DrawHorizontalAxisTooltip method in crosshairBehavior

The drawHorizontalAxisTooltip method is used to customize the tooltip label, position, and style for horizontal axis tooltip.

  • context - Specifies the painting context.
  • position - Specifies the position of the tooltip label.
  • text - Specifies the tooltip label.
  • style - Specifies the style of the tooltip text.
  • path - Specifies path reference of the tooltip rectangle.
  • fillPaint - Specifies the fill paint applied to the default tooltip rectangle.
  • strokePaint - Specifies the stroke paint applied to the default tooltip rectangle.
class CustomCrosshair extends CrosshairBehavior {
      Offset? customPosition;

      @override
      void show(x, double y, [String coordinateUnit = 'point']) {
        if (coordinateUnit == 'pixel') {
          customPosition = Offset(x.toDouble(), y);
        }
        super.show(x, y, 'pixel');
      }

      @override
      void drawHorizontalAxisTooltip(PaintingContext context, Offset position, String text, TextStyle style, [Path? path, Paint? fillPaint, Paint? strokePaint]) {
        TextStyle style = TextStyle(
        color: Colors.white,
        fontSize: 12,
        background: Paint()
          ..color = Colors.blueGrey
          ..strokeWidth = 16
          ..strokeJoin = StrokeJoin.round
          ..style = dart_ui.PaintingStyle.stroke,
        );

        if (parentBox != null) {
        final Rect bounds = parentBox!.paintBounds;
        final String finalText = 'Y : ${verticalValue.toStringAsFixed(2)}';
        final Offset horizontalPosition =
            Offset(bounds.right, customPosition!.dy).translate(-50, -20);
        _drawText(context.canvas, finalText, horizontalPosition, style);
      }
    }

      void _drawText(Canvas canvas, String text, Offset point, TextStyle style) {
        final TextPainter textPainter = TextPainter(
          text: TextSpan(
            text: text,
            style: style.copyWith(fontWeight: dart_ui.FontWeight.bold)),
        textDirection: dart_ui.TextDirection.ltr,
        textAlign: TextAlign.center,
        maxLines: getMaxLinesContent(text),
        );
        
        textPainter
        ..layout()
        ..paint(canvas, point);
      }
    }

Crosshair Horizonatl Axis Tooltip

DrawVerticalAxisTooltip method in crosshairBehavior

The drawVerticalAxisTooltip method is used to customize the tooltip label, position, and style for vertical axis tooltip.

For further reference, please consult the drawHorizontalAxisTooltip code sample.

Crosshair Vertical Axis Tooltip

onPaint in crosshairBehavior

The onPaint method is used to customize the label, position, and style of crosshair tooltip.

dynamic verticalValue;
    late String horizontalText;

    @override
    void initState() {
      verticalValue = null;
      horizontalText = '';
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
      final List<_SalesData> dateTimeData = [
        _SalesData(DateTime(2015, 2, 0), 31),
        _SalesData(DateTime(2015, 2, 1), 21),
        // Add data points.
      ];
      return Scaffold(
        body: SfCartesianChart(
          crosshairBehavior: CustomCrosshair(),
          onCrosshairPositionChanging: (CrosshairRenderArgs args) {
            if (args.orientation != null) {
              if (args.orientation == AxisOrientation.horizontal) {
                horizontalText = args.text;
              }
              if (args.orientation == AxisOrientation.vertical) {
                verticalValue = args.value;
              }
            }
          },
          series: <CartesianSeries<_SalesData, DateTime>>[
            SplineSeries(
              dataSource: dateTimeData,
              xValueMapper: (_SalesData sales, _) => sales.x,
              yValueMapper: (_SalesData sales, _) => sales.y,
              markerSettings: MarkerSettings(isVisible: true),
            ),
          ],
        ),
      );
    }
    

    class _SalesData {
      _SalesData(this.x, this.y);
      final dynamic x;
      final double y;
    }

    class CustomCrosshair extends CrosshairBehavior {
      @override
      bool get enable => true;

      Offset? customPosition;

      @override
      void show(x, double y, [String coordinateUnit = 'point']) {
        if (coordinateUnit == 'pixel') {
          customPosition = Offset(x.toDouble(), y);
        }
        super.show(x, y, 'pixel');
      }

      @override
      void drawHorizontalAxisLine(PaintingContext context, Offset offset, List<double>? dashArray, Paint strokePaint) {
        strokePaint
          ..isAntiAlias = true
          ..strokeCap = StrokeCap.round
          ..color = Colors.red
          ..strokeWidth = 2;
        dashArray = [10, 15, 10];
        super.drawHorizontalAxisLine(context, offset, dashArray, strokePaint);
      }

      @override
      void drawVerticalAxisLine(PaintingContext context, Offset offset, List<double>? dashArray, Paint strokePaint) {
        strokePaint
          ..isAntiAlias = true
          ..strokeCap = StrokeCap.round
          ..color = Colors.green
          ..strokeWidth = 2;
        dashArray = [5, 10, 5];
        super.drawVerticalAxisLine(context, offset, dashArray, strokePaint);
      }

      @override
      void drawVerticalAxisTooltip(
      PaintingContext context, Offset position, String text, TextStyle style, [Path? path, Paint? fillPaint, Paint? strokePaint]) {
        // Don't invoke default tooltip.
      }

      @override
      void drawHorizontalAxisTooltip(
      PaintingContext context, Offset position, String text, TextStyle style, [Path? path, Paint? fillPaint, Paint? strokePaint]) {
        // Don't invoke default tooltip.
      }

      @override
      void onPaint(PaintingContext context, Offset offset, SfChartThemeData chartThemeData, ThemeData themeData) {
        super.onPaint(context, offset, chartThemeData, themeData);
        // Custom tooltip.
        TextStyle style = TextStyle(
          color: Colors.white,
          fontSize: 12,
          background: Paint()
            ..color = Colors.blueGrey
            ..strokeWidth = 16
            ..strokeJoin = StrokeJoin.round
            ..style = dart_ui.PaintingStyle.stroke,
        );

        if (customPosition != null && horizontalText != '' && verticalValue != null) {
          final String finalText = 'X : ${horizontalText}  Y : ${verticalValue.toStringAsFixed(2)}';
          _drawText( context.canvas, finalText, customPosition!.translate(20, 20), style);
        }
      }

      void _drawText(Canvas canvas, String text, Offset point, TextStyle style) {
        final TextPainter textPainter = TextPainter(
          text: TextSpan(
            text: text,
            style: style.copyWith(fontWeight: dart_ui.FontWeight.bold)),
          textAlign: TextAlign.center,
          textDirection: dart_ui.TextDirection.ltr,
        );
        textPainter
          ..layout()
          ..paint(canvas, point);
      }

      @override
      void hide() {
        customPosition = null;
        horizontalText = '';
        verticalValue = null;
        super.hide();
      }
    }

Customized crosshair

Methods in selectionBehavior

SelectDataPoints method in selectionBehavior

The selectDataPoints method is used to select the data point programmatically. The required arguments are listed below.

  • pointIndex - index of the point which needs to be selected.
  • seriesIndex - index of the series for which the pointIndex is specified and this is an optional argument. By default it will be considered as 0.

Note: The enableMultiSelection and selectionType are also applicable for this but, it is based on their API values specified in the chart.

late SfCartesianChart chart;
    late SelectionBehavior _selectionBehavior;

    @override
    void initState(){
      _selectionBehavior = SelectionBehavior(enable: true);
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34)
        // Add the required data
      ];

      chart = SfCartesianChart(
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y,
              selectionBehavior: _selectionBehavior
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Select'),
                onPressed: select
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void select() {
        _selectionBehavior.selectDataPoints(1, 0);
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Methods in zoomPanBehavior

ZoomIn method in zoomPanBehavior

The zoomIn method is used to increase the magnification of the plot area.

late SfCartesianChart chart;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34)
        //add the required data
      ];
      
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Zoom'),
                onPressed: zoom
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void zoom() {
        _zoomPanBehavior.zoomIn();
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

ZoomOut method in zoomPanBehavior

The zoomOut method is used to decrease the magnification of the plot area.

late SfCartesianChart chart;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34)
        //add the required data
      ];
      
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Zoom'),
                onPressed: zoom
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void zoom() {
        _zoomPanBehavior.zoomOut();
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

zoomByFactor method in zoomPanBehavior

The zoomByFactor method changes the zoom level using zoom factor. Here, you can pass the zoom factor of an axis to magnify the plot area. The value ranges from 0 to 1.

late SfCartesianChart chart;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
        //add the required data
      ];
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Zoom'),
                onPressed: zoom
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void zoom() {
        _zoomPanBehavior.zoomByFactor(0.5);
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

ZoomByRect method in zoomPanBehavior

The zoomByRect method zooms the chart for a given rectangle value. Here, you can pass the rectangle with the left, right, top, and bottom values, using which the selection zooming will be performed.

late SfCartesianChart chart;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
        //add the required data
      ];
      
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Zoom'),
                onPressed: zoom
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void zoom() {
        _zoomPanBehavior.zoomByRect(const Rect.fromLTRB(200, 300, 300, 400));
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

ZoomToSingleAxis method in zoomPanBehavior

The zoomToSingleAxis method changes the zoom level of an appropriate axis. Here, you need to pass axis, zoom factor, zoom position of the zoom level that needs to be modified.

late SfCartesianChart chart;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    late NumericAxis xAxis;

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
        //add required data
      ];
      xAxis = NumericAxis();
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        primaryXAxis: xAxis,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Zoom'),
                onPressed: zoom
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void zoom() {
        final double zoomPosition = 0.5;
        final double zoomFactor = 0.4;
        _zoomPanBehavior.zoomToSingleAxis(xAxis,zoomPosition,zoomFactor);
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

PanToDirection method in zoomPanBehavior

The panToDirection method pans the plot area for given left, right, top, and bottom directions. To perform this action, the plot area needs to be in zoomed state.

late SfCartesianChart chart;
    late NumericAxis xAxis;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34),
        //add required data
      ];

      xAxis = NumericAxis();
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        primaryXAxis: xAxis,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Pan'),
                onPressed: pan
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void pan() {
        //In similar way, specify other directions like bottom, left and right
        _zoomPanBehavior.panToDirection('top'); 
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Reset method in zoomPanBehavior

The reset method returns the plot area back to its original position after zooming..

late SfCartesianChart chart;
    late ZoomPanBehavior _zoomPanBehavior;

    @override
    void initState(){
      _zoomPanBehavior =  ZoomPanBehavior(
        enableSelectionZooming: true,
        enableDoubleTapZooming: true,
        enablePinching: true,
        enablePanning: true
      );
      super.initState();
    }

    @override
    Widget build(BuildContext context) {
    
      final List<ChartData> chartData = [
        ChartData(10, 17),
        ChartData(20, 34)
        //add the required data
      ];
      
      chart = SfCartesianChart(
        zoomPanBehavior: _zoomPanBehavior,
        series: <CartesianSeries>[
          ColumnSeries<ChartData, double>(
            dataSource: chartData,
            xValueMapper: (ChartData data, _) => data.x,
            yValueMapper: (ChartData data, _) => data.y
          )
        ]
      );
      
      return Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
              TextButton(
                child: Text('Zoom'),
                onPressed: zoom
              ),
              Container(child: chart)
            ]
          )
        )
      );
    }

    void zoom() {
      _zoomPanBehavior.reset();
    }

     class ChartData {
        ChartData(this.x, this.y);
        final double x;
        final double? y;
      }

Events in zoomPanBehavior

Provided the following methods for handling pointer and gesture events and allowing customizations for various pointer events such as double-tap, scale and long-press.

  • handleEvent - Specifies to customize the necessary pointer events.
  • handleDoubleTap - Specifies to customize the pointer event when a tap has contacted the screen twice.
  • handleScaleStart - Specifies to customize the pointer event when the pointers begins to move.
  • handleScaleUpdate - Specifies to customize the pointer event when the pointers in the moving state.
  • handleScaleEnd - Specifies to customize the pointer event when the pointers stops moving and contacting on the screen.
  • handleLongPressStart - Specifies to customize the pointer event when a long-press begins.
  • handleLongPressMoveUpdate - Specifies to customize the pointer event during movement after a long-press.
  • handleLongPressEnd - Specifies to customize the pointer event when the pointer stops contacting the screen after a long-press.

This following code sample defines how to perform zoom in and out behavior in double tap zooming.

CategoryAxis xAxis = CategoryAxis();
    NumericAxis yAxis = NumericAxis();

    SfCartesianChart(
      primaryXAxis: xAxis,
      primaryYAxis: yAxis,
      zoomPanBehavior: CustomDoubleTapZoomPanBehavior(xAxis, yAxis),
      // Add series here.
      series: series: [
        LineSeries(),
      ]
    );

    class CustomDoubleTapZoomPanBehavior extends ZoomPanBehavior {
      CustomDoubleTapZoomPanBehavior(this.xAxis, this.yAxis);

      CategoryAxis xAxis;
      NumericAxis yAxis;

      @override
      bool get enableDoubleTapZooming => true;

      bool _isZoomIn = true;

      @override
      void handleDoubleTap(Offset position) {
        if (parentBox == null && !enableDoubleTapZooming) {
          return;
        }
        const double origin = 0.5;
        final double cumulativeZoomLevel = _isZoomIn ? 1.4 : 1.0;
        double zoomFactor = 1 / cumulativeZoomLevel;
        double zoomPosition = (1 - zoomFactor) * origin;

        _isZoomIn = !_isZoomIn;
        zoomToSingleAxis(xAxis, zoomPosition, zoomFactor);
        zoomToSingleAxis(yAxis, zoomPosition, zoomFactor);
      }
    }

Customized double tap

onPaint in ZoomPanBehavior

To customize the tooltip label, position, and selection rect of the zooming.

This following code sample defines how to perform selection zooming and customize the selection zooming rect in single tap using scale methods.

SfCartesianChart(
      zoomPanBehavior: CustomScaleZoomPanBehavior(),
      // Add series here.
      series: series: [
        LineSeries(),
      ]
    );

    class CustomScaleZoomPanBehavior extends ZoomPanBehavior {
      @override
      bool get enableSelectionZooming => true;

      RRect? _customRect;
      Rect? _zoomRect;
      Offset? _customRectStartPosition;
      Offset? _textPosition;

      @override
      void handleScaleStart(ScaleStartDetails details) {
        _customRectStartPosition = parentBox!.globalToLocal(details.focalPoint);
      }

      @override
      void handleScaleUpdate(ScaleUpdateDetails details) {
        if (details.pointerCount != 1 || !enableSelectionZooming) {
          return;
        }
        _calculateCustomRect(parentBox!.globalToLocal(details.focalPoint));
        parentBox!.markNeedsPaint();
      }

      @override
      void handleScaleEnd(ScaleEndDetails details) {
        _customRectStartPosition = null;
        _textPosition = null;
        zoomByRect(_zoomRect!);
        _customRect = RRect.zero;
        _zoomRect = Rect.zero;
        parentBox?.markNeedsPaint();
      }

      // Disable the long Press selection zooming behavior
      @override
      void handleLongPressStart(LongPressStartDetails details) {}

      // Disable the long Press selection zooming behavior
      @override
      void handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) {}

      // Disable the long Press selection zooming behavior
      @override
      void handleLongPressEnd(LongPressEndDetails details) {}

      @override
      void onPaint(PaintingContext context, Offset offset,
          SfChartThemeData chartThemeData, ThemeData themeData) {
        if (_zoomRect == null || _textPosition == null) {
          return;
        }
        _drawCustomRect(context, chartThemeData, themeData);
        _drawText(context.canvas);
      }

      void _calculateCustomRect(Offset position) {
        if (parentBox == null) {
          return;
        }
        if (_customRectStartPosition != null) {
          final Rect clipRect = parentBox!.paintBounds;
          final Offset startPosition = Offset(
            max(_customRectStartPosition!.dx, clipRect.left),
            max(_customRectStartPosition!.dy, clipRect.top),
          );
          Offset currentPosition = position;
          final double currentX =
              _minMax(currentPosition.dx, clipRect.left, clipRect.right);
          final double currentY =
              _minMax(currentPosition.dy, clipRect.top, clipRect.bottom);
          currentPosition = Offset(currentX, currentY);
          double left = startPosition.dx > currentPosition.dx
              ? currentPosition.dx
              : startPosition.dx;
          double top = startPosition.dy > currentPosition.dy
              ? currentPosition.dy
              : startPosition.dy;
          double right = startPosition.dx < currentPosition.dx
              ? currentPosition.dx
              : startPosition.dx;
          double bottom = startPosition.dy < currentPosition.dy
              ? currentPosition.dy
              : startPosition.dy;
          _zoomRect = Rect.fromLTRB(left, top, right, bottom);
          _customRect = RRect.fromRectAndRadius(_zoomRect!, Radius.circular(20));
          _textPosition = currentPosition;
        }
      }

      void _drawCustomRect(PaintingContext context, SfChartThemeData chartThemeData,
          ThemeData themeData) {
        final Paint customRectPaint = Paint()..color = Colors.teal.withOpacity(0.1);
        final Paint customStrokeRectPaint = Paint()
          ..color = Colors.teal.withOpacity(0.7)
          ..style = PaintingStyle.stroke
          ..strokeWidth = 2;
        context.canvas.drawRRect(_customRect!, customRectPaint);
        context.canvas.drawRRect(_customRect!, customStrokeRectPaint);
      }

      double _minMax(double value, double min, double max) {
        return value > max ? max : (value < min ? min : value);
      }

      void _drawText(Canvas canvas) {
        String left = _zoomRect!.left.toStringAsFixed(2);
        String top = _zoomRect!.top.toStringAsFixed(2);
        String right = _zoomRect!.right.toStringAsFixed(2);
        String bottom = _zoomRect!.bottom.toStringAsFixed(2);
        String text =
            'left: $left \n top: $top \n right: $right \n bottom: $bottom';
        final TextSpan span = TextSpan(
            text: text, style: TextStyle(fontSize: 10, color: Colors.black));
        final TextPainter textPainter = TextPainter(
          text: span,
          textAlign: TextAlign.center,
          textDirection: TextDirection.ltr,
        );
        textPainter.layout();
        if (_zoomRect!.width < textPainter.width ||
            _zoomRect!.height < textPainter.height) {
          return;
        }
        double leftX = 0;
        double topY = 0;
        if (_zoomRect!.left == _textPosition!.dx) {
          leftX = 80;
        } else {
          leftX = -10;
        }
        if (_zoomRect!.top == _textPosition!.dy) {
          topY = 60;
        } else {
          topY = -10;
        }
        canvas.save();
        canvas.translate(_textPosition!.dx, _textPosition!.dy);
        final Offset labelOffset =
            Offset(-textPainter.width + leftX, -textPainter.height + topY);
        textPainter.paint(canvas, labelOffset);
        canvas.restore();
      }
    }

Customized selection zooming

UpdateDataSource

Used to process only the newly added, updated, and removed data points in a series, instead of processing all the data points.

To re-render the chart with modified data points, setState() will be called. This will render the chart from scratch and thus, the app’s performance will be degraded on continuous updates.

To overcome this problem, the updateDataSource method can be called by passing updated data points indexes. The chart widget will process only that point and skip various steps like bounds calculation,
old data points processing, etc. Thus, this will improve the app’s performance.

The following are the arguments of this method.

  • addedDataIndexes - List<int> type - Indexes of newly added data points in the existing series.
  • removedDataIndexes - List<int> type - Indexes of removed data points in the existing series.
  • updatedDataIndexes - List<int> type - Indexes of updated data points in the existing series.
  • addedDataIndex - int type - Index of newly added data point in the existing series.
  • removedDataIndex - int type - Index of removed data point in the existing series.
  • updatedDataIndex - int type - Index of updated data point in the existing series.
Widget build(BuildContext context) {

    //Initialize the series controller
    ChartSeriesController? _chartSeriesController;

    @override
    Widget build(BuildContext context) {
      return Column(
        children: <Widget>[
          Container(
          child: SfCartesianChart(
                series: <LineSeries<ChartData, num>>[
                    LineSeries<ChartData, num>(
                      dataSource: chartData,
                      //Initialize the onRendererCreated event and store the controller for the respective series
                      onRendererCreated: (ChartSeriesController controller) {
                          _chartSeriesController = controller;
                      },
                    ),
                  ],
            )
          ),
          Container(
            child: ElevatedButton(
              onPressed: () {
                //Removed a point from data source
                chartData.removeAt(0);
                //Added a point to the data source
                chartData.add(ChartData(3,23));
                //Passed the necessary arguments to the updateDataSource method. Here passed the added and removed data point indexes.
                _chartSeriesController?.updateDataSource(
                  addedDataIndexes: <int>[chartData.length - 1],
                  removedDataIndexes: <int>[0],
                );
              }, child: Text('Add a point'),
            )
          )
        ]
      );
    }
  }

     class ChartData {
        ChartData(this.x, this.y);
        final num x;
        final double? y;
      }

See Also

PixelToPoint

Converts logical pixel value to the data point value.

The pixelToPoint method takes logical pixel value as input and returns a chart data point.

Since this method is in the series controller, x and y-axis associated with this particular series will be considering for conversion value.

//Initialize the series controller
    ChartSeriesController? seriesController;

    @override
    Widget build(BuildContext context) {
      return Container(
            child: SfCartesianChart(
             series: <CartesianSeries<ChartSampleData, num>>[
               LineSeries<ChartSampleData, num>(
                 onRendererCreated: (ChartSeriesController controller) {
                   seriesController = controller;
                 },
               )
             ],
             onChartTouchInteractionUp: (ChartTouchInteractionArgs args) {
               final Offset value = Offset(args.position.dx, args.position.dy);
               CartesianChartPoint<dynamic> chartpoint =
                 seriesController!.pixelToPoint(value);
               print('X point: ${chartpoint.x}');
               print('Y point: ${chartpoint.y}');
           }
         )
       );
    }

     class ChartSampleData{
        ChartSampleData(this.x, this.y);
        final num x;
        final double? y;
      }

PointToPixel

Converts chart data point value to logical pixel value.

The pointToPixel method takes chart data point value as input and returns logical pixel value.

Since this method is in the series controller, x and y-axis associated with this particular series will be considering for conversion value.

Note: This method is only applicable for Cartesian chart, not for the circular, pyramid,
and funnel charts.

//Initialize the series controller
    ChartSeriesController? seriesController;

    @override 
    Widget build(BuildContext context) {
      return Container(
          child: SfCartesianChart(
              series: <CartesianSeries<ChartSampleData, num>>[
                ColumnSeries<ChartSampleData, num>(
                  onRendererCreated: (ChartSeriesController controller) {
                    seriesController = controller;
                  },
                )
              ],
              onPointTapped: (PointTapArgs args) {
                  CartesianChartPoint<dynamic> chartPoint =
                      CartesianChartPoint<dynamic>(data[args.pointIndex].x,
                          data[args.pointIndex].y);
                  Offset pointLocation = seriesController!.pointToPixel(chartPoint);
                  print('X location: ${pointLocation.x}');
                  print('Y location: ${pointLocation.y}');
              },
            )
      );
    }

    class ChartSampleData{
        ChartSampleData(this.x, this.y);
        final num x;
        final double? y;
      }

See Also

Note: chartData in the above code snippets is a class type list and holds the data for binding to the chart series. Refer Bind data source topic for more details.