首页 > 解决方案 > 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);
          },
        ),
      ),
    );
  }
}

无限列表

列表大小应为 1

标签: imageflutterfilegoogle-cloud-firestorestream-builder

解决方案


您正在为 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 。


推荐阅读