Qu'est-ce que Flutter ?
Flutter est un framework de Google pour créer des applications multiplateformes (Android, iOS, web, desktop) à partir d'un seul code. Il utilise le langage Dart. Son principe fondateur : tout est widget — chaque élément de l'interface (texte, bouton, marge, écran entier) est un widget.
- Un seul code pour Android et iOS.
- Hot reload — les modifications s'affichent instantanément, sans tout recompiler.
- Widgets — on décrit l'interface en composant des widgets les uns dans les autres.
Créer & lancer un projet
On crée un projet avec la commande flutter create, puis on le lance sur un émulateur ou un téléphone branché avec flutter run.
Commandes
flutter create fioul_app # crée le projet cd fioul_app flutter run # lance sur l'appareil/émulateur
Fichiers importants
lib/main.dart # le point d'entrée de l'application pubspec.yaml # dépendances (paquets) et ressources
r dans la console) : le changement apparaît en moins d'une seconde.Tout est widget
Un widget décrit une partie de l'interface. Des widgets simples : Text (du texte), Icon (une icône), Image. Des widgets « contenants » reçoivent d'autres widgets via leur propriété child (un enfant) ou children (plusieurs).
Exemples de widgets
Text('Bonjour') Icon(Icons.local_gas_station) Padding( padding: EdgeInsets.all(16), // une marge de 16 px child: Text('Avec une marge'), // son enfant )
Une première application
On lance l'appli avec runApp(). MaterialApp pose le style Material ; Scaffold fournit la structure d'un écran (barre en haut AppBar, corps body).
lib/main.dart
import 'package:flutter/material.dart'; void main() => runApp(const MonAppli()); class MonAppli extends StatelessWidget { const MonAppli({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Fioul Groupé')), body: const Center(child: Text('Bonjour !')), ), ); } }
Rendu
build() renvoie l'arbre de widgets à afficher. Center centre son enfant à l'écran.Organiser l'écran (layout)
Deux widgets essentiels pour placer les éléments : Column (les empile verticalement) et Row (les aligne horizontalement). Ils prennent une liste children.
Exemple — une colonne
Column( children: const [ Text('Campagne Ladignac'), Text('Volume : 4200 L'), Text('Prix : 1,02 €/L'), ], )
Rendu
Volume : 4200 L
Prix : 1,02 €/L
mainAxisAlignment et crossAxisAlignment règlent l'alignement dans une Column/Row. Expanded fait qu'un enfant occupe l'espace restant.Stateless vs Stateful
Un StatelessWidget est figé (il ne change pas après affichage). Un StatefulWidget a un état qui peut évoluer : on appelle setState() pour dire à Flutter de redessiner l'écran.
Exemple — un compteur (état qui change)
class Compteur extends StatefulWidget { const Compteur({super.key}); @override State<Compteur> createState() => _CompteurState(); } class _CompteurState extends State<Compteur> { int _valeur = 0; @override Widget build(BuildContext context) { return Column(children: [ Text('Litres : $_valeur'), ElevatedButton( onPressed: () => setState(() => _valeur += 100), child: const Text('+100 L'), ), ]); } }
setState(), modifier _valeur ne rafraîchit pas l'écran. C'est setState() qui déclenche un nouveau build().Afficher une liste
ListView affiche une liste défilante. ListView.builder construit les éléments à la demande : idéal pour une liste venant de données (ex. des campagnes).
Exemple
final produits = ['Fioul 500L', 'Fioul 1000L', 'Fioul 2000L']; ListView.builder( itemCount: produits.length, itemBuilder: (context, i) => ListTile( leading: const Icon(Icons.local_gas_station), title: Text(produits[i]), ), )
Rendu
ListTile est un widget prêt à l'emploi pour une ligne de liste (icône, titre, sous-titre, action).Appeler une API (HTTP)
Le paquet http permet d'interroger une API REST (comme celle construite avec Spring). Les appels réseau sont asynchrones : on utilise async/await.
Ajouter le paquet (pubspec.yaml)
dependencies: http: ^1.2.0
Récupérer des données et les transformer
import 'package:http/http.dart' as http; import 'dart:convert'; Future<List> chargerCampagnes() async { final url = Uri.parse('http://10.0.2.2:8080/api/campagnes'); final reponse = await http.get(url); if (reponse.statusCode == 200) { return jsonDecode(reponse.body); // JSON → liste Dart } throw Exception('Erreur de chargement'); }
10.0.2.2 (et non localhost, qui désignerait l'émulateur lui-même).FutureBuilder (gère chargement/erreur/données) avec ListView.builder pour afficher la liste reçue de l'API.Un formulaire
Un TextField saisit du texte ; un TextEditingController en récupère la valeur. Un bouton déclenche l'action (ex. envoyer en POST à l'API).
Exemple — saisir une quantité de litres
final _litres = TextEditingController(); Column(children: [ TextField( controller: _litres, keyboardType: TextInputType.number, decoration: const InputDecoration(labelText: 'Litres'), ), ElevatedButton( onPressed: () { final valeur = _litres.text; // la saisie // ... envoyer à l'API : POST /campagnes/{id}/commandes }, child: const Text('Rejoindre la campagne'), ), ])