【问题标题】:Flutter infinite/long list - memory issue and stack overflow errorFlutter无限/长列表-内存问题和堆栈溢出错误
【发布时间】:2018-12-31 13:59:20
【问题描述】:

我的用例是创建文章的列表视图(每个项目具有相同的外观,可能有大量文章,例如 > 10000)。我试过了 - 带有 ListView.builder 的 ListView:它假定仅在显示项目时呈现项目 - ScrollController:确定何时加载下一个项目(分页) - 然后我使用List来存储使用http从restful API获取的数据,方法是将http中的数据添加到List实例中

这种方法是可以的,但是如果用户继续滚动页面,List 实例的项目会越来越多,它可能会因堆栈溢出错误而崩溃。

如果我不调用 List.addAll(),而是分配从 api 获取的数据,例如:list = data; 我有一个问题,当用户向上滚动时,他/她将无法看到以前的项目。

有没有解决这个问题的好方法?谢谢!

  import 'package:flutter/material.dart';
  import 'package:app/model.dart';
  import 'package:app/components/item.dart';

  abstract class PostListPage extends StatefulWidget {
    final String head;
    DealListPage(this.head);
  }

  abstract class PostListPageState<T extends PostListPage> extends State<PostListPage> {
    final int MAX_PAGE = 2;

    DealListPageState(String head) {
      this.head = head;
    }

    final ScrollController scrollController = new ScrollController();

    void doInitialize() {

      page = 0;
      try {
        list.clear();
        fetchNextPage();
      }
      catch(e) {
        print("Error: " + e.toString());
      }
    }

    @override
    void initState() {
      super.initState();
      this.fetchNextPage();
      scrollController.addListener(() {
        double maxScroll = scrollController.position.maxScrollExtent;
        double currentScroll = scrollController.position.pixels;
        double delta = 200.0; // or something else..
        if ( maxScroll - currentScroll <= delta) {
          fetchNextPage();
        }
      });
    }

    @override
    void dispose() {
      scrollController.dispose();
      super.dispose();
    }


    void mergeNewResult(List<PostListItem> result) {
      list.addAll(result);

    }


    Future fetchNextPage() async {
      if (!isLoading && mounted) {
        page++;
        setState(() {
          isLoading = true;
        });
        final List<PostListItem> result = await doFetchData(page);
        setState(() {
          if (result != null && result.length > 0) {
            mergeNewResult(result);
          } else {
          //TODO show notification
          }
          isLoading = false;
        });
      }
    }

    Future doFetchData(final int page);

    String head;
    List<PostListItem> list = new List();
    var isLoading = false;

    int page = 0;
    int pageSize = 20;
    final int scrollThreshold = 10;

    Widget buildProgressIndicator() {
      return new Padding(
        padding: const EdgeInsets.all(8.0),
        child: new Center(
          child: new Opacity(
            opacity: isLoading ? 1.0 : 0.0,
            child: new CircularProgressIndicator(),
          ),
        ),
      );
    }

    @override
    Widget build(BuildContext context) {
      ListView listView = ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (BuildContext context, int index) {

          if (index == list.length) {
            return buildProgressIndicator();
          }

          if (index > 0) {
            return Column(
                children: [Divider(), PostListItem(list[index])]
            );
          }
          return PostListItem(list[index]);
        },
        controller: scrollController,
        itemCount: list.length
    );

    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
          title: Text(head),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.search),
              onPressed: () {

              },
            ),
            // action button
            IconButton(
              icon: Icon(Icons.more_horiz),
              onPressed: () {
              },
            ),
          ]
        ),
        body: new RefreshIndicator(
          onRefresh: handleRefresh,
          child: listView
        ),

      );
    }

    Future<Null> handleRefresh() async {
      doInitialize();
      return null;
    }
  }

在我的情况下,当列表长度为 600 时,我开始出现堆栈溢出错误,例如:

I/flutter ( 8842): Another exception was thrown: Stack Overflow
I/flutter ( 8842): Another exception was thrown: Stack Overflow

屏幕:

enter image description here

不知何故,颤振没有显示更多的错误细节。

【问题讨论】:

  • 只需在ListView.builder#itemBuilder 中获取分页数据(使用MapCache 进行异步数据访问)- 这样您将在滚动ListView 时按需请求数据
  • @pskink:谢谢!但是在这种情况下如何设置 itemCount 参数呢?你有一些示例代码吗?我想知道 itemBuilder() 方法实际上是构建项目,但不确定它是否是更改列表的正确位置。
  • 您可以指定计数(如果您知道)或从小部件构建器返回 null - 类似于 this
  • 堆栈溢出发生在哪里?您可以添加堆栈跟踪吗?我预计会出现某种“内存不足”错误,但不会出现堆栈溢出。
  • @boformer:我附上了屏幕和错误。不知何故,颤动的细节并不多。

标签: flutter


【解决方案1】:

我为a related question about paginated scrolling 编写了一些示例代码,您可以查看。

我没有在那里实现缓存失效,但它可以很容易地在 getPodcast 方法中使用类似下面的东西来删除距离当前位置超过 100 个索引的所有项目:

for (key in _cache.keys) {
  if (abs(key - index) > 100) {
    _cache.remove(key);
  }
}

更复杂的实现可以考虑滚动速度和过去的用户行为,以绘制概率曲线(或更简单的高斯曲线)以更智能地获取内容。

【讨论】:

    猜你喜欢
    • 2017-09-05
    • 2019-07-08
    • 1970-01-01
    • 2015-05-18
    • 1970-01-01
    • 2015-06-29
    • 2020-05-23
    • 2011-10-30
    • 2014-01-26
    相关资源
    最近更新 更多