image - Stream Builder 导致无限循环抖动
问题描述
我已经在firestore上存储了数据,但是当我检索它时。它导致无限循环。我列表中的项目数量不断增加。
class PotHolesList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final potholesData = Provider.of<PotHoles>(context);
Future<File> _createFileFromString(String encodedStr) async {
Uint8List bytes = base64Decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File(
"$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + ".png");
await file.writeAsBytes(bytes);
return file;
}
return StreamBuilder(
stream: _firestore.collection('potholes').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {...}
final potholes = snapshot.data.docs;
for (var pt in potholes) {
final id = pt['id'];
final address = pt['address'];
final latitude = pt['latitude'];
final longitude = pt['longitude'];
var image;
var P;
_createFileFromString(pt['image']).then((value) {
image = value;
P = PotHole(
id: id,
currentPosition: Position.fromMap(
{'latitude': latitude, 'longitude': longitude}),
address: address,
image: image,
);
potholesData.addPothole(P);
});
print(potholesData.items.length);
}
return ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: potholesData.items.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: potholesData.items[i],
child: PotHoleItem(),
),
);
},
);
}
}
我已经尝试将图像 base64 字符串从 firestore 转换为 Flutter 中的文件类型,因为它是未来,我必须等待,然后进行其余的添加。
这是更新的代码,但它仍然提供无限循环,列表项的长度不断增加。
class PotHolesList extends StatelessWidget {
final _stream = _firestore.collection('potholes').snapshots();
Future<File> _createFileFromString(String encodedStr) async {
Uint8List bytes = base64Decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File(
"$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + ".png");
await file.writeAsBytes(bytes);
return file;
}
@override
Widget build(BuildContext context) {
final potholesData = Provider.of<PotHoles>(context);
return StreamBuilder(
stream: _stream,
// ignore: missing_return
builder: (context, snapshot) {
if (!snapshot.hasData) {...}
final potholes = snapshot.data.docs;
for (var pt in potholes) {
final id = pt['id'];
final address = pt['address'];
final latitude = pt['latitude'];
final longitude = pt['longitude'];
var image;
var P;
_createFileFromString(pt['image']).then((value) {
image = value;
P = PotHole(
id: id,
currentPosition: Position.fromMap(
{'latitude': latitude, 'longitude': longitude}),
address: address,
image: image,
);
potholesData.addPothole(P);
});
print(potholesData.items.length);
}
return ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: potholesData.items.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: potholesData.items[i],
child: PotHoleItem(),
),
);
},
);
}
}
这就是 PotHoleItem 代码:
class PotHoleItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final pothole = Provider.of<PotHole>(context, listen: false);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 14.0),
child: Container(
decoration: BoxDecoration(
color: Color(0xFFd8e2dc),
borderRadius: BorderRadius.circular(15.0)),
child: ListTile(
trailing: Icon(pothole.isFixed ? Icons.check : Icons.clear),
title: Row(
// mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.symmetric(vertical: 5.0),
// height: double.infinity,
alignment: Alignment.center,
child: Image.file(
pothole.Image,
fit: BoxFit.contain,
width: 65,
height: 65,
),
),
SizedBox(
width: 10.0,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"LAT: ${pothole.Latitude}, LONG: ${pothole.Longitude}",
style: TextStyle(
fontSize: 14.0, fontWeight: FontWeight.bold),
),
SizedBox(height: 10.0),
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: Text(
"${pothole.Address}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.0, fontWeight: FontWeight.normal),
),
),
],
),
),
],
),
onTap: () {
Navigator.of(context)
.pushNamed(PotHoleDetailScreen.routeName, arguments: pothole);
},
),
),
);
}
}
解决方案
您正在为 future: 参数调用 FutureBuilder 创建未来。不要那样做。FutureBuilder的文档说:
未来必须更早获得,例如在 State.initState、State.didUpdateWidget 或 State.didChangeDependencies 期间。在构造 FutureBuilder 时,不能在 State.build 或 StatelessWidget.build 方法调用期间创建它。如果future与FutureBuilder同时创建,那么每次FutureBuilder的parent重建时,异步任务都会重新启动。
一般准则是假设每个构建方法都可以在每一帧被调用,并将省略的调用视为优化。
我有一个很好的 10 分钟截屏视频来说明这一点:https ://www.youtube.com/watch?v=sqE-J8YJnpg 。
推荐阅读
- flask - 检查 JWT ALG
- javascript - 如何使用 ajax json 响应更新购物车
- matlab - 绘图函数不显示两个向量的除法结果
- python - 在我的答题器游戏中添加粒子效果
- ios - 如何将 SpriteKit Sprite 节点的可点击区域从矩形更改为圆形?
- javascript - 使用 TeaVM 将 java 库移植到 wasm
- javascript - 使用 javascript 检查段落中的 ClassName 是否等于按钮中的 TextContent
- snowflake-cloud-data-platform - 无法取消查询
- scala - 在 scala 中,是否可以阻止编译器首先搜索 Predef 隐式?
- javascript - 如何将数字数组转换为仅在单击加号图标时出现的列表?