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.
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 FutureBuilder
builder:
. 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!
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.
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}');
}
},
)