Flutter:创建Tooltip的组件销毁后Tooltip仍残留,如何手动移除?
Flutter:移除组件后手动清除残留Tooltip
在Flutter开发中,当带有Tooltip的组件从组件树中移除时,Tooltip可能会残留在界面上无法自动消失。以下是复现该问题的最小示例代码:
class BadToolTip extends StatefulWidget { @override State<StatefulWidget> createState() { return _BadToolTipState(); } } class _BadToolTipState extends State<BadToolTip> { bool _isDeleted = false; @override Widget build(BuildContext context) { if (_isDeleted) { return SizedBox.shrink(); } else { return IconButton( color: Colors.redAccent, onPressed: () => setState(() { _isDeleted = true; // Todo: remove tooltip from overlay here? }), tooltip: "This will not go away!", icon: Icon(Icons.delete)); } } }

解决方法
1. 使用Flutter 3.10+ 新增的TooltipController(推荐)
Flutter 3.10版本为Tooltip添加了controller参数,可直接通过控制器手动控制Tooltip的显示与隐藏,完美解决残留问题:
class FixedToolTip extends StatefulWidget { @override State<StatefulWidget> createState() { return _FixedToolTipState(); } } class _FixedToolTipState extends State<FixedToolTip> { bool _isDeleted = false; late TooltipController _tooltipController; @override void initState() { super.initState(); _tooltipController = TooltipController(); } @override void dispose() { _tooltipController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (_isDeleted) { // 移除组件前主动隐藏Tooltip _tooltipController.hideTooltip(); return SizedBox.shrink(); } else { return IconButton( color: Colors.redAccent, onPressed: () => setState(() { _isDeleted = true; }), tooltip: "This will go away now!", controller: _tooltipController, icon: Icon(Icons.delete)); } } }
2. 手动管理OverlayEntry(适配旧版本Flutter)
如果你的Flutter版本低于3.10,可以直接操作Overlay层,遍历并移除Tooltip对应的OverlayEntry:
class FixedToolTipOld extends StatefulWidget { @override State<StatefulWidget> createState() { return _FixedToolTipOldState(); } } class _FixedToolTipOldState extends State<FixedToolTipOld> { bool _isDeleted = false; final GlobalKey _iconKey = GlobalKey(); void _hideTooltip() { final overlay = Overlay.of(context); if (overlay != null) { // 遍历Overlay中的元素,移除关联的Tooltip overlay.context.visitChildElements((element) { if (element.widget is OverlayEntry) { final entry = element.widget as OverlayEntry; // 通过目标组件位置匹配Tooltip,确保只移除当前IconButton对应的Tooltip final renderBox = _iconKey.currentContext?.findRenderObject() as RenderBox?; if (renderBox != null && entry.builder(context).toString().contains("Tooltip")) { entry.remove(); } } }); } } @override Widget build(BuildContext context) { if (_isDeleted) { _hideTooltip(); return SizedBox.shrink(); } else { return IconButton( key: _iconKey, color: Colors.redAccent, onPressed: () => setState(() { _isDeleted = true; }), tooltip: "This will go away too!", icon: Icon(Icons.delete)); } } }
3. 自定义Tooltip组件(完全自主控制)
如果以上方案都不适用,可以自己实现Tooltip逻辑,完全掌控显示和隐藏时机:
class CustomToolTip extends StatefulWidget { @override State<StatefulWidget> createState() { return _CustomToolTipState(); } } class _CustomToolTipState extends State<CustomToolTip> { bool _isDeleted = false; OverlayEntry? _tooltipEntry; void _showCustomTooltip() { final renderBox = context.findRenderObject() as RenderBox; final position = renderBox.localToGlobal(Offset.zero); _tooltipEntry = OverlayEntry( builder: (context) => Positioned( left: position.dx, top: position.dy - 50, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(4), ), child: Text( "Custom tooltip that goes away!", style: TextStyle(color: Colors.white), ), ), ), ); Overlay.of(context)?.insert(_tooltipEntry!); // 自动隐藏逻辑 Future.delayed(Duration(seconds: 2), _hideCustomTooltip); } void _hideCustomTooltip() { _tooltipEntry?.remove(); _tooltipEntry = null; } @override void dispose() { _hideCustomTooltip(); super.dispose(); } @override Widget build(BuildContext context) { if (_isDeleted) { _hideCustomTooltip(); return SizedBox.shrink(); } else { return IconButton( color: Colors.redAccent, onPressed: () => setState(() { _isDeleted = true; }), onLongPress: _showCustomTooltip, icon: Icon(Icons.delete)); } } }
内容的提问来源于stack exchange,提问作者PeterM
相关产品推荐
相关产品推荐

