Flutter:Future 不断重建,因为它在流中被调用。如果可能的话,如何让它再次调用?

Flutter: Future keeps rebuilding because it is being called inside a stream. How to only make it call again, if possible?

所以我想显示歌曲列表,但是显示歌曲的 Uint8List 图稿的 future 是从 future 调用的。代码有效,但专辑封面看起来好像有问题,因为它不断被调用。我不知道如何解决这个问题,我尝试了很多解决方案。请帮忙。

这是我的代码:

StreamBuilder<List<SongInfo>>(
          stream: widget.songs,
          builder: (context, snapshot) {
            if (snapshot.hasError)
              return Utility.createDefaultInfoWidget(Text("${snapshot.error}"));

            if (!snapshot.hasData)
              return Utility.createDefaultInfoWidget(
                  CircularProgressIndicator());

            return (snapshot.data.isEmpty)
                ? NoDataWidget(
                    title: "There is no Songs",
                  )
                : Column(
                    children: [
                      Container(
                        padding:
                            EdgeInsets.symmetric(vertical: 10, horizontal: 15),
                        alignment: Alignment.centerRight,
                        child: Text("${snapshot.data.length} Songs"),
                      ),
                      Expanded(
                        child: ListView.builder(
                          shrinkWrap: true,
                          itemCount: snapshot.data.length,
                          itemBuilder: (context, songIndex) {
                            SongInfo song = snapshot.data[songIndex];
                            return ListItemWidget(
                              title: Text("${song.title}"),
                              subtitle: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                mainAxisAlignment:
                                    MainAxisAlignment.spaceAround,
                                children: <Widget>[
                                  Text("Artist: ${song.artist}"),
                                  Text(
                                    "Duration: ${Utility.parseToMinutesSeconds(int.parse(song.duration))}",
                                    style: TextStyle(
                                        fontSize: 14.0,
                                        fontWeight: FontWeight.w500),
                                  ),
                                ],
                              ),
                              trailing: (widget.addToPlaylistAction == true)
                                  ? IconButton(
                                      icon: Icon(Icons.playlist_add),
                                      onPressed: () {
                                        showDialog(
                                            context: context,
                                            builder: (context) {
                                              return AlertDialog(
                                                title: Text(_dialogTitle),
                                                content: FutureBuilder<
                                                        List<PlaylistInfo>>(
                                                    future: model.getPlayList(),
                                                    builder:
                                                        (context, snapshot) {
                                                      if (snapshot.hasError) {
                                                        print("has error");
                                                        return Utility
                                                            .createDefaultInfoWidget(
                                                                Text(
                                                                    "${snapshot.error}"));
                                                      }

                                                      if (snapshot.hasData) {
                                                        if (snapshot
                                                            .data.isEmpty) {
                                                          print("is Empty");
                                                          return NoDataWidget(
                                                            title:
                                                                "There is no playlists",
                                                          );
                                                        }

                                                        return PlaylistDialogContent(
                                                          options: snapshot.data
                                                              .map((playlist) =>
                                                                  playlist.name)
                                                              .toList(),
                                                          onSelected: (index) {
                                                            snapshot.data[index]
                                                                .addSong(
                                                                    song: song);
                                                            Navigator.pop(
                                                                context);
                                                          },
                                                        );
                                                      }

                                                      print("has no data");
                                                      return Utility
                                                          .createDefaultInfoWidget(
                                                              CircularProgressIndicator());
                                                    }),
                                              );
                                            });
                                      },
                                      tooltip: "Add to playlist",
                                    )
                                  : Container(
                                      width: .0,
                                      height: .0,
                                    ),
                              leading: song.albumArtwork == null
                                  ? FutureBuilder<Uint8List>(
                                      future: model.audioQuery.getArtwork(
                                          type: ResourceType.SONG,
                                          id: song.id,
                                          size: Size(100, 100)),
                                      builder: (context, snapshot) {
                                        SchedulerBinding.instance
                                            .addPostFrameCallback(
                                                (_) => setState(() {
                                                      isServiceError = false;
                                                      isDataFetched = true;
                                                    }));
                          
                                        if (snapshot.data.isEmpty)
                                          return CircleAvatar(
                                            backgroundImage: AssetImage(
                                                "assets/images/title.png"),
                                          );
                                        if (isDataFetched) {
                                          return CircleAvatar(
                                            backgroundColor: Colors.transparent,
                                            backgroundImage: MemoryImage(
                                              snapshot.data,
                                            ),
                                          );
                                        } else {
                                          return CircleAvatar(
                                            child: CircularProgressIndicator(),
                                          );
                                        }
                                      })
                                  : CircleAvatar(
                                      backgroundImage: FileImage(
                                          IO.File(song?.albumArtwork)),
                                    ),
                            );
                          },
                        ),
                      ),
                    ],
                  );
          },

我不希望使用 post-frame 回调在未来的构建器或流构建器中设置状态。原因是你基本上要求 flutter 在下一帧中再次构建小部件,同时构建当前的小部件,递归地将整个事情设置为一个循环。如果您需要那些 isServiceError 和 isDataFetched 标志,也许您可​​以创建一个新的有状态小部件并在 initState 中手动执行加载任务。

您当前代码中的问题似乎与以下方面有关:

SchedulerBinding.instance
    .addPostFrameCallback((_) => setState(() {
        isServiceError = false;
        isDataFetched = true;
}));

在未来的构建器中调用。每次设置状态时,都会在重建小部件时再次调用相同的代码,从而形成一个循环,在这个循环中,整个事情会一次又一次地不必要地构建。

您可以通过在分配 post 帧回调之前检查标志来避免它,如下所示:

if(!isDataFetched)
{
     SchedulerBinding.instance
    .addPostFrameCallback((_) => setState(() {
        isDataFetched = true;
    }));
}

因此在下一帧中,isDataFetched 将为真,因此不再有 post 帧回调。


然而,这个解决方案并不是一个真正合适的解决方案,因为正如我上面提到的,在未来的构建器中使用 post-frame 回调设置状态不是一个好主意。如果你不需要未来构建器之外的那些标志,你应该简单地避免它们并依赖构建器本身内部的 snapshot.hasData 和 snapshot.hasError。