You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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));
    }
  }
}

Tooltip残留效果

解决方法

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:40:13