抖动无限/长列表-内存问题和堆栈溢出错误

时间:2018-12-31 13:59:20

标签: flutter

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

这种方法是可以的,但是如果用户继续滚动页面,则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

某种程度上,颤振不再显示该错误的更多细节。

1 个答案:

答案 0 :(得分:0)

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

我没有在此处实现缓存无效化,但是可以使用getPodcast方法中的以下类似内容轻松地将其扩展:从当前位置删除所有索引超过100个的项目:

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

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