Communication HTTP et Gestion des Données
Gérer les données
Section titled “Gérer les données”La gestion des données dans une application Flutter moderne implique plusieurs aspects cruciaux :
- Communication client-serveur (HTTP)
- Modèles de données, sérialisation et génération de code
- Gestion d’état et intégration d’APIs
- Persistance de données locales
Communication client-serveur (HTTP)
Section titled “Communication client-serveur (HTTP)”Le Package http offre le moyen le plus simple de recevoir et d’envoyer des données vers un serveur.
Il est possible d’appeler directement les méthodes statiques get, post, put, delete, read. Il est également possible d’instancier un client Http “réutilisable”.
Exemple de requête GET basique
Section titled “Exemple de requête GET basique”import 'package:http/http.dart' as http;
Future<List<Post>> _loadPosts() async { final result = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: {'authorization': token}, );
return List.from(jsonDecode(result.body)) .map((e) => Post.fromMap(e)) .toList();}Exemple interactif sur DartPad
Utilisation du package HTTP
Section titled “Utilisation du package HTTP”http.get - Exemple complet
Section titled “http.get - Exemple complet”import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;
void main() { runApp(const App());}
class App extends StatelessWidget { const App({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return const MaterialApp( home: MainScreen(), debugShowCheckedModeBanner: false, ); }}
class MainScreen extends StatelessWidget { const MainScreen({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Articles')), body: FutureBuilder<List<Post>>( future: _loadPosts(), builder: (context, snapshot) { if (snapshot.hasError) { return const Text( 'Erreur', style: TextStyle(color: Colors.red) ); }
if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); }
final posts = snapshot.data!; return ListView.builder( itemCount: posts.length, itemBuilder: (context, index) { final post = posts[index]; return ListTile( title: Text( post.title, maxLines: 1, overflow: TextOverflow.ellipsis, ), subtitle: Text( post.body, maxLines: 1, overflow: TextOverflow.ellipsis, ), ); }, ); }, ), ); }
Future<List<Post>> _loadPosts() async { final result = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/posts'), );
return List.from(jsonDecode(result.body)) .map((e) => Post.fromMap(e)) .toList(); }}http.post - Envoi de données
Section titled “http.post - Envoi de données”Ces méthodes permettent d’ajouter une Map pour les headers.
http.post( Uri.parse('http://localhost:8080/reply'), body: '{"msgId":123}', headers: {'authorization': token},);JSON handling
Section titled “JSON handling”Parsing JSON basique
Section titled “Parsing JSON basique”Dart fournit des outils intégrés pour le traitement JSON via le package dart:convert :
import 'dart:io'; // Fileimport 'dart:convert'; // jsonDecode
// Exemple de traitement d'un fichier JSONvoid main(List<String> arguments) async { // 1. récupération du chemin // 2. lecture du contenu du fichier cf File(path) // 3. parsing json cf. jsonDecode // 4. transformation données vers CSV // 5. enregistrement fichier CSV}Assets JSON
Section titled “Assets JSON”Pour embarquer des assets JSON, on les déclare dans le fichier pubspec.yaml :
flutter: uses-material-design: true assets: - assets/logo.png - assets/config.json - packages/my_assets/assets/image.pngPour utiliser cet asset :
final config = await rootBundle.loadString('assets/config.json');Data models et sérialisation
Section titled “Data models et sérialisation”Modèle de données basique
Section titled “Modèle de données basique”class Post { final int id; final int userId; final String title; final String body;
Post({ required this.id, required this.userId, required this.title, required this.body, });
Post.fromMap(Map<String, dynamic> data) : id = data['id'], userId = data['userId'], title = data['title'], body = data['body'];}Freezed pour data classes
Section titled “Freezed pour data classes”Introduction à Freezed
Section titled “Introduction à Freezed”Freezed est un package qui :
- simplifie la création de modèles de données immuables (génère les toString, ==, hash, copyWith…)
- permet un mécanisme proche des Union types / swift enums
- est compatible avec
json_serializable
Configuration de Freezed
Section titled “Configuration de Freezed”Freezed est basé sur build_runner, package de génération de code développé par l’équipe de Dart.
La génération de code est une technique assez courante dans le développement Dart.
dependencies: freezed_annotation: any
dev_dependencies: build_runner: any freezed: anyUtilisation de Freezed
Section titled “Utilisation de Freezed”import 'package:freezed_annotation/freezed_annotation.dart';
part 'message.freezed.dart';part 'message.g.dart';
@freezedclass Message with _$Message { const factory Message({ required int id, required String userName, required String message, required DateTime date, List<Reply>? replies, }) = _Message;
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(json);}Pour générer les fichiers :
dart run build_runner builddart run build_runner watchAPI integration patterns
Section titled “API integration patterns”Pattern FutureBuilder
Section titled “Pattern FutureBuilder”Le pattern FutureBuilder est couramment utilisé pour intégrer des APIs dans l’interface utilisateur :
FutureBuilder<List<Post>>( future: _loadPosts(), builder: (context, snapshot) { // Gestion des erreurs if (snapshot.hasError) { return const Text( 'Erreur', style: TextStyle(color: Colors.red) ); }
// État de chargement if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); }
// Affichage des données final posts = snapshot.data!; return ListView.builder( itemCount: posts.length, itemBuilder: (context, index) { final post = posts[index]; return ListTile( title: Text(post.title), subtitle: Text(post.body), ); }, ); },)Gestion d’état avec les données
Section titled “Gestion d’état avec les données”Pour une gestion d’état plus avancée, considérez l’utilisation de patterns comme :
- Bloc : stream et réactivité
- Provider : notifiers et réactivité
- MobX : Réactivité et ViewModel
- Redux : global store & unidirectional data flow
Consultez Flutter architectures pour plus d’exemples.
Switch expressions pour la gestion d’état
Section titled “Switch expressions pour la gestion d’état”Dart 3.0 introduit les switch expressions qui sont utiles pour gérer différents états de données :
final view = switch(result) { Result.pending => ProgressView(), Result.error => ErrorView(), Result.data => UserList(),};Bonnes pratiques
Section titled “Bonnes pratiques”Séparation des responsabilités
Section titled “Séparation des responsabilités”- Séparez la logique métier de l’interface utilisateur
- Utilisez des services dédiés pour les appels API
- Implémentez une gestion d’erreur robuste
Gestion des erreurs
Section titled “Gestion des erreurs”Implémentez une gestion d’erreur appropriée pour les appels réseau :
Future<List<Post>> _loadPosts() async { try { final result = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: {'authorization': token}, );
if (result.statusCode == 200) { return List.from(jsonDecode(result.body)) .map((e) => Post.fromMap(e)) .toList(); } else { throw Exception('Failed to load posts: ${result.statusCode}'); } } catch (e) { throw Exception('Network error: $e'); }}Headers et authentification
Section titled “Headers et authentification”N’oubliez pas d’inclure les headers nécessaires, notamment pour l’authentification :
final result = await http.get( Uri.parse('https://api.example.com/data'), headers: { 'authorization': 'Bearer $token', 'Content-Type': 'application/json', },);Cette approche structurée vous permettra de créer des applications Flutter robustes et maintenables avec une intégration API efficace.