Loupe

Faire des requêtes HTTP dans vos applications Flutter

Les applications faisant appel à des APIs pour récupérer les données à afficher dans l'application sont très fréquentes. Cela se fait via des requêtes HTTP.

Pour ce faire, je vais reprendre en exemple l'application réalisée dans l'article précédent récupérant les données de géolocalisation de l'appareil. Cette application aura pour but d'afficher les heures des levers et couchers du soleil à l'endroit où se trouve l'utilisateur.

J'ai donc trouvé une API libre pour cela : Sunrise Sunset API. C'est une API toute simple, un seul appel en GET est nécessaire pour récupérer nos données.

https://api.sunrise-sunset.org/json?lat={votre-latitude}&lng={votre-longitude}

Et voici à quoi ressemble le résultat :

{
    "results": {
        "sunrise": "6:03:18 AM",
        "sunset": "6:19:26 PM",
        "solar_noon": "12:11:22 PM",
        "day_length": "12:16:08",
        "civil_twilight_begin": "5:37:32 AM",
        "civil_twilight_end": "6:45:13 PM",
        "nautical_twilight_begin": "5:07:15 AM",
        "nautical_twilight_end": "7:15:29 PM",
        "astronomical_twilight_begin": "4:36:29 AM",
        "astronomical_twilight_end": "7:46:15 PM"
    },
    "status": "OK"
}
 

Dans cet article nous verrons donc comment faire appel à cette API, puis comment parser le résultat afin de le traiter.

Requête GET

Pour pouvoir faire des appels HTTP, il nous faut le package http. Ajoutons-le donc dans les dépendances du projet, dans le fichier pubspec.yaml :

dependencies:
  http: ^0.12.0+2

La première étape est réussie, voici une première étoile pour vous récompenser : . Au cours de cet article vous récolterez 5 étoiles, une pour chaque étape. Récoltez-en le plus possible !

Avec ce nouveau package nous pouvons effectuer notre requête. C'est une action asynchrone, il sera donc nécessaire d'attendre le résultat. Et pour cela, on utilise...... Vous vous souvenez, on en a parlé dans l'article précédent....c'est....une Future oui ! Une pour ceux qui ont trouvé la bonne réponse.

Dans un premier temps, nous afficherons le résultat brut (en string donc). Notre Future sera donc de type Future<string> :

Future<String> sunriseSunsetTimes;

Future<String> _getTimes() async {
    // Récupération de la localisation actuelle de l'utilisateur
    var geoloc = await location.getLocation();
    // Construction de l'URL a appeler
    var url = 'https://api.sunrise-sunset.org/json?lat=${geoloc.latitude}&lng=${geoloc.longitude}&formatted=0';
    // Appel
    var response = await http.get(url);
    print('Response status: ${response.statusCode}');
    print('Response body: ${response.body}');
    return response.body;
}
 

Ensuite, nous pouvons construire notre vue ainsi :

body: FutureBuilder(
    future: sunriseSunsetTimes,
    builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
        switch (snapshot.connectionState) {
            case ConnectionState.waiting:
                // En attendant le résultat, affichons un loader.
                return new Center(child: new CircularProgressIndicator());
            case ConnectionState.done:
                if (snapshot.hasError) {
                    // En cas d'erreur.
                    return new Text(
                        '${snapshot.error}',
                        style: TextStyle(color: Colors.red),
                    );
                } else {
                    // Une fois le résultat obtenu, affichons la réponse obtenue.
                    return Text(snapshot.data);
                }
                break;
            default:
                return new Text('');
        }
    }),
 

Avec ceci, nous affichons le texte reçu par l'API. Ce texte brut est en json. Afin de pouvoir l'utiliser, nous allons devoir le parser pour constuire l'objet correspondant.

Vous avez réussi à afficher la réponse de l'API ? Vous gagnez une nouvelle !

Parser le JSON

Notre réponse de l'API étant un objet simple (tout comme l'application), nous pouvons nous permettre de parser le résultat à la main pour construire l'objet souhaité. Pour ma part, je n'ai besoin que des heures de lever et de coucher de soleil. Voici donc à quoi ressemble mon objet :

class SunriseSunsetTimes { 
    final DateTime sunrise; 
    final DateTime sunset; 

    SunriseSunsetTimes(this.sunrise, this.sunset);
}

J'y ajoute un constructeur fromJson, pour construire mon objet à partir du json. Le json sera préalablement converti dans un objet de type clé/valeur : Map<String, dynamic> :

SunriseSunsetTimes.fromJson(Map<String, dynamic> json)
    : sunrise = DateTime.parse(json["results"]["sunrise"]),
    sunset = DateTime.parse(json["results"]["sunset"]);

Avec ceci, nous pouvons modifier le contenu de notre Future pour parser la réponse de l'API ainsi que le type de retour (qui n'est plus une chaîne de caractères mais l'objet contenant les horaires) :

Future<SunriseSunsetTimes> _getTimes() async {
    // Récupération de la localisation actuelle de l'utilisateur
    var geoloc = await location.getLocation();
    // Construction de l'URL a appeler
    var url = 'https://api.sunrise-sunset.org/json?lat=${geoloc.latitude}&lng=${geoloc.longitude}&formatted=0';
    // Appel
    var response = await http.get(url);
    // Convertit le json
    Map timesMap = jsonDecode(response.body);
    // Mapper le json dans l'objet SunriseSunsetTimes
    var times = SunriseSunsetTimes.fromJson(timesMap);
    return times;
}
 

Etant donné que le type de retour a changé, il faut modifier le FutureBuilder dans la construction de la vue pour prendre en compte le nouvel objet :

body: FutureBuilder(
    future: sunriseSunsetTimes,
    builder: (BuildContext context, AsyncSnapshot<SunriseSunsetTimes> snapshot) {
    switch (snapshot.connectionState) {
        case ConnectionState.waiting:
            // En attendant le résultat, affichons un loader.
            return new Center(child: new CircularProgressIndicator());
        case ConnectionState.done:
            if (snapshot.hasError) {
                // En cas d'erreur.
                return new Text(
                '${snapshot.error}',
                style: TextStyle(color: Colors.red),
                );
            } else {
                // Une fois le résultat obtenu, affichons les coordonnées reçues.
                return Column(children: <Widget>[
                Text("Lever de soleil : " + snapshot.data.sunrise.toString()),
                Text("Coucher de soleil : " + snapshot.data.sunset.toString()),
                ],);
            }
            break;
        default:
            return new Text('');
    }
}),
 

Vous devriez maintenant voir les dates s'afficher de façon assez brute encore une fois. C'est bien le cas ? Attrapez donc cette nouvelle étoile !

Nous pouvons à présent utiliser nos données, par exemple pour formater l'affichage. Sachant que les dates sont forcément celles du jour, nous pouvons imaginer un affichage indiquant dans combien de temps et où était le lever et le coucher de soleil.

String _getDateDiff(DateTime date){
  var now = DateTime.now(); 
  var diff = now.difference(date); 
  String res = ""; 

  if(diff.isNegative){
    res += "Il y a ";
  } 
  else{ 
    res += "Dans "; 
  } 

  res += diff.inMinutes.abs().toString() + " minutes"; return res;
} 

[...]

return Column(children: <Widget>[ 
  Text("Lever de soleil : " + _getDateDiff(snapshot.data.sunrise.toLocal())), 
  Text("Coucher de soleil : " + _getDateDiff(snapshot.data.sunset.toLocal())),
]

Voilà qui est plus lisible :) Vous avez eu le courage d'avoir un affichage plus lisible ? Oui ? Vous gagnez une nouvelle étoile !

Avec tout cela, vous savez maintenant comment faire appel à une API depuis vos applications Flutter. Je ne décrirai ici que la méthode GET, la méthode POST n'étant pas beaucoup plus compliquée, j'ai confiance en vous, vous saurez vous en sortir.

Alors dites-moi, combien avez vous récolté d'étoiles au long de cet article ? 5/5 j'espère !

Pour ceux qui voudraient en lire un peu plus sur Flutter, voici quelques liens :

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus