Search code examples
flutterdart

How to get position of each item in page with scrollView in Flutter?


I have a page like this

enter image description here

I Open an Overlay widget based on the position of each item. i get this position with these codes

WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
  final RenderBox box = key.currentContext?.findRenderObject() as RenderBox;
  position = box.localToGlobal(Offset.zero);
  setState(() {});
});

the problem is that when I scroll to top parts of this messaging list offset.dy of that I get from this code is just keeps going down and this offset is not based on the page that is being shown but based on the list.

how can I fix this?


Solution

  • I belive solution presented by boeledi is what you are looking for.

     Rect _getPosition(BuildContext context) {
        final RenderBox box = context.findRenderObject() as RenderBox;
        final Offset topLeft = box.size.topLeft(box.localToGlobal(Offset.zero));
        final Offset bottomRight =
            box.size.bottomRight(box.localToGlobal(Offset.zero));
        return Rect.fromLTRB(
            topLeft.dx, topLeft.dy, bottomRight.dx, bottomRight.dy);
      }
    

    and translating it by the offset of the Overlay

        final Rect widgetPosition = _getPosition(context).translate(
          -overlayPosition.left,
          -overlayPosition.top,
        );
    

    Inside of the widget tree use CustomSingleChildLayout:

    
    
    CustomSingleChildLayout(
        delegate: _OverlayableContainerLayout(widgetPosition),
        child: child,
    ),
    

    And change position delegate.

    class _OverlayableContainerLayout extends SingleChildLayoutDelegate {
      _OverlayableContainerLayout(this.position);
    
      final Rect position;
    
      @override
      BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
        return BoxConstraints.loose(Size(position.width, position.height));
      }
    
      @override
      Offset getPositionForChild(Size size, Size childSize) {
        return Offset(position.left, position.top);
      }
    
      @override
      bool shouldRelayout(_OverlayableContainerLayout oldDelegate) {
        return position != oldDelegate.position;
      }
    }
    

    source