Flutter组件在滚动布局中保持可见

668 阅读1分钟

效果图

6ZWGQO.md.gif

当组件在滚动容器Leading方向不可见时自动滚动到顶部和容器对齐

当组件在滚动容器Trailing方向不可见时自动滚动到底部和容器对齐

当组件完全可见时不滚动

代码实现

void _makeVisible() {
  ///_visibleKey是GlobalKey类型,绑定到测试组件
  var object = _visibleKey.currentContext.findRenderObject();
  var position = Scrollable.of(_visibleKey.currentContext).position;
  final RenderAbstractViewport viewport = RenderAbstractViewport.of(object);
  double max = viewport
    .getOffsetToReveal(object, 0.0)
    .offset
    .clamp(position.minScrollExtent, position.maxScrollExtent) as double;
  double min = viewport
    .getOffsetToReveal(object, 1.0)
    .offset
    .clamp(position.minScrollExtent, position.maxScrollExtent) as double;
  if (position.pixels >= min && position.pixels <= max) {
    Scaffold.of(_visibleKey.currentContext)
      .showSnackBar(SnackBar(content: Text("无需滚动")));
    return;
  }
  ///滚动到完全可见
  position.jumpTo(position.pixels.clamp(min, max));
}

分析:

  • 无论是ListView还是CustomScrollView,它们最终都会生成Scrollable类型的Widget

  • Scrollable组件最终会生成Viewport或者ShrinkWrapViewport组件

  • Viewport类组件会生成RenderViewport类型的RenderObject,也就是RenderAbstractViewport的子类型

RenderAbstractViewport.getOffsetToReveal(RenderObject object,double alignment)方法:

  • alignment为0.0时代表object和容器顶部对齐需要滚动的距离TOP
  • alignment为0.5代表object和容器居中对齐需要滚动的距离CENTER
  • alignment为1.0代表object和容器底部对齐需要滚动的距离BOTTOM

很明显BOTTOM<CENTER<TOP, p = ScrollPosition.pixels:

  • 如果p当前小于BOTTOM代表在容器下方且不完全可见
  • 如果p大于TOP代表在容器上方且不完全可见
  • 如果p在BOTTOM和TOP之间代表完全可见

上述代码是笔者摘抄自Scrollable.ensureVisible()方法,哈哈