前言
extended_scroll是我上传到pub.dev的flutter组件,扩展 ScrollController的ensureVisible方法,支持设置offsetTop
为什么要扩展
void scroll2PositionByTabIndex(int index) {
RenderSliverToBoxAdapter? keyRenderObject = cardKeys[index].currentContext?.findAncestorRenderObjectOfType<RenderSliverToBoxAdapter>();
if (keyRenderObject != null) {
_scrollController.position
.ensureVisible(keyRenderObject, duration: const Duration(milliseconds: 300), curve: Curves.linear)
.then((value) => isTabClicked = false);
}
}
痛点:当点击浮动header中tab(评价、详情)时,调用ScrollController的ensureVisible方法时,总是存在被浮动header挡住的情况
解决这个问题
1、页面初始化缓存各个模块的位置信息,再调用scrollController.animateTo去滚动页面
//缓存商品、评论、详情、同店好货4个模块的y坐标
void cacheChildrenOffset() {
for (int i = 0; i < cardKeys.length; i++) {
RenderObject? keyRenderObject = cardKeys[i].currentContext?.findRenderObject();
if (keyRenderObject != null) {
double offsetY = keyRenderObject.getTransformTo(null).getTranslation().y;
itemsOffsetMap[i] = offsetY;
}
}
}
Widget floatingHeader = Positioned(
top: 0,
left: 0,
child: tabHeader(
context,
onChange: (index) {
isTabClicked = true;
store.dispatch(ChangeTopTabIndexAction(index));
scroll2PositionByTabIndex(index);
},
),
);
//根据index滚动页面至相应模块位置
void scroll2PositionByTabIndex(int index) {
if (itemsOffsetMap[index] != null) {
double offsetY = itemsOffsetMap[index]! - 42 - getStatusHeight(context);
if (offsetY < 0) offsetY = 0;
_scrollController.animateTo(offsetY, duration: const Duration(milliseconds: 300), curve: Curves.linear).then((value) => isTabClicked = false);
}
}
缺点:存在偶尔失灵,由于各个设备的性能不一样,存在没缓存好位置信息就要滚动的情况,有点不稳定
2、利用ensureVisible使锚点可见,并能设置距离顶部一定距离,可是查看定义并不支持
发现有个alignment,原本认为可以满足需求
void scroll2PositionByTabIndex(int index) {
RenderSliverToBoxAdapter? keyRenderObject = cardKeys[index].currentContext?.findAncestorRenderObjectOfType<RenderSliverToBoxAdapter>();
if (keyRenderObject != null) {
_scrollController.position
.ensureVisible(keyRenderObject,
alignment: (42 + getStatusHeight(context)) / getScreenHeight(context), duration: const Duration(milliseconds: 300), curve: Curves.linear)
.then((value) => isTabClicked = false);
}
}
经过测试并不能满足需求, 到这里怒改官方代码,(flutter/lib/src/widgets/scroll_position.dart#line686)
//根据index滚动页面至相应模块位置
void scroll2PositionByTabIndex(int index) {
RenderSliverToBoxAdapter? keyRenderObject = cardKeys[index].currentContext?.findAncestorRenderObjectOfType<RenderSliverToBoxAdapter>();
if (keyRenderObject != null) {
_scrollController.position
.ensureVisible(keyRenderObject, offsetTop: 42 + getStatusHeight(context), duration: const Duration(milliseconds: 300), curve: Curves.linear)
.then((value) => isTabClicked = false);
}
}
到这里,完美满足需求,效果如下
结语
这个ensureVisible的扩充,已经抽成独立的包,pub.dev,欢迎使用,详细example见 github.com/GuoguoDad/j… , 如果对你有帮助,请不要吝啬你的赞👍🏻