防止从子小部件重新构建FutureBuilder

huangapple go评论61阅读模式
英文:

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&lt;Deployment&gt;(
      future: FirebaseFirestore.instance
          .collection(&#39;deployments&#39;)
          .where(&quot;users&quot;, arrayContains: user!.email)
          .get()
          .then((value) =&gt; 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(&#39;No deployments found, please contact admin.&#39;),
            ),
          );
        } 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) {
        // …其他内容
      }
    );
  }
}

参考:

英文:

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(&#39;deployments&#39;)
          .where(&quot;users&quot;, arrayContains: user!.email)
          .get()
          .then((value) =&gt; Deployment.fromJson(value.docs[0].data()));

  @override
  Widget build(BuildContext context) {
    return FutureBuilder&lt;Deployment&gt;(
      future: constructed_future,
      builder: (context, snapshot) {
// … other stuff
}

Reference :

huangapple
  • 本文由 发表于 2023年6月22日 15:42:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76529609.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定