web-dev-qa-db-fra.com

Flutter - Est-il possible d'extraire des données d'un futur sans utiliser FutureBuilder?

Je lis dans l'entrée fournie par l'utilisateur (dans ce cas, un code postal) à partir d'une TextField que je dois vérifier par rapport à une base de données pour en vérifier la validité. Cependant, je dois créer une requête asynchrone dans la base de données à l'intérieur de la fonction __fariée__ du bouton d'envoi (une RaisedButton dans ce cas) onPressed: () {}. Dans la plupart des langages de programmation, la tâche est relativement simple et simple. Le problème que je rencontre dans Flutter, cependant, est le fait que les objets Future renvoyés à partir de requêtes de base de données asynchrones ne peuvent être utilisés que par des objets FutureBuilder qui à leur tour ne renvoient que des objets Widget. Il me faut simplement un String renvoyé que je peux ensuite utiliser pour passer à un nouvel itinéraire via un objet MaterialPageRoute ou afficher une erreur à l'utilisateur sans changer d'itinéraire. Y a-t-il un moyen de faire cela avec Flutter? Retourner un widget est inutile pour moi car je ne veux pas créer un nouveau widget à afficher. J'utilise Flutter 0.3.2 et Dart 2.0.0

Voici un exemple simplifié où je dois appeler la requête de base de données:

@override
Widget build(Buildcontext context) {
    return new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
            new Container(
                padding: const EdgeInsets.all(16.0),
                child: new TextField(
                    keyboardType: TextInputType.number,
                    controller: _controller,
                    decoration: new InputDecoration(
                    hintText: 'Zip Code',
                ),
                onSubmitted: (string) {
                  return string;
                },
              ),
            ),
            new RaisedButton(
                onPressed: () {
                        // use regex to test against user input
                        if (_controller.text != null && _controller.text.isNotEmpty) {
                            RegExp zipCodeRegExp = new RegExp(r"^(\d{5})$");

                            // if the user input validates...
                            if (zipCodeRegExp.hasMatch(_controller.text)) {
                            zipCode = _controller.text;

                           // need to perform database query here and return a string, not a Widget

                            } else {
                               // an else condition here
                            }
                        } else {
                           // an else condition here
                        }
                    }
                }
            ),
        ],
    );
}

Peut-être que je ne suis pas le "mantra" de Flutter? J'apprécie votre considération et votre contribution à ce sujet.

5
Ad Astra

J'ai depuis compris cela (je crois que c'est ce que Günter disait à l'origine, mais la raison fondamentale pour laquelle je ne comprenais pas encore à l'époque). Le seul moyen de pouvoir utiliser une variable Future sans créer d'objet Widget consiste à utiliser l'API Future. L’API Future permet d’analyser un objet Future comme s’il s’agissait d’un objet AsyncSnapshot (c’est là que l’on analyserait .data dans une fonction FutureBuilderbuilder:. Cela peut être effectué sur un objet Future renvoyé (pouvant utiliser async avec await). Par exemple: 

Future regionName = dbClient.getRegionNameFromZipCode(int.parse(zipCode)); <-- this database method getRegionNameFromZipCode returns a Future object and uses async and await

regionName.then((data) {
   String hZonesString = data[0]['hzone'];
   print(hZonesString);
}, onError: (e) {
     print(e);
   });

C’est assez simple une fois que vous avez compris comment utiliser l’API Future, et son intention plutôt que d’utiliser FutureBuilder. Bon à savoir pour les débutants de cette langue comme moi!

1
Ad Astra

FutureBuilder est simplement une aide utile pour reconstruire l’arborescence des widgets à la fin d’un avenir.

Vous pouvez utiliser

funcThatReturnsFuture().then((result) {
  print(result);
  setState(() {
    someVal = result;
  })
})

ou 

Future funcThatMakesAsyncCall() async {
  var result = funcThatReturnsFuture();
  print(result);  
  setState(() {
    someVal = result;
  })
}

La principale limitation est que vous ne pouvez pas renvoyer la valeur directement à l'appelant sans une Future, car il n'y a aucun moyen de revenir d'une exécution asynchrone à une exécution synchronisée.

5

Un Future est simplement un sucre sémantique pour un rappel. Imaginez que vous ayez:

void fetchName(void Function(String) callback);

void main() {
  fetchName((name) {
     print('Your name is: $name');
  });
}

Il n'y a aucun moyen de convertir (ou extraire) name à partir de fetchName. Il n'existe pas tant que le rappel n'est pas terminé et le rappel peut ne pas être terminé immédiatement (il peut être lu à partir d'une base de données, comme votre exemple, ou du réseau, etc.).

L’un des avantages de l’utilisation de FutureBuilder est que cela aide vraiment à comprendre les abstractions asynchrones telles que Future (et StreamBuilder pour Stream) et vous permet de vous concentrer sur l’écriture (synchrone) du code de générateur:

new FutureBuilder<String>(
  future: _calculation, // a Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.none: return new Text('Press button to start');
      case ConnectionState.waiting: return new Text('Awaiting result...');
      default:
        if (snapshot.hasError)
          return new Text('Error: ${snapshot.error}');
        else
          return new Text('Result: ${snapshot.data}');
    }
  },
)
1
matanlurey