Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

initialize data once in initState and call the setState when data is ready causes exception

Tags:

flutter

Since flutter calls the build method many times in different condition, to avoid getting the data many times, I initialize the data in initState.

I want to re-build the widget when the data is ready.

Here is my code :

class Test extends StatefulWidget {

  @override
  _TestState createState() => new _TestState();

}

class _TestState extends State<Test> {

  Data data;
  bool dataReady = false;

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

    getData(context).then((Data data) async {
      setState(() {
        dataReady= true;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    if (dataReady) {
      return createMainContent(context);
    } else {
      return new Container();
    }
  }

}

However, it results in following exception :

inheritFromWidgetOfExactType(_InheritedProvider) or inheritFromElement() was called before _TestState.initState() completed.

May I know am I doing something wrong here?

When I add the following line to implementation of getData(context)

     await Future.delayed(new Duration(milliseconds: 300));

the exception does not happen.

like image 919
h19881103 Avatar asked Oct 16 '18 06:10

h19881103


People also ask

Can you call setState in initState?

Because we can't call setState() directly in the initState method, we can't call it from a function called by initState either. Unless this function is asynchronous and the setState call is made after the first await.

Is initState called before build?

initState() This is the first method called when the widget is created (after the class constructor, of course.) initState is called once and only once. It must also call super.

Can we make initState async in flutter?

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 method in initState in flutter?

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(); // .... }


2 Answers

For everyone coming here at a later point

It is best to use the @override void didChangeDependencies () method of the State class.

From the docs

This method is also called immediately after initState. It is safe to call BuildContext.inheritFromWidgetOfExactType from this method.

But make sure to check if you have already performed your initialization

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  if (bloc == null) { // or else you end up creating multiple instances in this case.
    bloc = BlocProvider<MyBloc>.of(context);
  }
}
like image 135
JavaBanana Avatar answered Oct 01 '22 02:10

JavaBanana


Edit: Better answer below.


Apparently, you cannot access getData(context) during initState (more concrete: before it completed).

The reason, so I believe, is that getData tries to look up an InheritedWidget ancestor up in the tree, but the tree is just now being built (your widget is created during the parent widget's build).

The obvious solution would be to delay getData's lookup to a later point in time. There are several ways to achieve that:

  • Delay the lookup to a later time. scheduleMicrotask should work fine.
  • Look it up during the first build call. You could have an isInitialized field set to false and in you build, something like:

    if (!isInitialized) {
      isInitialized = true;
      // TODO: do the getData(...) stuff
    }
    
like image 20
Marcel Avatar answered Oct 01 '22 02:10

Marcel