skip to Main Content

It’s about a Flutter web app

I have an FirebaseAnimatedList with ListTiles.

When hovering over a ListTile an Overlay (a Row with 4 clickable IconButtons) attached to this ListTile shall be shown in the upper right corner of the ListTile. When exiting the ListTile the Overlay shall disappear. But the Overlay shall of course not disappear when exiting the ListTile via the Overlay (like in Microsoft Teams).

So I wrapped the ListTile in a MouseRegion. Entering MouseRegion should show the Overlay, exiting should hide the Overlay. And it’s doing so! but of course not what I wanted, since hovering over the Overlay means exiting the ListTile, hiding the Overlay, entering the ListTile, showing the Overlay, hovering over the Overlay, … it’s flickering. So I simply added some Duration, and it’s nearly working fine, but of course not (Thread?-)safe. Sometimes the Overlay doesn’t disappear anymore. I don’t believe that this is a correct approach to this problem.


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)

I’m doing flutter for 6 months now and this is my first question an stackoverflow. I’m running out of ideas with this overlay-problem. Thanks for help!

2

Answers


  1. Chosen as BEST ANSWER

    Looks like I found a solution using a Timer :-)

    I also wrapped the Overlay in a MouseRegion. Exiting ListTile sets

    timer = Timer(const Duration(seconds: 2), hideOverlay);
    

    that's the main idea. In fact, I use two Timers to make it nicer.

    Perhaps there is an easier solution without timers ...


  2. The problem is that you are using Future.delayed() to show and hide the 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(
            onHover: (event) {
          if (event is PointerExitEvent) {
            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');
              },
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search