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

问题描述 投票:0回答:1

关于 Flutter 网络应用

我有一个带有 ListTiles 的 FirebaseAnimatedList。

当鼠标悬停在 ListTile 上时,附加到此 ListTile 的叠加层(一行带有 4 个可点击图标按钮)应显示在 ListTile 的右上角。当退出 ListTile 时,Overlay 将消失。但是当通过 Overlay 退出 ListTile 时,Overlay 当然不会消失(就像在 Microsoft Teams 中一样)。

所以我将 ListTile 包裹在 MouseRegion 中。进入 MouseRegion 应该显示 Overlay,退出应该隐藏 Overlay。它正在这样做!但当然不是我想要的,因为悬停在 Overlay 上意味着退出 ListTile,隐藏 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;

  void showOverlay() {
    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: buildOverlay(),
        ),
      ),
    );

    overlay.insert(entry!);
  }

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

  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.start),
            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: (e) async {
          await Future.delayed(const Duration(milliseconds: 200));
          showOverlay();
        },
        onExit: (e) async {
          await Future.delayed(const Duration(milliseconds: 500));
          hideOverlay();
        },
        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');
          },
        ),
      ),
    );
  }
}

ListTile with Overlay (not disappearing anymore)

我现在做 flutter 已经 6 个月了,这是我的第一个问题,一个 stackoverflow。对于这个覆盖问题,我的想法已经用完了。感谢您的帮助!

flutter overlay listtile
1个回答
0
投票

问题是您正在使用 Future.delayed() 来显示和隐藏叠加层。

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;

  void showOverlay() {
    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: buildOverlay(),
        ),
      ),
    );

    overlay.insert(entry!);
  }

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

  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.start),
            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(
        onHover: (event) {
          if (event.isExited) {
            hideOverlay();
          } else {
            showOverlay();
          }
        },
        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');
          },
        ),
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.