在前几节中,我们介绍了Flutter中常用的可滚动组件,也说过可以用ScrollController来控制可滚动组件的滚动位置,本节先介绍一下ScrollController,然后以ListView为例,展示一下ScrollController的具体用法。最后,再介绍一下路由切换时如何来保存滚动位置。
1.ScrollController
ScrollController构造函数如下
ScrollController({
double initialScrollOffset = 0.0, //初始滚动位置
this.keepScrollOffset = true,//是否保存滚动位置
...
})
我们介绍一下ScrollController常用的属性和方法:
- initialScrollOffset初始化位置设置
//定义一个ScrollController,定义初始化位置为500
ScrollController scrollController = ScrollController(initialScrollOffset:500);
initialScrollOffset初始化设置
- offset:可滚动组件当前的滚动位置。
-
jumpTo(double offset)、animateTo(double offset,...):这两个方法用于跳转到指定的位置,它们不同之处在于,后者在跳转时会执行一个动画,而前者不会。
jumpTo和animateTo区别
ScrollController还有一些属性和方法,我们将在后面原理部分解释
1.滚动监听
ScrollController间接继承自Listenable,我们可以根据ScrollController来监听滚动事件,如:
//滚动监听
scrollController.addListener(() {
print(scrollController.offset); //打印滚动位置
if (scrollController.offset < 1000 && showToTopBtn) {
// TODO
setState(() {
//调用父组件传递的方法
showToTopBtn = false;
});
} else if (scrollController.offset >= 1000 && showToTopBtn == false) {
setState(() {
//调用父组件传递的方法
showToTopBtn = true;
});
}
});
2.实例
import 'package:flutter/material.dart';
import 'package:flutter_os_china/constants/constants.dart';
class ListViewWidget extends StatefulWidget {
const ListViewWidget({Key? key}) : super(key: key);
@override
State<ListViewWidget> createState() => _ListViewWidgetState();
}
class _ListViewWidgetState extends State<ListViewWidget> {
//定义一个ScrollController
ScrollController scrollController = ScrollController();
bool showToTopBtn = false; //是否显示“返回到顶部”按钮
@override
void initState() {
// TODO: implement initState
super.initState();
//滚动监听
scrollController.addListener(() {
// scrollController.jumpTo(100);
// scrollController.jumpTo(600);
print(scrollController.offset); //打印滚动位置
if (scrollController.offset < 1000 && showToTopBtn) {
setState(() {
//调用父组件传递的方法
showToTopBtn = false;
});
} else if (scrollController.offset >= 1000 && showToTopBtn == false) {
setState(() {
//调用父组件传递的方法
showToTopBtn = true;
});
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ListView'),
),
body: buildListViewSeparated(),
floatingActionButton: showToTopBtn?FloatingActionButton(
backgroundColor: AppColor.primaryColor,
child: const Icon(Icons.arrow_upward,color: Colors.white),
onPressed: () {
// scrollController.jumpTo(0.0);
scrollController.animateTo(
0.0,
duration: const Duration(milliseconds: 200),//动画持续时间
curve: Curves.linear,//动画方式
);
},
):null
);
}
// ListView.separated
ListView buildListViewSeparated() {
return ListView.separated(
controller: scrollController,
itemBuilder: (context,index){
return ListTile(title: Text('$index'),trailing: const Icon(Icons.arrow_forward_ios),onTap: (){},);
},
separatorBuilder: (context,index){
return index%2==0?const Divider(color: Colors.green):const Divider(color: Colors.blue);
},
itemCount: 100,
);
}
// ListView.builder
ListView buildListViewBuilder() {
return ListView.builder(
itemCount: 100,//这里demo设置为100
itemExtent: 80.0,//强制高度为50
itemBuilder:(context,index){
return ListTile(title: Text('$index'),trailing: const Icon(Icons.arrow_forward_ios),onTap: (){},);
});
}
}
滚动距离大于1000的时候出现回到顶部的图标,点击回到顶部
3.滚动位置恢复
PageStorage是一个用于保存页面(路由)相关数据的组件,它并不会影响子树的UI外观,其实,PageStorage是一个功能型组件,它拥有一个存储桶(bucket),子树中的Widget可以通过指定不同的PageStorageKey来存储各自的数据或状态。
每次滚动结束,可滚动组件都会将滚动位置offset存储到PageStorage中,当可滚动组件重新创建时再恢复。如果ScrollController.keepScrollOffset为false,则滚动位置将不会被存储,可滚动组件重新创建时会使用ScrollController.initialScrollOffset;ScrollController.keepScrollOffset为true时,可滚动组件在第一次创建时,会滚动到initialScrollOffset处,因为这时还没有存储过滚动位置。在接下来的滚动中就会存储、恢复滚动位置,而initialScrollOffset会被忽略。
4.ViewPort及滚动位置等信息
位置信息都是通过 实例化ScrollController后,通过 scrollController.position获取相关位置信息,具体的位置信息有:(当前滚动位置等于最大可滚动长度的时候,这个时候可以下拉加载更多)
-
pixels:当前滚动位置。 -
maxScrollExtent:最大可滚动长度。 -
extentBefore:滑出ViewPort顶部的长度;此示例中相当于顶部滑出屏幕上方的列表长度。 -
extentInside:ViewPort内部长度;此示例中屏幕显示的列表部分的长度。 -
extentAfter:列表中未滑入ViewPort部分的长度;此示例中列表底部未显示到屏幕范围部分的长度。 -
atEdge:是否滑到了可滚动组件的边界(此示例中相当于列表顶或底部)












网友评论