我有一个 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;
}
任何我如何改进这一点的提示/提示将不胜感激。
谢谢!
必须分页才能顺利执行 只需一次性加载 10 个文档即可 滚动控制器的帮助检查您是否位于列表末尾 然后加载接下来的 10 个文档 高效的方式.
使用 sliversList 小部件代替“listview”。 请参阅 sliversList 和 sliverscomponents 的示例此处
我认为@AmitSingh的建议是最好的,但如果你想加载一次数据,那么你可以在分页中获取数据,但不是在用户滚动时获取数据,而是在你获取第一组数据时获取数据。
是的,你应该使用分页或延迟加载!对于大多数移动设备来说,一次读取和呈现 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()
]),
);
}
}
在这种情况下,有很多方法可以提高性能:
每次小部件重建时都会执行该函数。确保使用 StatelessWidget 或 Stateful widget 来代替 const 关键字。
对于流使用StreamBuilder小部件,您可以为其分配一个future函数,因此它只会在数据更改时更新小部件。
尽可能使用不带 shrinkWrap
这样使用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 发生更改时都会重建小部件,从而使渲染视图的速度变慢。