英文:
Prevent rebuilding of FutureBuilder from child widgets
问题
为了展示从Firestore加载数据,我创建了这个小部件:
class GetDeploymentDetails extends StatelessWidget {
const GetDeploymentDetails({required this.user, super.key});
final User? user;
@override
Widget build(BuildContext context) {
return FutureBuilder<Deployment>(
future: FirebaseFirestore.instance
.collection('deployments')
.where("users", arrayContains: user!.email)
.get()
.then((value) => Deployment.fromJson(value.docs[0].data())),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: const Center(
child: CircularProgressIndicator(),
),
);
} else if (!snapshot.hasData) {
return Scaffold(
body: const Center(
child: Text('未找到部署,请联系管理员。'),
),
);
} else
return AppHomeScreen(
user: user,
deployment: snapshot.data!,
);
},
);
}
}
但每次我进行需要在AppHomeScreen中重新构建的UI更改时,整个UI都会重新加载,并将我带回第一页。例如,点击文本字段以输入一些文本会再次调用AppHomeScreen的build方法并重新加载整个应用程序的数据。
我该如何修复这个问题?以及在加载小部件之间调用futures和streams的正确方式是什么,同时能够显示数据正在加载的circularprogressindicator。
英文:
To show loading of data from firestore, I created this widget:
class GetDeploymentDetails extends StatelessWidget {
const GetDeploymentDetails({required this.user, super.key});
final User? user;
@override
Widget build(BuildContext context) {
return FutureBuilder<Deployment>(
future: FirebaseFirestore.instance
.collection('deployments')
.where("users", arrayContains: user!.email)
.get()
.then((value) => Deployment.fromJson(value.docs[0].data())),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: const Center(
child: CircularProgressIndicator(),
),
);
} else if (!snapshot.hasData) {
return Scaffold(
body: const Center(
child: Text('No deployments found, please contact admin.'),
),
);
} else
return AppHomeScreen(
user: user,
deployment: snapshot.data!,
);
},
);
}
}
But everytime I'm making a UI change that requires rebuilding in the AppHomeScreen, the whole UI reloads and takes me back to the first page. For example, tapping on a textfield to enter some text, calls the build method of AppHomeScreen again and reloads the data of the entire app.
How can I fix this? And what is the correct way to call futures and streams in between loading of widgets while being able to show that data is loading with a circularprogressindicator.
答案1
得分: 1
正如@Randal Schwartz在评论和他分享的视频中提到的,不要在构建Widget内部构建Stream或Future Builder,因为每次状态更改时都会重新构建。
相反,您应考虑将构建的Future移动到一个类实例变量中,如下所示:
class GetDeploymentDetails extends StatelessWidget {
const GetDeploymentDetails({required this.user, super.key});
final User? user;
final constructed_future = FirebaseFirestore.instance
.collection('deployments')
.where("users", arrayContains: user!.email)
.get()
.then((value) => Deployment.fromJson(value.docs[0].data()));
@override
Widget build(BuildContext context) {
return FutureBuilder<Deployment>(
future: constructed_future,
builder: (context, snapshot) {
// …其他内容
}
);
}
}
参考:
- 感谢@Randal Schwartz发布的这个视频修复常见的FutureBuilder和StreamBuilder问题
英文:
As @Randal Schwartz mentioned in the comments and the video he shared, do not build the Stream or Future Builder inside the build Widget as it will rebuild every time when a state changes.
Instead you should consider moving the constructed Future to a class instance variable as follows :
class GetDeploymentDetails extends StatelessWidget {
const GetDeploymentDetails({required this.user, super.key});
final User? user;
final constructed_future = FirebaseFirestore.instance
.collection('deployments')
.where("users", arrayContains: user!.email)
.get()
.then((value) => Deployment.fromJson(value.docs[0].data()));
@override
Widget build(BuildContext context) {
return FutureBuilder<Deployment>(
future: constructed_future,
builder: (context, snapshot) {
// … other stuff
}
Reference :
- Thanks to this video Fixing a common FutureBuilder and StreamBuilder problem posted by @Randal Schwartz
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论