How to synchronize trackball in multiple Flutter SfCartesianChart ?
In this article, we described how to synchronize the trackball in multiple charts.
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
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 forums, Direct-Trac, or feedback portal. We are always happy to assist you!