133 votes

Existe-t-il un moyen de charger des données asynchrones sur la méthode InitState ?

Je suis à la recherche d'un moyen de charger des données asynchrones sur la méthode InitState, j'ai besoin de certaines données avant l'exécution de la méthode de construction. J'utilise un code GoogleAuth, et je dois exécuter la méthode de construction jusqu'à ce qu'un Stream soit exécuté.

Ma méthode initState est :

 @override
  void initState () {
    super.initState();
    _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account)     {
      setState(() {
        _currentUser = account;
      });
    });
    _googleSignIn.signInSilently();
  }

J'apprécierai tout commentaire.

4 votes

StreamBuilder est la solution correcte

0 votes

Ce code est absolument parfait tel quel ?

1 votes

Depuis initState() n'est appelé qu'une seule fois, y a-t-il un intérêt à utiliser setState() à l'intérieur ?

88voto

diegoveloper Points 24042

Vous pouvez créer un async et l'appeler dans votre initState

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

        }

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

6 votes

Ça ne devrait rien changer, sauf si son appel est synchrone et instantané. Mais alors il n'y a pas de problème d'asynchronisme.

12 votes

Dans ce cas build exécuté AVANT _asyncMethod terminé !

3 votes

@diegoveloper quel est le but d'utiliser WidgetsBinding.instance.addPostFrameCallback au lieu de la réponse de @Nae ?

78voto

Nae Points 5451

A partir de maintenant, en utilisant .then semble fonctionner :

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

27 votes

Si votre fonction asynchrone ne retourne rien, utilisez whenComplete à la place.

2 votes

@Aravin Personnellement, je préfère les flux autant que possible. Mais cela "fonctionne" à des fins démonstratives. Je doute fort que ce soit la solution la plus efficace. droite manière.

0 votes

J'obtiens "Unhandled Exception : setState() called after dispose()".

53voto

Thanthu Points 1415

Méthode 1 : Vous pouvez utiliser StreamBuilder pour le faire. Cela permettra d'exécuter le constructeur chaque fois que les données dans flux changements.

Vous trouverez ci-dessous un extrait de code tiré d'un de mes exemples de projets :

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

Dans le code ci-dessus StreamBuilder écoute tout changement de contenu, initialement il s'agit d'un tableau vide et affiche l'icône CircularProgressIndicator . Une fois que j'ai fait l'appel API, les données récupérées sont ajoutées au tableau de contenu, qui exécutera la fonction de l'API. constructeur méthode.

Lorsque l'utilisateur fait défiler la page vers le bas, d'autres contenus sont récupérés et ajoutés au tableau des contenus, qui sera à nouveau exécuté. constructeur méthode.

Dans votre cas, seul le chargement initial sera nécessaire. Mais cela vous donne la possibilité d'afficher autre chose à l'écran jusqu'à ce que les données soient récupérées.

J'espère que cela vous sera utile.

EDIT :

Dans votre cas, je suppose que cela ressemblera à ce qui est indiqué ci-dessous :

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
            }
        });

Méthode 2 : Une autre méthode consisterait à créer un async et l'appeler à partir de votre initState() comme indiqué ci-dessous :

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

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

15voto

iDecode Points 3965

Créer une fonction anonyme à l'intérieur initState comme ça :

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

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

1 votes

Ayez le courage de mentionner la raison du downvoting.

2 votes

En quoi est-ce différent que d'appeler _performYourTask(); directement ? Comment le fait d'envelopper la fonction asynchrone avec une autre fonction asynchrone qui n'est pas attendue peut-il aider ?

1 votes

Dans la fonction anonyme ci-dessus, vous pouvez appeler setState après _performYourTask est effectuée (ce que vous ne pouvez certainement pas faire dans le cadre de l'initiative initState directement)

8voto

Fardeen Khan Points 425

Réponse précédente !

Vous pouvez définir une valeur booléenne telle que "loaded" et lui attribuer la valeur "true" dans votre fonction d'écoute et faire en sorte que votre fonction de construction renvoie vos données lorsque "loaded" a la valeur "true". Sinon, il suffit de lancer un CircularProgressIndicator.

Modifié Je ne suggérerais pas d'appeler setState dans une méthode que vous appelez dans initState. Si le widget n'est pas monté lorsque le setState est appelé (alors que l'opération asynchrone se termine), une erreur sera signalée. Je vous suggère d'utiliser un package après_layout

Jetez un coup d'œil à cette réponse pour mieux comprendre setState dans initState : https://stackoverflow.com/a/53373017/9206337

Ce post vous donnera une idée pour savoir quand l'application termine la méthode de construction. Ainsi, vous pouvez attendre que votre méthode asynchrone fixe l'état après le montage du widget : https://stackoverflow.com/a/51273797/9206337

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