Type Future<int> et in dans un widget

Bonjour,

Je bloque sur un point : je dois passer un paramètre à un widget de type , ce dernier est calculé par une fonction async, mais évidemment elle renvoie toujours un Future et pas un int. Pour être plus concret, voici ma view :

 future: widget.viewModel.getAllExploitant(),
                  initialData: const [],
                  builder: (context, snapshot) {
                    if (snapshot.hasError) {
                      return const Center(
                        child: Text('An error has occurred!'),
                      );
                    } else {
                      final data = snapshot.data;
                      return Center(
                          child: ListView.builder(
                        itemCount: data!.length,
                        itemBuilder: (context, index) {
                          return LineExploitant(
                            snapshot.data!,
                            index,
                            widget.viewModel.getSession(
                              snapshot.data!.elementAt(index).id!,
                            ),
                          );
                        },
                      ));
                    }
                  })),

Le problème se situe dans le LineExploitant (le 3ème paramètre doit être un int, car si je déclare Future dans le widget, j’ai le fameux message « instance of  »

Mon code n’a pas d’erreur dans VS code, mais à l’exécution j’ai le message :

Expected a value of type 'int', but got one of type '_Future<int>'

Je parviens à le faire avec des List, mais pas avec un simple int, alors je finis par sécher !

Merci

Hello @StuntmanMike ,

Je ne suis pas sûr de ma réponse car je ne sais pas ce que fait ton widget.viewModel.getSession(), mais je pense que tu peux essayer ceci :

 future: widget.viewModel.getAllExploitant(),
                  initialData: const [],
                  builder: (context, snapshot) {
                    if (snapshot.hasError) {
                      return const Center(
                        child: Text('An error has occurred!'),
                      );
                    } else {
                      final data = snapshot.data;
                      return Center(
                          child: ListView.builder(
                        itemCount: data!.length,
                        itemBuilder: (context, index) {
                          List<...> dataList = data;
                          int id = widget.viewModel.getSession(dataList.elementAt(index).id!);
                          return LineExploitant(
                           dataList,
                            index,
                            id,
                          );
                        },
                      ));
                    }
                  })),

Si ton getSession() est un Future il faut attendre ton résultat. Rajoute un await devant ton widget.viewModel.getSession(dataList.elementAt(index).id!);


Aussi tu peux typer ton snapshot de cette manière

builder: (BuildContext context, AsyncSnapshot<List<...>> snapshot) 

Tu pourras supprimer tes lignes final data = snapshot.data; et List<...> dataList = data; car ton le résultat de ton snapshot sera déjà typé.


Ps : J’ai pas mis de type de list car je sais pas ce que tu récupères, tu as juste à compléter par le type que tu récupères ou par un dynamic.

Merci de ta réponse @AntoLhn, pour le await, je le fais déjà :

  @override
  Future<int> getSession(String? idExploitant) async {
    int counter = await _useCases.getNbSession(idExploitant);
    return counter;
  }

(Sachant que getNbSession est déjà une fonction async) Mais je vais tester ta première solution.

Oui @StuntmanMike, mais ton await est pour ta fonction getNbSession(). Comme ta fonction getSession() est aussi une fonction future, lorsque tu l’appels tu dois aussi faire un await. Sinon ton code va continuer son exécution sans prendre le temps d’attendre la réponse de ta fonction et tu vas avoir une erreur.

En réalité getSession() est insérée via une classe abstraite, donc il fait directement référence à une fonction avec un await, alors je ne sais pas comment l’insérer ici :

abstract class IListExploitantViewModel extends ChangeNotifier {
  //String get email;
  getAllExploitant();
  getInfoById(String index);
  getSession(String idExploitant);

  //void userTouchedSettingsButton();
  //void userTouchedLogoutButton();
}

GetSessions n’est donc (si je comprends bien, ce qui est aléatoire :sweat_smile:) qu’une référence de :

 @override
  Future<int> getNbSession(String? id) async {
    listSession = List<Sessions>.from(
        await _apiManager.findListOfItems<Sessions>(
            filter: PropertyFilter(
                field: "exploitant_session",
                operator: FilterOperator.equals,
                value: id)));
 
    return listSession.length;
  }

D’accord je comprend mieux ce que tu veux faire. Cependant j’avoue ne pas avoir de solution qui me vient à l’esprit … :confused:
Je ne fonctionne pas comme toi et dans ton cas je ne pense pas avoir assez d’expérience pour d’aiguiller plus malheureusement.

LE SUJET RESTE OUVERT !

Merci d’y avoir réfléchi :wink:

J’apporte un d’eau au moulin en simplifiant le problème : ma fonction calcul donc bien, seulement, je ne sais pas comment envoyer les données à ma View pour l’afficher, ainsi :

  getSession(String? idExploitant) async {
    nbSession = await _useCases.getNbSession(idExploitant);
    print("nbsession : " + nbSession.toString());
    return nbSession;
  }

Le « print » renvoie les bonnes valeurs, mais il me faudrait un Return int , sauf que si je mets un

int getSession(String? idExploitant) async {

il faut un future :

Future<int> getSession(String? idExploitant) async {

et donc j’ai une erreur du type Future ne correspond pas à int dans mon builder…

Merci !

Hello,

Ma contribution uniquement pour le plaisir de jouer avec les Future :slight_smile:

Le principe est que l’on ne connait pas le temps de traitement d’un Future, le résultat peut arriver n’importe quand.

Je suis parti sur une solution AnimatedBuilder, l’idée étant de créer un mini Viewmodel pour la valeur uniquement et de rafraichir avec un AnimatedBuilder.

Le code sera plus parlant :

La classe Builder

class FutureValueBuilder<T> extends ChangeNotifier {
  T? defaultValue;
  T? _value;
  Future<T> future;

  FutureValueBuilder(this.defaultValue, this.future) {
    future.then((value) {
      _value = value;
      notifyListeners();
    });
  }

  T? get value => _value;
}

La fonction Future pour le test

Future<int> myFuture(String char) async {
  return char.hashCode + 1;
}

Dans le build

...
ListView.builder(
  itemCount: 3,
  itemBuilder: (context, index) {
    List<String> liste = ["a", "b", "c"];
    final builder = FutureValueBuilder(0, myFuture(liste[index]));
    return AnimatedBuilder(
      animation: builder,
      builder: (context, _) {
        int id = builder.value ?? 0;
        return Text("${liste[index]} => $id");
      },
    );
  },
),
...

On pourrait améliorer ça en ajoutant les paramètres nommés, en gérant le onError etc…

Merci de ta réponse, effectivement, c’est ce qu’il faudrait faire, mais j’ai finalement opté pour une autre voie (qui optimise les requêtes avec mon Directus)