【问题标题】:Stream Builder causing infinite loop flutterStream Builder 导致无限循环抖动
【发布时间】:2021-06-22 06:55:54
【问题描述】:

我已将数据存储在 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);
          },
        ),
      ),
    );
  }
}

Infinite list

List size should be 1

【问题讨论】:

    标签: image flutter file google-cloud-firestore stream-builder


    【解决方案1】:

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

    【讨论】:

    • 那么请告诉我在我的代码中要做什么。我真的需要它。不过我会看视频。
    • 正如它所说的“未来一定是更早获得的,例如在 State.initState、State.didUpdateWidget 或 State.didChangeDependencies 期间。”。
    • 但是当我将每个项目添加到列表中时,这将如何发生,并且它需要是动态的。数据由 firestore 收集,然后除了 base64 字符串之外的所有数据都转换为图像,然后转换为文件需要一个未来。
    • 随着流值的变化,流生成器会重新唤醒。
    • 那么我应该如何修改代码呢?在这行代码之后:final potholes = snapshot.data.docs;每个列表项都是从firestore中检索的,base64字符串到图像到文件的转换需要一个future函数,而这需要发生在for循环中的每个项目上,那么我应该如何处理代码呢?
    猜你喜欢
    • 2020-08-27
    • 2021-05-07
    • 2012-08-24
    • 2020-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多