'In Flutter, How can I make a widget "hover and follow" another widget in a Stack, without latency?

My demand is demonstrated in the following pic, I need the hovering box to "overflow" the underlying box:

A translucent box hovers and follows an item in the ListView while the list scrolls

My current implementation is to use Stack and Positioned.fromRect, get the Rect using the global key of item, then while it scroll I call setState to refresh it.

      Stack(
            children: [
              ListView.builder(
                controller: _controller,
                physics: NoFlingScrollPhysics(),
                itemBuilder: (context, index) {
                  return Center(
                    child: GestureDetector(
                        onTap: () {
                          setState(() {
                            selectedIndex = index;
                          });
                        },
                        child: RectGetter(
                            key: keys[index],
                            child: SizedBox.square(
                              dimension: MediaQuery.of(context).size.width / 2,
                              child: Container(
                                  alignment: Alignment.center,
                                  decoration: BoxDecoration(color: Colors.green, border: Border.all(color: Colors.black)),
                                  child: Text(index.toString())),
                            ))),
                  );
                },
                itemCount: 20,
              ),
              Positioned.fromRect(
                  rect: rect,
                  child: GestureDetector(
                      behavior: HitTestBehavior.deferToChild,
                      onTap: () => toggleColor(),
                      onVerticalDragUpdate: (update) {
                        _controller
                            .jumpTo(_controller.offset - update.delta.dy);
                      },
                      child: Container(color: color)))
            ],
          ),

    getRect(int? index) {
        if (index == null) return Rect.zero;
        var rect = RectGetter.getRectFromKey(keys[index]);
        rect = rect?.inflate(10);
        return rect ?? Rect.zero;
    }
    Rect get rect => getRect(selectedIndex);

However this method is laggy, it noticeably rubberbands while I scroll. Are there any better solution regarding this feature demand?

I am aware of inline OverflowBox solution (The items in ListView have OverflowBox in them), but the overflowing parts of it cannot be hit-tested by flutter's internal design, which is against my needs.



Solution 1:[1]

Thanks to @pskink, CompositedTransformTarget / CompositedTransformFollower is the solution.

TLDR: If HitTest/GestureDetector is needed on the "overflowing parts", and its position and size is dynamic, always use Overlay + CompositedTransformFollower!

See DartPad of my implementation

Explanation: Stack is Overlay under the hood, however Stack trades flexibility for convenience.

CompositedTransformTarget/CompositedTransformFollower pair can be used in Overlay to link Widgets in different layers by their position and align them with targetAnchor/followerAnchor parameter in the Follower widget.

The Follower can also get the size of its Target by accessing LayerLink#leaderSize.

This method does not incur the no-HitTest issue of OverflowBox either.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Linkfting