Home > Software design >  Flutter - FutureBuilder fires twice on hot reload
Flutter - FutureBuilder fires twice on hot reload

Time:11-15

In my flutter project when I start the project in the simulator everything works fine and the future builder only fires once, but when I do hot reload the FutureBuilder fires twice which causes an error any idea how to fix this?

  Future frameFuture()  async {
    var future1 = await AuthService.getUserDataFromFirestore();
    var future2 = await GeoService.getPosition();
    return [future1, future2];
  }

  @override
  void initState() {
    user = FirebaseAuth.instance.currentUser!;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: frameFuture(),
        builder: (context, snap) {
          if (snap.connectionState == ConnectionState.done && snap.hasData) return HomePage();
          else return Container(
            color: Colors.black,
            child: Center(
              child: spinKit,
            ),
          );
        }
    );
  }

CodePudding user response:

I solved the issue. I put the Future function in the initState and then used the variable in the FutureBuilder. I'm not sure why it works this way, but here's the code:

  var futures;

  Future frameFuture()  async {
    var future1 = await AuthService.getUserDataFromFirestore();
    var future2 = await GeoService.getPosition();
    return [future1, future2];
  }

  @override
  void initState() {
    user = FirebaseAuth.instance.currentUser!;
    super.initState();
    futures = frameFuture();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: futures,
        builder: (context, snap) {
          if (snap.connectionState == ConnectionState.done && snap.hasData) return HomePage();
          else return Container(
            color: Colors.black,
            child: Center(
              child: spinKit,
            ),
          );
        }
    );
  }

CodePudding user response:

The solution as you already figured out is to move the future loading process to the initState of a StatefulWidget, but I'll explain the why it happens: You were calling your future inside your build method like this:

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: frameFuture(),

The issue is that Flutter calls the build method each time it renders the Widget, whenever a dependency changes(InheritedWidget, setState) or Flutter decides to rebuild it. So each time you redraw your UI frameFuture() gets called, this makes your build method to have side effects (this async call) which it should not, and is encouraged for widgets not to have side effects.

By moving the async computation to the initState you're only calling it once and then accessing the cached variable futures from your state.

As a plus here is an excerpt of the docs of the FutureBuilder class

"The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted."

Hope this makes clear the Why of the solution.

  • Related