15 votes

initialiser les données une fois dans initState et appeler le setState quand les données sont prêtes provoque une exception

Comme Flutter appelle la méthode de construction plusieurs fois dans des conditions différentes, pour éviter de récupérer les données plusieurs fois, j'initialise les données dans le fichier initState .

Je veux recréer le widget lorsque les données sont prêtes.

Voici mon 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();
    }
  }

}

Cependant, cela entraîne l'exception suivante :

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

Puis-je savoir si je fais quelque chose de mal ici ?

Lorsque j'ajoute la ligne suivante à l'implémentation de getData(context)

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

l'exception ne se produit pas.

17voto

JavaBanana Points 450

Pour tous ceux qui viendront ici plus tard

Il est préférable d'utiliser le @override void didChangeDependencies () de la méthode State classe.

De la docs

Cette méthode est également appelée immédiatement après initState. Il est prudent d'appeler BuildContext.inheritFromWidgetOfExactType à partir de cette méthode.

Mais assurez-vous de vérifier si vous avez déjà effectué votre initialisation

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  if (bloc == null) { // or else you end up creating multiple instances in this case.
    bloc = BlocProvider<MyBloc>.of(context);
  }
}

12voto

Marcel Points 2638

Edit : Meilleure réponse ci-dessous.


Apparemment, vous ne pouvez pas accéder getData(context) pendant initState (plus concrètement : avant qu'il ne soit achevé).

La raison, selon moi, est que getData essaie de rechercher un InheritedWidget en haut de l'arbre, mais l'arbre vient juste d'être construit (votre widget est créé pendant le processus de création du widget parent). build ).

La solution évidente serait de retarder getData La recherche de l'utilisateur est reportée à un moment ultérieur. Il existe plusieurs façons d'y parvenir :

  • Retarder la recherche à une date ultérieure. scheduleMicrotask devrait fonctionner correctement.
  • Cherchez-le pendant la première build appeler. Vous pourriez avoir un isInitialized le champ est réglé sur false et en vous build quelque chose comme :

    if (!isInitialized) {
      isInitialized = true;
      // TODO: do the getData(...) stuff
    }

2voto

MJames Montes Points 1

Une alternative est de le mettre à l'intérieur PostFrameCallback qui se situe entre initState y Build .

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) => getData());
    super.initState();
  }

  getData() async {

  }

1voto

Ian Samz Points 143

J'ai déplacé mon code vers ma méthode de construction à partir de initState et cela a fonctionné.

class _TestState extends State<Test> {

  Data data;
  bool dataReady = false;

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

  }

  @override
  Widget build(BuildContext context) {
    getData(context).then((Data data) async {
      setState(() {
        dataReady= true;
      });
    });
    if (dataReady) {
      return createMainContent(context);
    } else {
      return new Container();
    }
  }

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X