在小部件构建方法运行之前,如何在初始值未知的 bool 上使用 setState?火力地堡

How do I use setState on a bool of which the initial value isn't know until after the widget build method runs? Flutter Firebase

对于 post 和评论系统,我有一个响应 class,其中状态字段确定响应是 'published' 还是 'hidden'。

我使用 FirestoreListView 来显示这些响应。如果响应的状态设置为隐藏,则它应该被折叠,否则它应该只显示。为此,我有一个 bool responseVisible。第一次读取响应时,我按如下方式初始化 responseVisible:

bool responseVisible = response.status == 'published';

然后我使用 2 个可见性小部件。 responseVisible为true时正常可见,responseVisible为false时折叠可见

这非常有效。显示状态为已发布的回复,状态为隐藏的回复有回复折叠消息。

但现在我想添加一个按钮来打开折叠的回复。为此,我使用 setState 将 responseVisible 更改为 true。但是由于我在widget build方法中初始化responseVisible,所以setState值一直被初始化值覆盖。

通常,我只是从小部件构建方法中取出 responseVisible bool 并在那里初始化它,但在这种情况下,我不能,因为在小部件构建方法运行之前我不知道响应的状态。

所以现在我卡住了。我如何重写它才能打开折叠的响应?

class Response {
  final String postId;
  final String? responseId;
  final UserProfile author;
  final String content;
  final DateTime datePublished;
  DateTime? dateModified;
  final Map<String, Reply>? replies;
  final String status;
  final String statusContext;

  Response({
    required this.postId,
    this.responseId,
    required this.author,
    required this.content,
    required this.datePublished,
    this.dateModified,
    this.replies,
    required this.status,
    required this.statusContext,
  });
class ResponseStream extends StatefulWidget {
  ResponseStream({Key? key, required this.post, this.height}) : super(key: key);

  final Post post;
  double? height = 400;

  @override
  State<ResponseStream> createState() => _ResponseStreamState();
}

class _ResponseStreamState extends State<ResponseStream> {

  @override
  Widget build(BuildContext context) {
    final responsesCollection = FirebaseFirestore.instance
        .collection('posts')
        .doc(widget.post.postId)
        .collection('responses')
        .orderBy('datePublished', descending: true)
        .withConverter<Response>(
          fromFirestore: (snapshot, _) => Response.fromJson(snapshot.id, snapshot.data()!),
          toFirestore: (responses, _) => responses.toJson(),
        );

    return SizedBox(
      height: widget.height,
      child: FirestoreListView<Response>(
          query: responsesCollection,
          itemBuilder: (context, snapshot) {
            Response response = snapshot.data();
            bool responseVisible = response.status == 'published';

            return ListTile(
              title: Text(
                "${response.author.name} ~ ${fs.format(response.datePublished)}",
              ),
              subtitle: Column(
                children: [
                  Visibility(
                    visible: responseVisible,
                    child: Text(response.content),
                  ),
                  Visibility(
                    visible: !responseVisible,
                    child: Column(
                      children: [
                        Text('This response was collapsed for: ${response.statusContext}.'),
                        GestureDetector(
                          child: const Text("Show Response"),
                          onTap: () {
                            setState(() {
                              responseVisible = !responseVisible;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            );
          }),
    );
  }
}

您是否考虑过将参数“responseVisible”添加到 Response class 本身,将其初始值设置为 false,然后进行修改?

您可以在 ResponseStream 小部件的顶部创建一个地图以覆盖现有的已发布值,其中 postId 映射到可见性。

Map<String,bool> responseVisibility = new Map();

如果地图不包含该 postId,则将存储的可见性设置为默认值,只需查询地图即可。

if(!responseVisibility.containsKey(response.postId)) responseVisibility[response.postId] = response.status == 'published';

bool responseVisible = responseVisibility[response.postId];

最后,要更新可见性,只需更新地图即可。

setState(() {
  responseVisibility[response.postId] = !responseVisibility[response.postId];
});

我最后做的是创建一个名为 ResponseDetail 的单独小部件。这样我就可以继续在主构建方法中初始化 responseVisible,并在其中放置新的 ResponseDetail 小部件,它有自己的带有 setState() 运行 的构建方法。这样,新小部件对 setState 的调用就不会被外部构建方法的初始化值覆盖。

class ResponseDetail extends StatefulWidget {
  ResponseDetail({Key? key, required this.response, required this.responseVisible, required this.ownPost})
      : super(key: key);

  Response response;
  bool responseVisible;
  bool ownPost;

  @override
  State<ResponseDetail> createState() => _ResponseDetailState();
}

class _ResponseDetailState extends State<ResponseDetail> {
  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Visibility(...),
        Visibility(...),
      ],
    );
  }
}

然后在外层构建方法中:

    ... itemBuilder: (context, snapshot) {
            Response response = snapshot.data();
            bool responseVisible = response.status == 'published';    
            return ListTile(
              title: Text("${response.author.name} ~ fs.format(response.datePublished)}"),
              subtitle: ResponseDetail(response: response, responseVisible: responseVisible, ownPost: ownPost),
        ), 
    ...