Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/flutter/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Flutter 颤振列表视图延迟加载_Flutter - Fatal编程技术网

Flutter 颤振列表视图延迟加载

Flutter 颤振列表视图延迟加载,flutter,Flutter,如何实现无限列表视图的项目延迟加载?当用户滚动到listview的末尾时,我想通过网络加载更多项目。您可以收听广播 ScrollController有一些有用的信息,例如scrolloffset和列表 在您的例子中,有趣的部分位于当前可见的滚动位置controller.position。它表示可滚动文件的一段 ScrollPosition包含有关其在可滚动文件中位置的信息。例如extentBefore和extentAfter。或者它的大小,带有extentInside 考虑到这一点,您可以基于表

如何实现无限列表视图的项目延迟加载?当用户滚动到listview的末尾时,我想通过网络加载更多项目。

您可以收听广播

ScrollController
有一些有用的信息,例如scrolloffset和列表

在您的例子中,有趣的部分位于当前可见的
滚动位置
controller.position
。它表示可滚动文件的一段

ScrollPosition
包含有关其在可滚动文件中位置的信息。例如
extentBefore
extentAfter
。或者它的大小,带有
extentInside

考虑到这一点,您可以基于表示剩余可用滚动空间的
extentAfter
触发服务器调用

下面是一个使用我所说内容的基本示例

class MyHome extends StatefulWidget {
  @override
  _MyHomeState createState() => new _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  ScrollController controller;
  List<String> items = new List.generate(100, (index) => 'Hello $index');

  @override
  void initState() {
    super.initState();
    controller = new ScrollController()..addListener(_scrollListener);
  }

  @override
  void dispose() {
    controller.removeListener(_scrollListener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Scrollbar(
        child: new ListView.builder(
          controller: controller,
          itemBuilder: (context, index) {
            return new Text(items[index]);
          },
          itemCount: items.length,
        ),
      ),
    );
  }

  void _scrollListener() {
    print(controller.position.extentAfter);
    if (controller.position.extentAfter < 500) {
      setState(() {
        items.addAll(new List.generate(42, (index) => 'Inserted $index'));
      });
    }
  }
}
类MyHome扩展StatefulWidget{
@凌驾
_MyHomeState createState()=>new_MyHomeState();
}
类MyHomeState扩展状态{
滚动控制器;
列表项=新列表。生成(100,(索引)=>“Hello$index”);
@凌驾
void initState(){
super.initState();
控制器=新的ScrollController()…addListener(\u scrollListener);
}
@凌驾
无效处置(){
控制器。RemovelListener(_scrollListener);
super.dispose();
}
@凌驾
小部件构建(构建上下文){
归还新脚手架(
正文:新的滚动条(
子项:新建ListView.builder(
控制器:控制器,
itemBuilder:(上下文,索引){
返回新文本(项目[索引]);
},
itemCount:items.length,
),
),
);
}
void_scrollListener(){
打印(控制器、位置、延伸器);
if(控制器位置延伸小于500){
设置状态(){
items.addAll(newlist.generate(42,(index)=>Inserted$index));
});
}
}
}


您可以清楚地看到,当到达滚动的末尾时,滚动条会因为加载了更多的项目而消耗。

感谢Rémi Rousselet的方法,但它并没有解决所有问题。尤其是当ListView滚动到底部时,它仍然会调用scrollListener几次。改进的方法是将通知侦听器与Remi的方法相结合。以下是我的解决方案:

bool _handleScrollNotification(ScrollNotification notification) {
  if (notification is ScrollEndNotification) {
    if (_controller.position.extentAfter == 0) {
      loadMore();
    }
  }
  return false;
}

@override
Widget build(BuildContext context) {
    final Widget gridWithScrollNotification = NotificationListener<
            ScrollNotification>(
        onNotification: _handleScrollNotification,
        child: GridView.count(
            controller: _controller,
            padding: EdgeInsets.all(4.0),
          // Create a grid with 2 columns. If you change the scrollDirection to
          // horizontal, this would produce 2 rows.
          crossAxisCount: 2,
          crossAxisSpacing: 2.0,
          mainAxisSpacing: 2.0,
          // Generate 100 Widgets that display their index in the List
          children: _documents.map((doc) {
            return GridPhotoItem(
              doc: doc,
            );
          }).toList()));
    return new Scaffold(
      key: _scaffoldKey,
      body: RefreshIndicator(
       onRefresh: _handleRefresh, child: gridWithScrollNotification));
}
bool\u handleScrollNotification(滚动通知通知){
如果(通知为ScrollEndNotification){
如果(_controller.position.extentAfter==0){
loadMore();
}
}
返回false;
}
@凌驾
小部件构建(构建上下文){
最终小部件GridWithCrollNotification=NotificationListener<
滚动通知>(
onNotification:_handleScrollNotification,
子项:GridView.count(
控制器:_控制器,
填充:所有边缘设置(4.0),
//创建一个包含两列的网格。如果将滚动方向更改为
//水平,这将产生2行。
交叉轴计数:2,
交叉轴间距:2.0,
主轴间距:2.0,
//生成100个在列表中显示其索引的小部件
子项:_documents.map((doc){
退货项目(
博士:博士,
);
}).toList());
归还新脚手架(
钥匙:_scaffoldKey,
正文:刷新指示器(
onRefresh:_handleRefresh,child:gridWithScrollNotification));
}

解决方案使用了ScrollController,我看到了关于页面的评论
我想分享我关于包增量加载列表视图的发现 .
正如packaged所说:这可以用来加载从API请求接收的分页数据

基本上,当ListView构建最后一项时,这意味着用户已向下滚动到底部
希望它能帮助有类似问题的人。

为了演示的目的,我改变了示例,让一个页面只包含一个项目 并添加一个循环压缩机指示器


使用lazy_load_scrollview:1.0.0包,该包在幕后使用了与熊猫世界在这里回答的相同的概念。该软件包使其更易于实现。

如果您希望实现上下方向的延迟加载,则发布的解决方案无法解决此问题。滚动将在此处跳转,请参见

如果你想做上下方向的惰性加载,库可以提供帮助

示例():

静态常数双套件高度=30.0;
双向交叉控制器;
双oldScrollPosition=0.0;
@凌驾
void initState(){
super.initState();
for(int i=-10;i正边界)
{

对于(inti=itemCount+1;i,这里是我的方法,其灵感来自上述答案

NotificationListener(onNotification: _onScrollNotification, child: GridView.builder())

bool _onScrollNotification(ScrollNotification notification) {
    if (notification is ScrollEndNotification) {
      final before = notification.metrics.extentBefore;
      final max = notification.metrics.maxScrollExtent;

      if (before == max) {
        // load next page
        // code here will be called only if scrolled to the very bottom
      }
    }
    return false;
  }

下面是我的查找列表视图结尾的解决方案

_scrollController.addListener(scrollListenerMilli);


if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
      getMoreData();
    }
如果您想在1/2或3/4之后延迟加载,请这样更改

if (_scrollController.position.pixels == (_scrollController.position.maxScrollExtent * .75)) {//.5
      getMoreData();
    }

被接受的答案是正确的,但您也可以按如下操作:

Timer _timer;

  Widget chatMessages() {
    _timer = new Timer(const Duration(milliseconds: 300), () {
      _scrollController.animateTo(
        _scrollController.position.maxScrollExtent,
        curve: Curves.easeOut,
        duration: const Duration(milliseconds: 300),
      );
    });
    return StreamBuilder(
      stream: chats,
      builder: (context, snapshot) {
        return snapshot.hasData
            ? ListView.builder(
                // physics: NeverScrollableScrollPhysics(),
                controller: _scrollController,
                shrinkWrap: true,
                reverse: false,
                itemCount: snapshot.data.documents.length,
                itemBuilder: (context, index) {
                  return MessageTile(
                    message: snapshot.data.documents[index].data["message"],
                    sendByMe: widget.sendByid ==
                        snapshot.data.documents[index].data["sendBy"],
                  );
                })
            : Container();
      },
    );
  }

还有这个包,去掉了样板文件:

Use
ListView.builder()
并提供一些比已加载的内容大的项目。当用户滚动到尚未加载的索引时,您将加载数据并重新呈现列表。@GünterZöchbauer我不喜欢这种方法,因为分页要困难得多。例如,您不知道未知索引是否在第n+1页或第n+2页内。它是这取决于用例。它在我们的项目中运行得很好。你也可以将它与滚动位置相结合。我喜欢的是,我可以拥有比加载的项目多得多的数字,滚动条反映了这一点。@GünterZöchbauer你能给我一个如何按你的方式分页的例子吗?是的,它看起来不错,我会尝试。但我有问题。itemCount(itemBuilder参数)会随着items.length的更改而动态更改吗?顺便说一句,我应该总是在函数体的末尾调用重写函数的“super”调用吗?这有点离谱,但我非常担心
if (_scrollController.position.pixels == (_scrollController.position.maxScrollExtent * .75)) {//.5
      getMoreData();
    }
Timer _timer;

  Widget chatMessages() {
    _timer = new Timer(const Duration(milliseconds: 300), () {
      _scrollController.animateTo(
        _scrollController.position.maxScrollExtent,
        curve: Curves.easeOut,
        duration: const Duration(milliseconds: 300),
      );
    });
    return StreamBuilder(
      stream: chats,
      builder: (context, snapshot) {
        return snapshot.hasData
            ? ListView.builder(
                // physics: NeverScrollableScrollPhysics(),
                controller: _scrollController,
                shrinkWrap: true,
                reverse: false,
                itemCount: snapshot.data.documents.length,
                itemBuilder: (context, index) {
                  return MessageTile(
                    message: snapshot.data.documents[index].data["message"],
                    sendByMe: widget.sendByid ==
                        snapshot.data.documents[index].data["sendBy"],
                  );
                })
            : Container();
      },
    );
  }