flutter-use-http-package

Utilisez le package `http` pour exécuter des requêtes GET, POST, PUT ou DELETE. À utiliser quand vous avez besoin de récupérer des données ou d'en envoyer à une API REST.

npx skills add https://github.com/flutter/skills --skill flutter-use-http-package

Implémentation des réseaux Flutter

Table des matières

Configuration et permissions

Configurez l'environnement et les permissions spécifiques à la plateforme requises pour l'accès réseau.

  1. Ajoutez la dépendance du package http via le terminal :
    flutter pub add http
  2. Importez le package dans vos fichiers Dart :
    import 'package:http/http.dart' as http;
  3. Configurez les permissions Android en ajoutant la permission Internet à android/app/src/main/AndroidManifest.xml :
    <uses-permission android:name="android.permission.INTERNET" />
  4. Configurez les droits macOS en ajoutant la clé client réseau à la fois à macos/Runner/DebugProfile.entitlements et macos/Runner/Release.entitlements :
    <key>com.apple.security.network.client</key>
    <true/>

Exécution des requêtes et gestion des réponses

Exécutez les opérations HTTP et mappez les réponses à des objets Dart fortement typés.

  • URIs : Analysez toujours les chaînes d'URL en utilisant Uri.parse('your_url').
  • Headers : Injectez les headers d'autorisation et de type de contenu via le paramètre de carte headers. Utilisez HttpHeaders.authorizationHeader pour les tokens d'authentification.
  • Payloads : Pour les requêtes POST et PUT, encodez le corps en utilisant jsonEncode() depuis dart:convert.
  • Validation du statut : Évaluez response.statusCode. Traitez 200 OK (GET/PUT/DELETE) et 201 CREATED (POST) comme un succès.
  • Gestion des erreurs : Levez des exceptions explicites pour les codes de statut non-succès. Ne retournez jamais null en cas d'échec, car cela empêche FutureBuilder de déclencher son état d'erreur et provoque des indicateurs de chargement infinis.
  • Désérialisation : Analysez la chaîne brute en utilisant jsonDecode(response.body) et mappez-la à un objet Dart personnalisé à l'aide d'un constructeur factory (par exemple, fromJson).

Analyse en arrière-plan

Déléguez l'analyse JSON coûteuse à une Isolate distincte pour éviter les saccades de l'interface utilisateur (chutes d'images).

  • Importez package:flutter/foundation.dart.
  • Utilisez la fonction compute() pour exécuter la logique d'analyse dans une isolate de fond.
  • Assurez-vous que la fonction d'analyse passée à compute() est une fonction de haut niveau ou une méthode statique, car les closures ou les méthodes d'instance ne peuvent pas être transmises entre les isolates.

Flux de travail : Exécution des opérations réseau

Utilisez la liste de contrôle suivante pour implémenter et valider les opérations réseau.

Progression des tâches :

  • [ ] 1. Définissez le modèle Dart fortement typé avec un constructeur factory fromJson.
  • [ ] 2. Implémentez la méthode de requête réseau retournant un Future<Model>.
  • [ ] 3. Appliquez une logique conditionnelle basée sur le type d'opération :
    • Si récupération de données (GET) : Ajoutez les paramètres de requête à l'URI.
    • Si mutation de données (POST/PUT) : Définissez 'Content-Type': 'application/json; charset=UTF-8' et attachez le corps jsonEncode.
    • Si suppression de données (DELETE) : Retournez une instance de modèle vide en cas de succès (200 OK).
  • [ ] 4. Validez le statusCode et levez une Exception en cas d'échec.
  • [ ] 5. Intégrez le Future dans l'interface utilisateur en utilisant FutureBuilder.
  • [ ] 6. Gérez snapshot.hasData, snapshot.hasError, et par défaut un CircularProgressIndicator.
  • [ ] 7. Boucle de rétroaction : Exécutez l'application -> déclenchez la requête réseau -> consultez la console pour les exceptions non gérées -> corrigez les erreurs d'analyse ou de permissions.

Exemples

Implémentation haute fidélité : Récupération et analyse en arrière-plan

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

// 1. Fonction d'analyse de haut niveau pour Isolate
List<Photo> parsePhotos(String responseBody) {
  final parsed = (jsonDecode(responseBody) as List<Object?>)
      .cast<Map<String, Object?>>();
  return parsed.map<Photo>(Photo.fromJson).toList();
}

// 2. Exécution réseau avec analyse en arrière-plan
Future<List<Photo>> fetchPhotos() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/photos'),
    headers: {
      HttpHeaders.authorizationHeader: 'Bearer your_token_here',
      HttpHeaders.acceptHeader: 'application/json',
    },
  );

  if (response.statusCode == 200) {
    // Déléguez l'analyse lourde à une isolate de fond
    return compute(parsePhotos, response.body);
  } else {
    throw Exception('Failed to load photos. Status: ${response.statusCode}');
  }
}
}

// 3. Modèle fortement typé
class Photo {
  final int id;
  final String title;
  final String thumbnailUrl;

  const Photo({
    required this.id,
    required this.title,
    required this.thumbnailUrl,
  });

  factory Photo.fromJson(Map<String, dynamic> json) {
    return Photo(
      id: json['id'] as int,
      title: json['title'] as String,
      thumbnailUrl: json['thumbnailUrl'] as String,
    );
  }
}

// 4. Intégration de l'interface utilisateur
class PhotoGallery extends StatefulWidget {
  const PhotoGallery({super.key});

  @override
  State<PhotoGallery> createState() => _PhotoGalleryState();
}

class _PhotoGalleryState extends State<PhotoGallery> {
  late Future<List<Photo>> _futurePhotos;

  @override
  void initState() {
    super.initState();
    // Initialisez Future une seule fois pour éviter la réfétching lors des reconstructions
    _futurePhotos = fetchPhotos();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<Photo>>(
      future: _futurePhotos,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final photos = snapshot.data!;
          return ListView.builder(
            itemCount: photos.length,
            itemBuilder: (context, index) => ListTile(
              leading: Image.network(photos[index].thumbnailUrl),
              title: Text(photos[index].title),
            ),
          );
        } else if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        }

        // État de chargement par défaut
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}