splatk
splatk

Reputation: 11

Best practice of async call on flutter?

I don't want the async call fire every time when the widget rebuild. So, I call the async function in initState.

  1. call async in initState

    @override
    void initState() {
      someAsyncCall().then((result) {
        setState() {
         _result = result;
        }
      });
    }
    
    @override
    Widget build(BuildContext context) {
      if (_result == null) {
        reutrn new Container();
      }
    
      return new SomeWidget();
    }
    
  2. use FutureBuilder

    @override
    void initState() {
      _future = someAsyncCall();
    }
    
    @override
    Widget build(BuildContext context) {
      return new FutureBuilder(
        future: _future,
        builder: // do something
      );
    }
    

Is there any side effect or bad practice on both solution?


I modify the demo app from flutter to explain why I call the async in initSate.
1. open app, print main
2. push screen to test, print test, main
3. pop test, print main

If I call async function in build, it calls three times.
What I want is, I need the async function call once, unless the main dispose / pop.
According to the answer from Rémi Rousselet, call async in initState is kind of a problem or wrong.
So, how can I make sure the async function call once?

   @override
   Widget build(BuildContext context) {
    print("build main");
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new RaisedButton(onPressed: () {
              Navigator.push(context, new MaterialPageRoute(builder: (context) {
                return new Test();
              }));
            }),
          ],
        ),
      )
    );
  }

 class Test extends StatelessWidget {

   @override
   Widget build(BuildContext context) {
     print("build Test");
     return new Scaffold(
       body:
       new FlatButton(onPressed: () {
         Navigator.pop(context);
       }, child: new Text("back"))
     );
   }

 }

Do I need to do something like

@override
Widget build(BuildContext context) {
  if (!mounted) {
    print("build main");
    // aysnc function call here
  }
}

Upvotes: 1

Views: 3356

Answers (2)

Smily
Smily

Reputation: 3100

I would choose the first style as I can control what happens whilst waiting. It depends on what you want from your app, and I prefer a clear "loading" message rather than not knowing if it is stuck or loading.

And speaking of side effects, you can see what they said about FutureBuilder

A side-effect of this is that providing a new but already-completed future to a FutureBuilder will result in a single frame in the ConnectionState.waiting state. This is because there is no way to synchronously determine that a Future has already completed.

Upvotes: 0

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277667

While slightly more verbose, the FutureBuilder definitely is the way to go.

Calling async function within the initState and then doing a setState when they finish has a few problems. Most of the time while doing this, you won't bother about error catching or handling the loading time.

Bear in mind that Flutter expects initState to be synchronous. So while dart doesn't prevent you from doing async calls into sync functions, flutter will most likely think your widget is ready when it's not.

Upvotes: 2

Related Questions