Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to load async data on InitState method?

Tags:

flutter

dart

People also ask

Can I use async in initState?

Sometimes, you may need to execute async code while initializing app. But Flutter will show an error if you add 'async' modifier to initState.

How do you call async from initState?

Another method would be to create an async method and call it from your initState( ) a method is shown below: @override void initState() { super. initState(); asyncMethod(); } void asyncMethod() async { await asyncCall1(); await asyncCall2(); // .... }

Can I make initState async Flutter?

As a brief note, sometimes in Flutter (and Dart) you have to write a method/function that makes an asynchronous call, but the method can't be marked async itself. The Flutter initState method is a good example of this.

What is initState method in Flutter?

initState() is a method of class State and it is considered as an important lifecycle method in Flutter. initState() is called only Once and we use it for one time initializations. Example : To initialize data that depends on the specific BuildContext .


You can create an async method and call it inside your initState

       @override
        void initState () {
          super.initState();
          WidgetsBinding.instance.addPostFrameCallback((_){
            _asyncMethod();
          });

        }

        _asyncMethod() async {
         _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account)     {
            setState(() {
              _currentUser = account;
            });
          });
          _googleSignIn.signInSilently();
        }

As of now using .then notation seems to work:

  // ...
  @override
  initState() {
    super.initState();
    myAsyncFunction
    // as suggested in the comment
    // .whenComplete() {
    // or
      .then((result) {
    print("result: $result");
    setState(() {});
    });
  }
  //...

Method 1 : You can use StreamBuilder to do this. This will run the builder method whenever the data in stream changes.

Below is a code snippet from one of my sample projects:

StreamBuilder<List<Content>> _getContentsList(BuildContext context) {
    final BlocProvider blocProvider = BlocProvider.of(context);
    int page = 1;
    return StreamBuilder<List<Content>>(
        stream: blocProvider.contentBloc.contents,
        initialData: [],
        builder: (context, snapshot) {
          if (snapshot.data.isNotEmpty) {
            return ListView.builder(itemBuilder: (context, index) {
              if (index < snapshot.data.length) {
                return ContentBox(content: snapshot.data.elementAt(index));
              } else if (index / 5 == page) {
                page++;
                blocProvider.contentBloc.index.add(index);
              }
            });
          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        });
  }

In the above code StreamBuilder listens for any change in contents, initially its an empty array and shows the CircularProgressIndicator. Once I make API call the data fetched is added to contents array, which will run the builder method.

When the user scrolls down, more content is fetched and added to contents array which will again run builder method.

In your case only initial loading will be required. But this provides you an option to display something else on the screen till the data is fetched.

Hope this is helpful.

EDIT:

In your case I am guessing it will look something like shown below:

StreamBuilder<List<Content>>(
        stream: account, // stream data to listen for change
        builder: (context, snapshot) {
            if(account != null) {
                return _googleSignIn.signInSilently();
            } else {
                // show loader or animation
            }
        });

Method 2: Another method would be to create an async method and call it from you initState() method like shown below:

 @override
  void initState() {
    super.initState();
    asyncMethod();
  }

  void asyncMethod() async {
    await asyncCall1();
    await asyncCall2();
    // ....
  }

Create anonymous function inside initState like this:

@override
void initState() {
  super.initState();
  
  // Create anonymous function:
  () async {
    await _performYourTask();
    setState(() {
     // Update your UI with the desired changes. 
    });
  } ();
}

Previous Answer!!

You can set a Boolean value like loaded and set it to true in your listen function and make your build function return your data when loaded is set to true otherwise just throw a CircularProgressIndicator

Edited -- I would not suggest calling setState in a method you call in initState. If the widget is not mounted while the setState is called (as the async operation completes) an error will be reported. I suggest you use a package after_layout

Take a look at this answer for better understanding setState in initState : https://stackoverflow.com/a/53373017/9206337

This post will give you an idea to know when the app finishes the build method. So that you can wait for your async method to setState after widget is mounted : https://stackoverflow.com/a/51273797/9206337


  @override
  void initState() {
    super.initState();
    asyncInitState(); // async is not allowed on initState() directly
  }

  void asyncInitState() async {
    await yourAsyncCalls();
  }

Per documentation at https://pub.dev/packages/provider

initState() {
  super.initState();
  Future.microtask(() =>
    context.read<MyNotifier>(context).fetchSomething(someValue);
  );
}