在小部件构建方法运行之前,如何在初始值未知的 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),
),
...
对于 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),
),
...