Flutter - 无限滚动和图像导致应用程序崩溃

我们正在 Flutter 中构建产品列表页面,并尝试在用户滚动并到达页面末尾时加载产品。它基本上是一个带有网络图像的网格视图构建器。当我使用配置文件模式运行应用程序时,一旦屏幕上有大约 1300 个图像,应用程序就会崩溃,如果我们使用发布模式运行应用程序,一旦屏幕上有大约 5000 个图像,应用程序就会崩溃。 Flutter GC不应该启动,以便不再将不在视图中的图像保存在内存中吗?

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Infinite Scroll Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Infinite Scroll Crash Sample'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController scrollController;
  bool autoReloadInProgress = false;
  int noOfElements = 50;
  String baseUrl = "https://cdn.shopify.com/s/files/1/0286/0344/9475/products/";

  // short list, actual list has thousands of image urls
  List<String> images = [
    "imagejpg_1024x1024_2x_032a1436-7cb4-42de-9289-ea58b510d750.jpg?v=1579293459",
    "Raven_Brow_Balm_1024x_fa88c686-8c89-4647-babb-440ef8292d5b.png?v=1577445154",
    "BioTuff_GeneralUseBinLinersMed25_8_1024_1024.jpg?v=1587709003",
    "34eyeshadow_neutralnude_1024x1024_2x_08d10c39-1c3f-431c-b03b-436f2ff55cfa.png?v=1579411298",
    "Ballet_1024x1024_a61af758-0757-454e-938f-96065d0c5e40.jpg?v=1581403220",
    "pure-organic-beeswax-tealight-refills.png?v=1586942441",
    "Holding_Cherry_Red_2000x2000_d6ce67a2-e740-4a99-b599-86196146400b.png?v=1577858762",
    "WCP-FNS1-004_1024x1024_2x_43f2e247-eecb-4a56-89c6-c3ecc8e3031e.jpg?v=1579295821",
    "BabyWipes20sfront2D_1024x_01ad5c64-1ff6-402c-9995-5b7489555029.jpg?v=1587698137",
    "imageedit_214_7359687840_1.png?v=1577437619",
    "Organic_Chocolate_Maple_Pecan_1800x_10844ae0-d26c-4946-ab8b-d8c8d166b37b.png?v=1577863201",
    "FACIAL_POLISH_1_1000x_87f987c5-95da-4fe9-9a83-0f8a3d4cd74a.jpg?v=1581373634",
    ];
  @override
  void initState() {
    super.initState();
    scrollController = new ScrollController()..addListener(_scrollListener);
  }

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

  void _scrollListener() async{
    if(scrollController.position.extentAfter < 800) {
      setState(() {
        noOfElements += 50;
      });
    }
  }

  getProductGrid(){

    double deviceWidth = MediaQuery.of(context).size.width;
    double aspectRatio = 0.64;
    double cardWidth = deviceWidth < 200.0 ? deviceWidth : (deviceWidth <= 375 ? 180 : 200);
    double cellHeight = cardWidth / aspectRatio;
    int cardCount = (deviceWidth/cardWidth).toInt();
    double extraWidth = deviceWidth - (cardWidth * cardCount);
    return GridView.builder(
      itemCount: noOfElements,
      padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
      primary: false,
      shrinkWrap: true,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: cardCount, crossAxisSpacing: extraWidth / (cardCount-1),
          childAspectRatio: aspectRatio, mainAxisSpacing: 20
      ),
      itemBuilder: (BuildContext context, int index){
        return Container(
          //height: 200,
          //width: 200.0,
          color: Theme.of(context).accentColor,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text((index+1).toString(), textAlign: TextAlign.center, style: TextStyle(color: Colors.white),),
              Image.network(baseUrl + images[index%images.length]),
            ],
          )
        );
      },
    );

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Scrollbar(
          child:ListView(
            controller: scrollController,
            padding: EdgeInsets.all(10),
            children: <Widget>[
              Column(
                children: <Widget>[
                  getProductGrid(),
                ],
              ),
            ],
          )
      )
    );
  }
}

已添加示例代码的链接,因为它有很多图像源,并且不符合帖子的字符限制。对这个问题有任何见解吗?

https://drive.google.com/file/d/1vHqawSw18HSWP-YV_39_fFfo_IUGgQPv/view?usp=sharing

更新 - 工作代码

Scrollbar(
        child:CustomScrollView(
          controller: scrollController,
          physics: AlwaysScrollableScrollPhysics(),
          slivers: [
            SliverList(
             delegate: new SliverChildListDelegate([
                  //widgets go here
             ])
            )
          ]
       )
);
stack overflow Flutter - Infinite Scroll & Images causes App Crash
原文答案

答案:

作者头像

ListView and GridView is just an implementation of Slivers. However, if you'd like to implement a custom ScrollView, you'd need to use Slivers. The way Slivers work is when child widgets are out of site from the viewport, elements and render objects are destroyed along with their states. A workaround that could work for this issue is to create a CustomScrollView using Slivers.