Customizable TrackballBehavior-Tooltip, Custom builder, accessibility of data and viewport

Hey SyncFusion team,


I came across the issue that the Trackbar-Tooltip in the SfCartesianChart does not show, when the tooltip is wider than the chart width.

We are using

tooltipDisplayMode: TrackballDisplayMode.groupAllPoints

, so the popup is generally a bit wider with our data label text being a bit longer and also in that popup-style the date being included.

Is there a way to scale down the popup if it is too wide, or truncate text? We definitely want the popup to be shown.

---

I also tried to use the builder attribute within the TrackballBehavior to fix the width issue outselves, but with using it, there is an error introduced: when interacting with the chart (scrubbing over it to view data points, pinching for zooming), it will jump in a weird way and not show the built widget anymore. this does not happen when not providing the builder attribute.

I also looked through the last example by Sriram in this forum post: https://www.syncfusion.com/forums/174380/trackball-behavior-builder-widget-with-a-long-width-would-displays-wrong-in-some-points?reply=SfkQL5 for building the widget and updating the available size and so on, but the jumping always appeared.

---

this is roughly the configuration we are using:

return SfCartesianChart(
key: widget.key,
zoomPanBehavior: _customZoomPanBehavior,
margin: EdgeInsets.zero,
onZoomStart: _updateZoomValues,
onZoomEnd: _updateZoomValues,
trackballBehavior: CustomTrackBallBehavior(
enable: true,
tooltipDisplayMode: TrackballDisplayMode.groupAllPoints,
activationMode: ActivationMode.singleTap,
hideDelay: 2000,
// activating this will cause the hideDelay to be ignored
// shouldAlwaysShow: true,
// builder: buildTooltip,
tooltipSettings: getCustomTooltipSettings(Theme.of(context).brightness)),
//onActualRangeChanged: (args) {
// if (args.axisName == 'primaryXAxis') {
// // Retrieved the visible minimum and maximum value of x-axis
// minX = args.visibleMin.toDouble();
// maxX = args.visibleMax.toDouble();
// } else {
// // Retrieved the visible minimum value of y-axis
// minY = args.visibleMin.toDouble();
// }
// print("minX: ${minX}");
// print("maxX: ${maxX}");
// print("minY: ${minY}");
//},
legend: widget.legendVisible
? const Legend(
isVisible: true, opacity: 0.7, position: LegendPosition.bottom)
: const Legend() /*null*/,
onTrackballPositionChanging: (trackballArgs) {
final ts = trackballArgs.chartPointInfo.chartPoint?.xValue;
if (ts is num) {
final dateTime = DateTime.fromMillisecondsSinceEpoch(ts.toInt());
trackballArgs.chartPointInfo.header = dateTimeFormat.format(dateTime);
}
},
plotAreaBorderWidth: 0,
primaryXAxis: widget.xAxis ??
DateTimeAxis(
labelStyle: Theme.of(context).customTheme?.listTileSubtitle),
primaryYAxis: NumericAxis(
labelFormat: '{value}${translate("unit.$unitKey")}',
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
labelStyle: Theme.of(context).customTheme?.listTileSubtitle,
),
axes: (widget.graph.seriesList.length > 1)
? [
NumericAxis(
name: "secondary",
opposedPosition: true,
labelFormat: '{value}${translate("unit.$unitKey")}',
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0))
]
: [],
series: widget.series,
);
class _customZoomPanBehavior extends ZoomPanBehavior {
@override
bool get enableMouseWheelZooming => false;

@override
bool get enablePanning => true;

@override
bool get enableDoubleTapZooming => true;

@override
bool get enablePinching => true;

@override
bool get enableSelectionZooming => false;

@override
ZoomMode get zoomMode => ZoomMode.x;

double get zoomFactor => _zoomFactor;
double _zoomFactor = 1.0;

set zoomFactor(double value) {
if (_zoomFactor != value) {
_zoomFactor = value;
}
}

double get zoomPosition => _zoomPosition;
double _zoomPosition = 0.0;

set zoomPosition(double value) {
if (_zoomPosition != value) {
_zoomPosition = value;
}
}


// this is done to provide the tooltip when zoomed out, but also provide pinch to zoom.
@override
void handleHorizontalDragUpdate(DragUpdateDetails details) {
if (doesFloatEqual(zoomFactor, 1) && doesFloatEqual(zoomPosition, 0)) {
return;
} else {
super.handleHorizontalDragUpdate(details);
}
}

@override
void handleVerticalDragUpdate(DragUpdateDetails details) {
if (doesFloatEqual(zoomFactor, 1) && doesFloatEqual(zoomPosition, 0)) {
return;
} else {
super.handleVerticalDragUpdate(details);
}
}
}
void _updateZoomValues(ZoomPanArgs args) {
setState(() {
if (args.axis != null &&
args.axis!.name?.toLowerCase().contains('xaxis') == true) {
zoomFactor = args.currentZoomFactor;
zoomPosition = args.currentZoomPosition;
_customZoomPanBehavior.zoomFactor = zoomFactor;
_customZoomPanBehavior.zoomPosition = zoomPosition;
}
});
}
// fixes hide issue, by prevents hide
class CustomTrackBallBehavior extends TrackballBehavior {
CustomTrackBallBehavior({
super.activationMode = ActivationMode.longPress,
super.lineType = TrackballLineType.vertical,
super.tooltipDisplayMode = TrackballDisplayMode.floatAllPoints,
super.tooltipAlignment = ChartAlignment.center,
super.tooltipSettings = const InteractiveTooltip(),
super.markerSettings,
super.lineDashArray,
super.enable = false,
super.lineColor,
super.lineWidth = 1,
super.shouldAlwaysShow = false,
super.builder,
super.hideDelay = 0,
});
}
InteractiveTooltipgetCustomTooltipSettings(Brightness theme){
if(theme ==Brightness.light){
returnInteractiveTooltip(
canShowMarker:false,
enable:true,
color:Colors.white,
borderColor:Colors.black12,
borderWidth:1,
textStyle:TextStyle(
color:Colors.black87,
fontFamily:Font.manrope,
fontSize:15),
borderRadius:15);
}
returnInteractiveTooltip(
canShowMarker:false,
color:Color.lerp(Colors.grey.shade800,Colors.blueGrey,0.3),
borderColor:Colors.transparent,
borderWidth:0,
textStyle:TextStyle(
color:Colors.white.withAlpha(255),
fontFamily:Font.manrope,
fontSize:15),
borderRadius:15);
}

Greetings,
Raphael


3 Replies

AS Aswini Suresh Reddy Syncfusion Team March 23, 2026 02:00 PM UTC

Hi Raphael Betz,


We have analyzed your requirement and created a workaround sample to achieve it. We used the builder property in TrackballBehavior to create a custom tooltip. The tooltip width is limited using ConstrainedBox so it never overflows the chart. The series name will shrink and show ellipsis(…) when the chart is too narrow, while the value always stays fully visible. The date header will also wrap or truncate based on the available width. We have attached the sample below for your reference.


Regarding the tooltip jumping issue when scrubbing or pinch-zooming with the builder attribute, we were unable to reproduce this issue in our sample. Could you please share a screen recording or screenshots of the jumping behavior. We kindly request you to try to replicate the reported issue in the below attached test sample and revert us so that it will help us assist you in a better way. 


Please check and get back to us if you need further help.


Regards,

Aswini S.


Attachment: fr_198205_e3eae339.zip


WC William Corlin April 3, 2026 03:46 AM UTC

Hi, I’m running into something very similar and wanted to ask a follow-up.

In your case, when the tooltip gets wider than the chart and you try using a custom builder, do you also see inconsistent positioning during zoom/pan (like it “loses” the correct anchor point)?

I was testing based on this thread as well:
https://www.syncfusion.com/forums/174380/trackball-behavior-builder-widget-with-a-long-width-would-displays-wrong-in-some-points?reply=SfkQL5hot games

and it seems like once the width exceeds certain bounds, the layout/viewport calculations might not stay in sync with the trackball position.

Do you know if there’s any recommended way to keep the tooltip constrained within the visible viewport (like clamping or auto-resizing) without breaking the interaction behavior?



AS Aswini Suresh Reddy Syncfusion Team April 9, 2026 11:16 AM UTC

Hi William Corlin,

 

After further validation, we have confirmed the current behavior, when the tooltip width is greater than the chart’s plot area or axis bounds, the tooltip will not be rendered. This is the expected behavior, as tooltips are designed to render only when they fit within the plot area or axis bounds.

 

From your description, it appears that the tooltip is not visible because its width exceeds the plot area or axis bounds of the chart. If this is the exact scenario you are referring to, then this behavior works as designed.

 

However, if you are encountering a different case, such as the tooltip not rendering even when it partially overlaps or reasonably fits within the plot area, please share additional details along with a sample demo or screenshot. This will help us better understand the scenario and verify whether there is any deviation from the intended behavior.

 

Kindly review the attached demo for reference and get back to us with more information so we can clearly understand your requirement and assist you further better.

 

Regards,
Aswini S.


Attachment: Demo_44be1518.zip

Loader.
Up arrow icon