Flutter 应用程序性能问题与长列表

问题描述 投票:0回答:5

我有一个 flutter 应用程序,用户可以将项目添加到存储在 firebase 中的列表中。用户一次最多可以添加 1000 个项目。最初这不是问题,但随着列表项数量的增加,应用程序变得越来越慢,直到列表中大约有 1000 个项目后一次添加多个项目时,它会由于内存使用而导致应用程序崩溃 -

线程 #10,名称 = 'io.flutter.1.ui',停止原因 = EXC_RESOURCE RESOURCE_TYPE_MEMORY(限制 = 1450 MB,未使用 = 0x0)

如何改进代码以提高性能。我想保留 Stream 的设置,因为它可以让我动态地动态过滤列表。这里还有一个信息是 WidgetA 和 WidgetB 也都使用流数据来显示列表中的列表项的数量。

这是我的代码,为了便于阅读而进行了一些简化:

主屏幕类别:

Widget content(context) {
double h = MediaQuery.of(context).size.height; //screen height
double w = MediaQuery.of(context).size.width; //screen width



return StreamProvider<List<Activity>>.value(
    catchError: (_, __) => null,
    value: DatabaseService().activities(widget.uid),
    builder: (context, snapshot) {
      return SafeArea(
        child: Container(
          //color: Theme.of(context).backgroundColor, //SkyHookTheme.background,
          child: Scaffold(
            backgroundColor: Colors.transparent,
            body: NotificationListener<ScrollNotification>(
              onNotification: _handleScrollNotification,
              child: Stack(children: [
                ListView(
                  controller: _scrollController,
                  children: <Widget>[
                    Column(
                      children: <Widget>[
                   
                        WidgetA(),
                        WidgetB(),
                        ActivityList(),    //List of User Activities

                      ],
                    )
                  ],
                ),
              
              ]),
            ),
          ),
        ),
      );
    });
}

ActivityList类Listview构建:

ListView buildList(List<Activity> acts){
items = ListView.builder(
  shrinkWrap: true,
  physics: ClampingScrollPhysics(),
  scrollDirection: Axis.vertical,
  itemCount: len,
  itemBuilder: (context, index) {
    return ActivityTile(activity: acts[index], number: acts.length - (index));
  },
);


return items;

}

任何我如何改进这一点的提示/提示将不胜感激。

谢谢!

firebase performance flutter listview
5个回答
0
投票

必须分页才能顺利执行 只需一次性加载 10 个文档即可 滚动控制器的帮助检查您是否位于列表末尾 然后加载接下来的 10 个文档 高效的方式.


0
投票

使用 sliversList 小部件代替“listview”。 请参阅 sliversList 和 sliverscomponents 的示例此处


0
投票

我认为@AmitSingh的建议是最好的,但如果你想加载一次数据,那么你可以在分页中获取数据,但不是在用户滚动时获取数据,而是在你获取第一组数据时获取数据。


0
投票

是的,你应该使用分页或延迟加载!对于大多数移动设备来说,一次读取和呈现 1000 个文档的工作量太大。 相反,你应该像这样加载你的文档

import 'package:cloud_firestore/cloud_firestore.dart';  
Firestore firestore = Firestore.instance

class LongList extends StatefulWidget {

@覆盖 _LongListState createState() => _LongListState(); }

class _LongListState extends State<LongList> {

List<DocumentSnapshot> products = []; // stores fetched products  
bool isLoading = false; // track if products fetching  
bool hasMore = true; // flag for more products available or not  
int documentLimit = 10; // documents to be fetched per request  
DocumentSnapshot lastDocument; // flag for last document from where next   10 records to be fetched  
ScrollController _scrollController = ScrollController(); // listener for    listview scrolling

getProducts() async {  
       if (!hasMore) {  
        print('No More Products');  
        return;  
       }  
   if (isLoading) {  
     return;  
   }  
   setState(() {  
     isLoading = true;  
   });  
 QuerySnapshot querySnapshot;  
   if (lastDocument == null) {  
     querySnapshot = await firestore  
         .collection('products')  
         .orderBy('name')  
         .limit(documentLimit)  
         .getDocuments();  
   } else {  
     querySnapshot = await firestore  
         .collection('products')  
         .orderBy('name')  
         .startAfterDocument(lastDocument)  
         .limit(documentLimit)  
         .getDocuments();  
     print(1);  
   }  
   if (querySnapshot.documents.length < documentLimit) {  
     hasMore = false;  
   }  
   lastDocument = querySnapshot.documents[querySnapshot.documents.length - 1];  
   products.addAll(querySnapshot.documents);  
   setState(() {  
     isLoading = false;  
   });  
   }  

  void initState(){
  getProducts();
  _scrollController.addListener(() {
  double maxScroll = _scrollController.position.maxScrollExtent;
  double currentScroll = _scrollController.position.pixels;
  double delta = MediaQuery.of(context).size.height * 0.20;
  if (maxScroll - currentScroll <= delta) {
    getProducts();
  }
  });
  _pageManager = PageManager();

  super.initState();
  }  
  Widget build(BuildContext context) {  
   return Scaffold(  
     appBar: AppBar(  
       title: Text('Flutter Pagination with Firestore'),  
     ),  
     body: Column(children: [  
       Expanded(  
         child: products.length == 0  
             ? Center(  
                 child: Text('No Data...'),  
               )  
             : ListView.builder(  
                 controller: _scrollController,  
                 itemCount: products.length,  
                 itemBuilder: (context, index) {  
                   return ListTile(  
                     contentPadding: EdgeInsets.all(5),  
                     title: Text(products[index]['name']),  
                     subtitle: Text(products[index]  ['short_desc']),  
                   );  
                 },  
               ),  
       ),  
       isLoading  
           ? Container(  
               width: MediaQuery.of(context).size.width,  
               padding: EdgeInsets.all(5),  
               color: Colors.yellowAccent,  
               child: Text(  
                 'Loading',  
                 textAlign: TextAlign.center,  
                 style: TextStyle(  
                   fontWeight: FontWeight.bold,  
                 ),  
               ),  
             )  
           : Container()  
     ]),  
   );  
 }  

}


0
投票

在这种情况下,有很多方法可以提高性能:

  1. 每次小部件重建时都会执行该函数。确保使用 StatelessWidget 或 Stateful widget 来代替 const 关键字

  2. 对于流使用StreamBuilder小部件,您可以为其分配一个future函数,因此它只会在数据更改时更新小部件。

  3. 尽可能使用不带 shrinkWrap

     属性的 ListView.builder。使用shrinkWrap 使ListView 的行为类似于Column,这意味着视图不会被回收。该框架必须计算屏幕上每个小部件的大小,即使您没有查看它,也会呈现整个列表。

  4. 这样使用MediaQuery

Google I/O 2024 上宣布,随着 Flutter 3.22 的推出,建议使用:

MediaQuery.sizeOf(context).width;
MediaQuery.sizeOf(context).height;

代替:

MediaQuery.of(context).size.width;
MediaQuery.of(context).size.height;

sizeOf(context)
每当属性大小发生更改时都会重建,而
of(context.size)
每当 MediaQuery 发生更改时都会重建小部件,从而使渲染视图的速度变慢。

参考:https://www.youtube.com/watch?v=LeKLGzpsz9I&t=13m30s

© www.soinside.com 2019 - 2024. All rights reserved.