fl_chart散点图长按Tooltip闪显消失及选中点不显示问题
问题:fl_chart散点图Tooltip异常问题
我基于GitHub上的fl_chart散点图示例代码修改,仅将handleBuiltInTouches参数设为true,期望长按点位弹出Tooltip,但Tooltip仅闪显瞬间就消失;同时设置该参数后,即便showingTooltipIndicators已选中所有点位,也没有任何点位弹出Tooltip。相关代码如下:
class ScatterChartSample2 extends StatefulWidget { const ScatterChartSample2({Key? key}) : super(key: key); @override State<StatefulWidget> createState() => _ScatterChartSample2State(); } class _ScatterChartSample2State extends State { int touchedIndex = -1; Color greyColor = Colors.grey; List<int> selectedSpots = []; @override Widget build(BuildContext context) { return AspectRatio( aspectRatio: 1, child: Card( color: const Color(0xff222222), child: fl.ScatterChart( fl.ScatterChartData( scatterSpots: [ fl.ScatterSpot( 4, 4, color: selectedSpots.contains(0) ? Colors.green : greyColor, ), fl.ScatterSpot( 2, 5, color: selectedSpots.contains(1) ? Colors.yellow : greyColor, radius: 12, ), fl.ScatterSpot( 4, 5, color: selectedSpots.contains(2) ? Colors.purpleAccent : greyColor, radius: 8, ), fl.ScatterSpot( 8, 6, color: selectedSpots.contains(3) ? Colors.orange : greyColor, radius: 20, ), fl.ScatterSpot( 5, 7, color: selectedSpots.contains(4) ? Colors.brown : greyColor, radius: 14, ), fl.ScatterSpot( 7, 2, color: selectedSpots.contains(5) ? Colors.lightGreenAccent : greyColor, radius: 18, ), fl.ScatterSpot( 3, 2, color: selectedSpots.contains(6) ? Colors.red : greyColor, radius: 36, ), fl.ScatterSpot( 2, 8, color: selectedSpots.contains(7) ? Colors.tealAccent : greyColor, radius: 22, ), ], minX: 0, maxX: 10, minY: 0, maxY: 10, borderData: fl.FlBorderData( show: false, ), gridData: fl.FlGridData( show: true, drawHorizontalLine: true, checkToShowHorizontalLine: (value) => true, getDrawingHorizontalLine: (value) => fl.FlLine(color: Colors.white.withOpacity(0.1)), drawVerticalLine: true, checkToShowVerticalLine: (value) => true, getDrawingVerticalLine: (value) => fl.FlLine(color: Colors.white.withOpacity(0.1)), ), titlesData: fl.FlTitlesData( show: false, ), showingTooltipIndicators: selectedSpots, scatterTouchData: fl.ScatterTouchData( enabled: true, handleBuiltInTouches: true, touchTooltipData: fl.ScatterTouchTooltipData( tooltipBgColor: Colors.black, getTooltipItems: (fl.ScatterSpot touchedBarSpot) { return fl.ScatterTooltipItem( 'X: ', textStyle: TextStyle( height: 1.2, color: Colors.grey[100], fontStyle: FontStyle.italic, ), bottomMargin: 10, children: [ TextSpan( text: '${touchedBarSpot.x.toInt()} \n', style: const TextStyle( color: Colors.white, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), ), TextSpan( text: 'Y: ', style: TextStyle( height: 1.2, color: Colors.grey[100], fontStyle: FontStyle.italic, ), ), TextSpan( text: touchedBarSpot.y.toInt().toString(), style: const TextStyle( color: Colors.white, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), ), ], ); }, ), touchCallback: (fl.FlTouchEvent event, fl.ScatterTouchResponse? touchResponse) { if (touchResponse == null || touchResponse.touchedSpot == null) { return; } if (event is fl.FlTapUpEvent) { final sectionIndex = touchResponse.touchedSpot!.spotIndex; setState(() { if (selectedSpots.contains(sectionIndex)) { selectedSpots.remove(sectionIndex); } else { selectedSpots.add(sectionIndex); } }); } }, ), ), ), ), ); } }
原因分析
- 当
handleBuiltInTouches设为true时,fl_chart内部会接管触摸事件的完整逻辑,包括Tooltip的显示与隐藏,此时自定义touchCallback中的点击事件会和内置逻辑冲突,触发Tooltip后立刻被关闭。 showingTooltipIndicators参数在handleBuiltInTouches=true时会被内置触摸逻辑覆盖,无法手动控制Tooltip的显示状态。
解决方案
方案1:保留内置触摸逻辑,调整事件处理
保留handleBuiltInTouches=true,让fl_chart自行处理Tooltip的长按显示逻辑,同时修改touchCallback避免事件冲突:
scatterTouchData: fl.ScatterTouchData( enabled: true, handleBuiltInTouches: true, touchTooltipData: fl.ScatterTouchTooltipData( tooltipBgColor: Colors.black, getTooltipItems: (fl.ScatterSpot touchedBarSpot) { return fl.ScatterTooltipItem( 'X: ', textStyle: TextStyle( height: 1.2, color: Colors.grey[100], fontStyle: FontStyle.italic, ), bottomMargin: 10, children: [ TextSpan( text: '${touchedBarSpot.x.toInt()} \n', style: const TextStyle( color: Colors.white, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), ), TextSpan( text: 'Y: ', style: TextStyle( height: 1.2, color: Colors.grey[100], fontStyle: FontStyle.italic, ), ), TextSpan( text: touchedBarSpot.y.toInt().toString(), style: const TextStyle( color: Colors.white, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), ), ], ); }, ), touchCallback: (fl.FlTouchEvent event, fl.ScatterTouchResponse? touchResponse) { if (touchResponse == null || touchResponse.touchedSpot == null) { return; } final sectionIndex = touchResponse.touchedSpot!.spotIndex; // 仅处理点击事件,不干扰内置长按Tooltip逻辑 if (event is fl.FlTapUpEvent) { setState(() { if (selectedSpots.contains(sectionIndex)) { selectedSpots.remove(sectionIndex); } else { selectedSpots.add(sectionIndex); } }); } }, ),
同时移除showingTooltipIndicators参数,因为该参数在内置触摸逻辑下无效。
方案2:关闭内置触摸,手动控制Tooltip
如果需要通过showingTooltipIndicators手动控制Tooltip显示,将handleBuiltInTouches设为false,在touchCallback中手动管理Tooltip状态:
scatterTouchData: fl.ScatterTouchData( enabled: true, handleBuiltInTouches: false, touchTooltipData: fl.ScatterTouchTooltipData( tooltipBgColor: Colors.black, getTooltipItems: (fl.ScatterSpot touchedBarSpot) { // 保持原Tooltip样式逻辑不变 return fl.ScatterTooltipItem( 'X: ', textStyle: TextStyle( height: 1.2, color: Colors.grey[100], fontStyle: FontStyle.italic, ), bottomMargin: 10, children: [ TextSpan( text: '${touchedBarSpot.x.toInt()} \n', style: const TextStyle( color: Colors.white, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), ), TextSpan( text: 'Y: ', style: TextStyle( height: 1.2, color: Colors.grey[100], fontStyle: FontStyle.italic, ), ), TextSpan( text: touchedBarSpot.y.toInt().toString(), style: const TextStyle( color: Colors.white, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), ), ], ); }, ), touchCallback: (fl.FlTouchEvent event, fl.ScatterTouchResponse? touchResponse) { if (touchResponse == null || touchResponse.touchedSpot == null) { setState(() { selectedSpots.clear(); }); return; } final sectionIndex = touchResponse.touchedSpot!.spotIndex; if (event is fl.FlLongPressStartEvent) { setState(() { if (!selectedSpots.contains(sectionIndex)) { selectedSpots.add(sectionIndex); } }); } else if (event is fl.FlLongPressEndEvent) { setState(() { selectedSpots.remove(sectionIndex); }); } else if (event is fl.FlTapUpEvent) { setState(() { if (selectedSpots.contains(sectionIndex)) { selectedSpots.remove(sectionIndex); } else { selectedSpots.add(sectionIndex); } }); } }, ),
此时showingTooltipIndicators: selectedSpots会正常生效,可手动控制哪些点位显示Tooltip。
内容的提问来源于stack exchange,提问作者bluends
相关产品推荐
相关产品推荐

