Je développe une application Flutter mais je me demande ce que je dois faire lorsque j'appelle du code asynchrone depuis l'interface utilisateur - disons la méthode de construction d'un widget d'interface utilisateur.
Par exemple, mon application connecte Firebase via une classe de service, qui utilise le style Async-Await pour récupérer les enregistrements de Firebase. L'utilisation de l'attente garantit que la méthode de ma classe de service terminera la récupération des enregistrements avant de revenir du côté de l'interface utilisateur.
Cependant, étant donné que la méthode de la classe de service est marquée comme asynchrone, elle renvoie immédiatement un Future au widget d'interface utilisateur appelante et le code de l'interface utilisateur continue de s'exécuter, avant la fin de la méthode de la classe de service.
Oui, je peux écrire des codes pour le rappel "then ()" pour gérer les enregistrements récupérés, mais qu'en est-il des autres codes à l'intérieur du widget qui suivent et dépendent du résultat de l'appel asynchrone? Est-ce que cela signifie que je dois tout intégrer dans la clause "then ()" et éviter d'ajouter des étapes d'exécution après un appel de méthode qui renvoie un Future? Un modèle suggéré pour un tel modèle d'utilisation?
@override
Widget build(BuildContext context) {
RealtimeDatabase.getData() // my service class
.then((onValue) {
..... // do something after the async call is completed
}
..... // do something immediately before the async call is done
class RealtimeDatabase {
Future<String> getData() async {
DataSnapshot dataSnapshot = await FirebaseDatabase.instance.reference().orderByKey().once(); // will wait until completed
.....
.....
Désolé si ma description ici manque de clarté, tout conseil est le bienvenu.
Jimmy
Dans Flutter, il existe des widgets qui peuvent vous aider à le faire de manière transparente (par exemple, FutureBuilder
, StreamBuilder
) et vous pouvez contrôler ce qui doit être rendu en fonction de leur résolution.
Exemple sur FutureBuilder
:
Widget build(BuildContext context) {
return new FutureBuilder(
future: FirebaseDatabase.instance.reference().child("node"),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return snapshot.hasData? new Scaffold(
///start building your widget tree
):new CircularProgressIndicator(); ///load until snapshot.hasData resolves to true
},);
}
Exemple pour StreamBuilder
:
class Database {
DatabaseReference _refProfile = FirebaseDatabase.instance.reference().child(
"profiles");
getProfiles() => _refProfile.onValue; }
.............
Widget build(BuildContext context) {
return new StreamBuilder<Event>(
stream: _database.getProfiles(), //_database = new Database()
builder: (BuildContext context, AsyncSnapshot<Event> event) {
return event.hasData?new Scaffold(
///build your widget tree
):new CircularProgressIndicator();
/// place holder
}
Il convient de mentionner que
FutureBuilder
est mieux utilisé lorsque vous voulez aller chercher certaines données une fois et ne vous souciez pas d'avoir une connexion cohérente ou de suivre les changements dans les données .
Tandis que StreamBuilder
, d'autre part, vous permet de continuer à écouter les données et vous pouvez mettre à jour l'état de l'interface utilisateur en fonction de toute mise à jour des données.
Bien que je pense que les méthodes mentionnées par @aziza sont définitivement la voie à suivre, il convient de noter que vous pouvez également "rouler les vôtres".
Ainsi, par exemple, dans initState()
, vous pouvez faire quelque chose comme ceci:
@override
void initState() {
super.initState();
_myItems = // some sane default
FirebaseDatabase.instance.reference().child("node")
.then((items) {
if (widget.mounted) setState(() { _myItems = items; }
});
}
Widget build(BuildContext context) {
return new Scaffold(
///start building your widget tree
);
}
Il serait préférable d'utiliser FutureBuilder
ici, mais c'est une autre façon de le faire (même si ce n'est pas nécessairement une meilleure pratique).