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

Flutter Web:ListTile悬浮时如何正确显示Overlay

解决Flutter Web中ListTile悬浮Overlay的交互问题

核心问题是Overlay不属于ListTile的MouseRegion范围,鼠标从ListTile移到Overlay时会触发ListTile的onExit,导致Overlay被错误隐藏。我们需要同时监听ListTile和Overlay的鼠标状态,只有当鼠标完全离开两者时才隐藏Overlay。

修改后的完整代码

class ListTileWidget extends StatefulWidget {
  final Map dataMap;

  const ListTileWidget({Key? key, required this.dataMap}) : super(key: key);

  @override
  State<ListTileWidget> createState() => _ListTileWidgetState();
}

class _ListTileWidgetState extends State<ListTileWidget> {
  final layerLink = LayerLink();
  OverlayEntry? entry;
  bool _isHovering = false;
  Timer? _hideTimer;

  void _startHideTimer() {
    _hideTimer?.cancel();
    _hideTimer = Timer(const Duration(milliseconds: 200), () {
      if (!_isHovering) {
        hideOverlay();
      }
    });
  }

  void showOverlay() {
    if (entry != null) return;
    final overlay = Overlay.of(context);

    entry = OverlayEntry(
      builder: (context) => Positioned(
        width: 200,
        child: CompositedTransformFollower(
          link: layerLink,
          showWhenUnlinked: false,
          offset: const Offset(0, -20),
          followerAnchor: Alignment.topRight,
          targetAnchor: Alignment.topRight,
          child: MouseRegion(
            onEnter: (_) {
              setState(() => _isHovering = true);
              _hideTimer?.cancel();
            },
            onExit: (_) {
              setState(() => _isHovering = false);
              _startHideTimer();
            },
            child: buildOverlay(),
          ),
        ),
      ),
    );

    overlay.insert(entry!);
  }

  void hideOverlay() {
    entry?.remove();
    entry = null;
    _hideTimer?.cancel();
  }

  Widget buildOverlay() {
    return Material(
      color: Colors.grey,
      child: Row(
        children: [
          IconButton(
            icon: const Icon(Icons.remove),
            onPressed: () {
              print('Button pressed');
            },
          ),
          IconButton(
            icon: const Icon(Icons.brush),
            onPressed: () {
              print('Button pressed');
            },
          ),
          IconButton(
            icon: const Icon(Icons.play_arrow),
            onPressed: () {
              print('Button pressed');
            },
          ),
          IconButton(
            icon: const Icon(Icons.emoji_emotions),
            onPressed: () {
              print('Button pressed');
            },
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return CompositedTransformTarget(
      link: layerLink,
      child: MouseRegion(
        onEnter: (_) {
          setState(() => _isHovering = true);
          _hideTimer?.cancel();
          showOverlay();
        },
        onExit: (_) {
          setState(() => _isHovering = false);
          _startHideTimer();
        },
        child: ListTile(
          leading: const Icon(Icons.person),
          title: Text(
            '${widget.dataMap['userName']}\n${widget.dataMap['time']}',
            style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
          ),
          subtitle: Text(widget.dataMap['message']),
          onTap: () {
            print('onTap');
          },
        ),
      ),
    );
  }

  @override
  void dispose() {
    _hideTimer?.cancel();
    hideOverlay();
    super.dispose();
  }
}

关键改动说明

  1. 添加状态变量与计时器

    • _isHovering:跟踪鼠标是否在ListTile或Overlay范围内
    • _hideTimer:延迟执行隐藏逻辑,避免鼠标在ListTile和Overlay间切换时的闪烁
  2. Overlay添加MouseRegion
    给Overlay包裹MouseRegion,当鼠标进入Overlay时标记_isHovering = true并取消隐藏计时器;退出时标记_isHovering = false并启动隐藏计时器。

  3. 重构显示/隐藏逻辑

    • 移除原代码中的async/await延迟,改用Timer控制隐藏时机,避免线程安全问题
    • 只有当_isHovering为false时才执行hideOverlay,确保鼠标在Overlay上时Overlay不会消失
  4. 修正细节问题

    • 替换原代码中不存在的Icons.start为Icons.play_arrow
    • 在dispose中清理计时器和Overlay,避免内存泄漏

这样修改后,就能实现类似Microsoft Teams的交互:鼠标悬浮ListTile显示Overlay,移到Overlay上时Overlay保持显示,完全离开两者后延迟隐藏。

内容的提问来源于stack exchange,提问作者MarkusT75

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 19:15:07