Kodokon kodokon.com

Appeler une API HTTP : décodage et états d'écran

Structurez un appel réseau professionnel avec le package http : timeout, codes de statut, décodage typé et états exhaustifs.

11 min · 3 questions

Ouvrir cette leçon dans Kodokon

Le package http couvre l'essentiel : requêtes, en-têtes, corps. dio ajoute intercepteurs, annulation et retry, au prix d'une dépendance plus lourde - un bon choix quand l'application dépasse quelques endpoints. Quel que soit le client, la structure d'un appel réseau sérieux ne change pas : un timeout, une vérification du code HTTP, un décodage typé, et trois états d'écran (chargement, erreur, données) traités explicitement.

BASH
flutter pub add http
DART
import 'dart:convert';

import 'package:http/http.dart' as http;

class Article {
  const Article({required this.title});

  factory Article.fromJson(Map<String, dynamic> json) =>
      Article(title: json['title'] as String);

  final String title;
}

Future<List<Article>> fetchArticles() async {
  final uri = Uri.https('api.example.com', '/articles');
  final response = await http
      .get(uri)
      .timeout(const Duration(seconds: 8));
  if (response.statusCode != 200) {
    throw Exception('HTTP ${response.statusCode}');
  }
  final body = utf8.decode(response.bodyBytes);
  final data = jsonDecode(body) as List<dynamic>;
  return [
    for (final item in data)
      Article.fromJson(item as Map<String, dynamic>),
  ];
}
Timeout, statut vérifié, décodage UTF-8 puis typage.

Deux comportements surprennent souvent. Un : http ne lève aucune exception sur un statut 404 ou 500 - la requête a « réussi » du point de vue réseau, c'est à vous de vérifier statusCode. Deux : jsonDecode retourne dynamic ; castez immédiatement (as List<dynamic>, as Map<String, dynamic>) pour rétablir le typage. Pour des payloads volumineux, déportez le décodage dans un isolate avec compute afin de ne pas geler l'interface.

Reste à représenter l'état de l'écran. Les booléens isLoading combinés à error != null finissent toujours en états incohérents (chargement et erreur à la fois). Une classe sealed rend les états mutuellement exclusifs et le switch exhaustif : si vous ajoutez un état demain, le compilateur pointera chaque écran à mettre à jour.

DART
import 'package:flutter/material.dart';

sealed class ArticlesState {
  const ArticlesState();
}

class ArticlesLoading extends ArticlesState {
  const ArticlesLoading();
}

class ArticlesError extends ArticlesState {
  const ArticlesError(this.message);

  final String message;
}

class ArticlesLoaded extends ArticlesState {
  const ArticlesLoaded(this.titles);

  final List<String> titles;
}

Widget buildArticlesBody(ArticlesState state) {
  return switch (state) {
    ArticlesLoading() => const Center(
        child: CircularProgressIndicator(),
      ),
    ArticlesError(:final message) => Center(
        child: Text(message),
      ),
    ArticlesLoaded(:final titles) => ListView.builder(
        itemCount: titles.length,
        itemBuilder: (context, index) => ListTile(
          title: Text(titles[index]),
        ),
      ),
  };
}
Un switch exhaustif : aucun état ne peut être oublié.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Votre appel http.get reçoit une réponse 500. Que fait le package http ?
    • Il lève une exception que vous pouvez attraper avec try/catch.
    • Il renvoie normalement la Response : c'est à vous de vérifier statusCode.
    • Il relance automatiquement la requête trois fois.
  2. Les accents s'affichent mal dans les données reçues. Cause la plus probable ?
    • jsonDecode ne gère pas l'UTF-8.
    • Flutter ne sait pas afficher les caractères accentués sans package dédié.
    • Le serveur omet le charset : response.body est alors décodé en latin-1 ; utilisez utf8.decode(response.bodyBytes).
  3. Quel est l'avantage décisif d'une classe sealed pour modéliser l'état d'un écran ?
    • Le switch devient exhaustif : le compilateur signale tout état non géré.
    • Elle rend le rendu de l'écran plus rapide.
    • Elle permet de modifier l'état sans reconstruire le widget.