Flutter 如何在ListView中检查滚动位置是在顶部还是在底部?
我正在尝试实现无限滚动功能 我尝试在Flutter 如何在ListView中检查滚动位置是在顶部还是在底部?,flutter,Flutter,我正在尝试实现无限滚动功能 我尝试在NotificationListener中使用列表视图来检测滚动事件,但我看不到一个事件显示滚动是否已到达视图底部 实现这一点的最佳方法是什么?您可以使用ListView.builder创建包含无限项的滚动列表。当显示新单元格时,将根据需要调用您的itemBuilder 如果希望获得有关滚动事件的通知,以便从网络上加载更多数据,可以传递控制器参数,并使用添加侦听器将侦听器附加到滚动控制器。滚动控制器的位置可以用来确定滚动是否接近底部。\u ScrollCont
NotificationListener
中使用列表视图
来检测滚动事件,但我看不到一个事件显示滚动是否已到达视图底部
实现这一点的最佳方法是什么?您可以使用
ListView.builder
创建包含无限项的滚动列表。当显示新单元格时,将根据需要调用您的itemBuilder
如果希望获得有关滚动事件的通知,以便从网络上加载更多数据,可以传递控制器
参数,并使用添加侦听器
将侦听器附加到滚动控制器
。滚动控制器的位置可以用来确定滚动是否接近底部。\u ScrollController=new ScrollController();
_scrollController = new ScrollController();
_scrollController.addListener(
() {
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
double delta = 200.0; // or something else..
if ( maxScroll - currentScroll <= delta) { // whatever you determine here
//.. load more
}
}
);
_scrollController.addListener(
() {
double maxScroll=\u scrollController.position.maxScrollExtent;
双currentScroll=\u scrollController.position.pixels;
double delta=200.0;//或其他东西。。
if(maxScroll-currentScroll我对无限滚动使用了不同的方法。我对变量更改侦听器使用了r类。
如果变量发生变化,它将触发事件并最终命中API
class DashboardAPINotifier extends ChangeNotifier {
bool _isLoading = false;
get getIsLoading => _isLoading;
set setLoading(bool isLoading) => _isLoading = isLoading;
}
SliverList(delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
Widget listTile = Container();
if (index == widget._propertyList.length - 1 &&
widget._propertyList.length <widget._totalItemCount) {
listTile = _reachedEnd();
} else {
listTile = getItem(widget._propertyList[index]);
}
return listTile;
},
childCount: (widget._propertyList != null)? widget._propertyList.length: 0,
addRepaintBoundaries: true,
addAutomaticKeepAlives: true,
),
)
_reachEnd() method take care to hit the api. It trigger the `_dashboardAPINotifier._loading`
// Function that initiates a refresh and returns a CircularProgressIndicator - Call when list reaches its end
Widget _reachedEnd() {
if (widget._propertyList.length < widget._totalItemCount) {
_dashboardAPINotifier.setLoading = true;
_dashboardAPINotifier.notifyListeners();
return const Padding(
padding: const EdgeInsets.all(20.0),
child: const Center(
child: const CircularProgressIndicator(),
),
);
} else {
_dashboardAPINotifier.setLoading = false;
_dashboardAPINotifier.notifyListeners();
print("No more data found");
Utils.getInstance().showSnackBar(_globalKey, "No more data found");
}
}
初始化DashboardAPINotifier类
@override
void initState() {
super.initState();
_dashboardAPINotifier = DashboardAPINotifier();
_hitDashboardAPI(); // init state
_dashboardAPINotifier.addListener(() {
if (_dashboardAPINotifier.getIsLoading) {
print("loading is true");
widget._page++; // For API page
_hitDashboardAPI(); //Hit API
} else {
print("loading is false");
}
});
}
现在最好的部分是当你必须点击API的时候。
如果您使用的是SliverList
,那么您必须在什么时候点击API
class DashboardAPINotifier extends ChangeNotifier {
bool _isLoading = false;
get getIsLoading => _isLoading;
set setLoading(bool isLoading) => _isLoading = isLoading;
}
SliverList(delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
Widget listTile = Container();
if (index == widget._propertyList.length - 1 &&
widget._propertyList.length <widget._totalItemCount) {
listTile = _reachedEnd();
} else {
listTile = getItem(widget._propertyList[index]);
}
return listTile;
},
childCount: (widget._propertyList != null)? widget._propertyList.length: 0,
addRepaintBoundaries: true,
addAutomaticKeepAlives: true,
),
)
_reachEnd() method take care to hit the api. It trigger the `_dashboardAPINotifier._loading`
// Function that initiates a refresh and returns a CircularProgressIndicator - Call when list reaches its end
Widget _reachedEnd() {
if (widget._propertyList.length < widget._totalItemCount) {
_dashboardAPINotifier.setLoading = true;
_dashboardAPINotifier.notifyListeners();
return const Padding(
padding: const EdgeInsets.all(20.0),
child: const Center(
child: const CircularProgressIndicator(),
),
);
} else {
_dashboardAPINotifier.setLoading = false;
_dashboardAPINotifier.notifyListeners();
print("No more data found");
Utils.getInstance().showSnackBar(_globalKey, "No more data found");
}
}
我想为添加示例。请参阅以下代码段
var _scrollController = ScrollController();
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
// Perform your task
}
});
只有在列表中最后一项可见时才会触发此操作。通常有两种方法
1.使用ScrollController
//创建一个变量
var_controller=ScrollController();
@凌驾
void initState(){
super.initState();
//设置侦听器。
_controller.addListener(){
if(_controller.position.atEdge){
如果(_controller.position.pixels==0){
//你在顶端。
}否则{
//你在最底层。
}
}
});
}
用法:
ListView(控制器:\控制器)//分配控制器。
2.使用NotificationListener
NotificationListener(
onNotification:(滚动结束){
var metrics=scrollEnd.metrics;
if(metrics.atEdge){
如果(metrics.pixels==0)打印(“在顶部”);
else打印(“在底部”);
}
返回true;
},
子项:ListView.builder(
物理:ClampingScrollPhysics(),
itemBuilder:(_,i)=>ListTile(标题:Text('Item$i')),
物品计数:20,
),
)
我觉得这个答案是对Esteban的答案的补充(带有扩展方法和节流阀),但它也是一个有效的答案,所以它是:
Dart最近(不确定)得到了一个很好的特性,它允许我们编写onBottomReach
方法,就像ScrollController
的一部分一样:
导入'dart:async';
进口“包装:颤振/材料.省道”;
卷轴控制器上的延伸张力{
BottomReach无效(无效回调,
{双灵敏度=200.0,持续时间限制}){
最终持续时间=节流??持续时间(毫秒:200);
定时器;
addListener((){
如果(计时器!=null){
返回;
}
//我用定时器来破坏定时器
定时器=定时器(持续时间,()=>timer=null);
//见Esteban Díaz的回答
最终maxScroll=position.maxScrollExtent;
最终currentScroll=position.pixels;
如果(maxScroll-currentScroll更简单的方法如下:
NotificationListener<ScrollEndNotification>(
onNotification: onNotification,
child: <a ListView or Wrap or whatever widget you need>
)
t.metrics.pixel
当用户的scrol位于顶部时为0,当确定scrools时大于0。
t.metrics.atEdge
为true
当用户使用scrol位于顶部或使用scrol
log
方法来自程序包import'dart:developer';
final ScrollController=ScrollController();
final ScrollController controller = ScrollController();
void _listener() {
double maxPosition = controller.position.maxScrollExtent;
double currentPosition = controller.position.pixels;
/// You can change this value . It's a default value for the
/// test if the difference between the great value and the current value is smaller
/// or equal
double difference = 10.0;
/// bottom position
if ( maxPosition - currentPosition <= difference )
/// top position
else
if(mounted)
setState(() {});
}
@override
void initState() {
super.initState();
controller.addListener(_listener);
}
void_listener(){
double maxPosition=controller.position.maxScrollExtent;
双currentPosition=控制器位置像素;
///您可以更改此值。它是
///测试大值和当前值之间的差值是否较小
///或同等
双差=10.0;
///底部位置
如果(maxPosition-currentPosition您能解释一下如何确定ListView在ScrollController中的最大滚动偏移量吗?我刚刚找到了解决方案:ScrollController.position。maxScrollExtent@Pierre.Vriens这段代码创建了一个用于控制列表的scrollController并粘贴一个lis如果你想让你知道什么时候会结束,然后继续加载更多的内容,或者根据我们定义的“delta”值做什么?@AnujSharma没有标准。为了这个例子,这是一个随机数……你可以完全忽略它,然后实现一些不同的功能(比如在列表末尾显示一个加载程序)..请确保在此函数中限制您的api调用,因为如果您在滚动结束时,并且您的第一个api调用需要时间才能返回数据,则我将尽可能多次调用。@NatwarSingh我们可以设置一个变量标志,以了解是否有任何api调用正在运行。如果没有api正在运行,则可以在服务器上发送api请求,反之亦然系统不会向服务器发送任何api请求。Thansk,您节省了我的时间。此答案应该更高。尝试了上述答案,但此答案更清晰,更易于实现。它检测列表的顶部,但不检测底部。如果您希望代码可用,请提供一些解释。使用ScrollController更容易