Articles in this section
Category / Section

How to synchronize trackball in multiple Flutter SfCartesianChart ?

13 mins read

In this article, we described how to synchronize the trackball in multiple charts. 

In Flutter Cartesian Chart widget, you can synchronize the trackball of multiple charts using the available callback events and public methods. A callback event is a callback function or method, which you can pass as an argument to another function or method. It can perform an action when you require it, and public methods are methods that can be called by using the class object where they are defined. The callback event used for synchronizing trackball is the onTrackballPositionChanging and the show public method of the trackball is used for activating the trackball in multiple charts in a synchronized fashion on user interaction.

The following steps explains how to synchronize the trackball in multiple charts

 

Step 1: Define two TrackballBehavior variables with the required properties globally for the first and second charts respectively to use the public methods of trackball.

// Trackball behavior for first chart.
late TrackballBehavior trackball1; 
// Trackball behavior for second chart.
late TrackballBehavior trackball2;
 
@override
void initState(){
  trackball =
    TrackballBehavior(enable: true, activationMode: ActivationMode.singleTap);
trackball2 =
    TrackballBehavior(enable: true, activationMode: ActivationMode.singleTap);
super.initState();
}

Step 2: Declare two ChartSeriesController variables to get the series details when tapping on the chart and two position variables to get the tapped position of the chart.

// Initialize the variable to get the series details for the first chart.
ChartSeriesController? _firstChartController;
// Initialize the variable to get the series details for the second chart.
ChartSeriesController? _secondChartController;

// Initialize the variable to get the tapped position for the first chart.
Offset? _firstPosition;
// Initialize the variable to get the tapped position for the second chart.
Offset? _secondPosition;

Step 3: Initialize a Cartesian chart (First chart) as an individual StatefulWidget with all the necessary properties and also set the globally defined trackball behavior variable (trackBall1) of the first chart in the trackballBehavior property available in the chart.

class FirstChart extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return FirstChartState();
  }
}
 
class FirstChartState extends State<FirstChart{
  @override
  Widget build(BuildContext context) {
    return SfCartesianChart(
        primaryXAxis: CategoryAxis(),
        title: ChartTitle(text: 'Chart 1'),
        trackballBehavior: trackBall1, // Trackball behavior variable of the first chart.
        series: <LineSeries<SalesData, String>>[
          LineSeries<SalesData, String>(
              dataSource: chartData,
              xValueMapper: (SalesData sales, _) => sales.year,
              yValueMapper: (SalesData sales, _) => sales.sales)
        ],     );
  }
}

Step 4: Initialize another Cartesian chart (Second chart) as an individual StatefulWidget with all the necessary properties and also set the globally defined trackball behavior variable (trackBall2) of the second chart in the trackballBehavior property available in the chart.

class SecondChart extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return SecondChartState();
  }
}
 
class SecondChartState extends State<SecondChart> {
  @override
  Widget build(BuildContext context) {
    return SfCartesianChart(
        primaryXAxis: CategoryAxis(),
        title: ChartTitle(text: 'Chart 2'),
        trackballBehavior: trackball2, // Trackball behavior variable of the second chart.
        series: <LineSeries<SalesData, String>>[
          LineSeries<SalesData, String>(
              dataSource: chartData,
              xValueMapper: (SalesData sales, _) => sales.year,
              yValueMapper: (SalesData sales, _) => sales.sales)
        ],      );
  }
}

Step 5: The synchronization of trackball can be done with the help of the onChartTouchInteractionDown, onChartTouchInteractionUp, onTrackballPositionChanging callback events and the trackball’s show public method in the chart.

In the first chart, with the help of the pixel position arguments are obtained from the onTrackballPositionChanging event on user interaction, call the trackball show method for the second chart with the obtained pixel position arguments (dx and dy) as the parameters.

class FirstChartState extends State<FirstChart> {
  // Declare variable to avoid calling of the second chart's trackball.
  bool _isInteractive = false;

  @override
  Widget build(BuildContext context) {
    return SfCartesianChart(
      onChartTouchInteractionDown: (ChartTouchInteractionArgs tapArgs) {
      // Set to true when touched on the chart.
        _isInteractive = true;
      },
      onChartTouchInteractionUp: (ChartTouchInteractionArgs tapArgs) {
        // Set to false when touched out of the chart.
        _isInteractive = false;
        // Hide the trackball when touched out of the chart.
        trackBall2.hide();
      },
      onTrackballPositionChanging: (TrackballArgs trackballArgs) {
        if (_isInteractive) {
          _secondPosition = _secondChartController!.pointToPixel(
            trackballArgs.chartPointInfo.chartPoint!,
          );
         // Called the second chart’s trackball show method with pixel position arguments as parameters.
          trackBall2.show(_secondPosition!.dx, _secondPosition!.dy, 'pixel');
        }
      },
      backgroundColor: Colors.white,
      primaryXAxis: const CategoryAxis(),
      title: const ChartTitle(text: 'Chart 1'),
      trackballBehavior: trackBall1,  // Trackball behavior variable of the first chart.
      series: <LineSeries<SalesData, String>>[
        LineSeries<SalesData, String>(
          dataSource: <SalesData>[
            SalesData('Jan', 21),
            SalesData('Feb', 24),
            SalesData('Mar', 35),
            SalesData('Apr', 38),
            SalesData('May', 54),
            SalesData('Jun', 21),
            SalesData('Jul', 24),
            SalesData('Aug', 35),
            SalesData('Sep', 38),
            SalesData('Oct', 54),
            SalesData('Nov', 38),
            SalesData('Dec', 54)
          ],
          xValueMapper: (SalesData sales, _) => sales.year,
          yValueMapper: (SalesData sales, _) => sales.sales,
          onRendererCreated: (ChartSeriesController controller) {
            _firstChartController = controller; // Get the series details for the first chart.
          },
        ),
      ],
    );
  }
}

Similarly, in the second chart, call the trackball show method for the first chart with the obtained pixel position arguments (dx and dy) from the onTrackballPositionChanging as the parameters.

class SecondChartState extends State<SecondChart> {
  // Declare variable to avoid calling of the first chart's trackball.
  bool _isInteractive = false;

  @override
  Widget build(BuildContext context) {
    return SfCartesianChart(
      onChartTouchInteractionDown: (ChartTouchInteractionArgs tapArgs) {
        // Set to true when touched on the chart.
        _isInteractive = true;
      },
      onChartTouchInteractionUp: (ChartTouchInteractionArgs tapArgs) {
         // Set to false when touched out of the chart.
        _isInteractive = false;
        // Hide the trackball when touched out of the chart.
        trackBall1.hide();
      },
      onTrackballPositionChanging: (TrackballArgs trackballArgs) {
        if (_isInteractive) {
          _firstPosition = _firstChartController!.pointToPixel(
            trackballArgs.chartPointInfo.chartPoint!,
          );
          // Called the first chart’s trackball show method with pixel position arguments as parameters.
          trackBall1.show(_firstPosition!.dx, _firstPosition!.dy, 'pixel');
        }
      },
      backgroundColor: Colors.white,
      primaryXAxis: const CategoryAxis(),
      title: const ChartTitle(text: 'Chart 2'),
      trackballBehavior: trackBall2,  // Trackball behavior variable of the second chart.
      series: <LineSeries<SalesData, String>>[
        LineSeries<SalesData, String>(
          dataSource: <SalesData>[
            SalesData('Jan', 21),
            SalesData('Feb', 24),
            SalesData('Mar', 35),
            SalesData('Apr', 38),
            SalesData('May', 54),
            SalesData('Jun', 21),
            SalesData('Jul', 24),
            SalesData('Aug', 35),
            SalesData('Sep', 38),
            SalesData('Oct', 54),
            SalesData('Nov', 38),
            SalesData('Dec', 54)
          ],
          xValueMapper: (SalesData sales, _) => sales.year,
          yValueMapper: (SalesData sales, _) => sales.sales,
          onRendererCreated: (ChartSeriesController controller) {
            _secondChartController = controller; // Get the series details for the second chart.
          },
        ),
      ],
    );
  }
}

Thus, the trackballs can be synchronized in the multiple charts.

 

Screenshot

View the sample in GitHub.

 

Conclusion

I hope you enjoyed learning about how to synchronize trackball in multiple charts (SfCartesianChart).

You can refer to our  Flutter CartesianChart feature tour page to know about its other groundbreaking feature representations and Flutter CartesianChart documentation, and how to quickly get started for configuration specifications. You can also explore our Flutter CartesianChart example to understand how to create and manipulate data.

For current customers, you can check out our components from the License and Downloads page. If you are new to Syncfusion, you can try our 30-day free trial to check out our other controls.

If you have any queries or require clarifications, please let us know in the comments section below. You can also contact us through our support forumsDirect-Trac, or feedback portal. We are always happy to assist you!

Did you find this information helpful?
Yes
No
Help us improve this page
Please provide feedback or comments
Comments
Please  to leave a comment
Access denied
Access denied