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更容易