Structurez un appel réseau professionnel avec le package http : timeout, codes de statut, décodage typé et états exhaustifs.
Ouvrir cette leçon dans KodokonLe 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.
flutter pub add httpimport '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>),
];
}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.
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]),
),
),
};
}http.get reçoit une réponse 500. Que fait le package http ?try/catch.Response : c'est à vous de vérifier statusCode.jsonDecode ne gère pas l'UTF-8.response.body est alors décodé en latin-1 ; utilisez utf8.decode(response.bodyBytes).sealed pour modéliser l'état d'un écran ?switch devient exhaustif : le compilateur signale tout état non géré.