Cours Flutter Complet 💙

Les 12 chapitres essentiels — widgets, state, navigation, API et publication

12
Chapitres
100+
Exemples de code
Flutter 3+
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et installation

💙 Hello Flutter
// lib/main.dart
import 'package:flutter/material.dart';

void main()

class MyApp extends StatelessWidget

Flutter est un framework UI de Google pour créer des apps natives iOS, Android, Web et Desktop à partir d'un seul codebase. Écrit en Dart, il utilise son propre moteur de rendu (Skia/Impeller) — pas de bridge vers les composants natifs. Utilisé par Google, BMW, Alibaba, eBay et Nubank.

⚙️ Installation
# Installer Flutter SDK
# → https://docs.flutter.dev/get-started/install

# Vérifier l'installation
flutter doctor

# Créer un projet
flutter create mon_app
cd mon_app

# Lancer
flutter run # Détecte l'émulateur/device
flutter run -d chrome # Web
flutter run -d macos # Desktop

# Hot reload : appuyez sur 'r' dans le terminal
# Hot restart : appuyez sur 'R'

⚖️ Flutter vs React Native
Aspect Flutter React Native
Langage Dart JavaScript / TypeScript
Rendu Propre moteur (Skia/Impeller) Composants natifs via bridge
Performance Excellente (compilé nativement) Bonne (New Architecture)
UI Pixel-perfect identique sur toutes les plateformes Composants natifs (look natif)
Hot reload Oui (sub-seconde) Oui
Web/Desktop Supporté officiellement Limité (Expo Web)
Courbe Apprendre Dart Connaître JS/React
🧠 Quiz
Pourquoi Flutter n'utilise pas les composants natifs ?
Flutter dessine chaque pixel avec son propre moteur de rendu (Impeller). Avantages : UI identique sur iOS et Android (pixel-perfect), performances prévisibles (pas de bridge), liberté totale de design. Inconvénient : les widgets ne sont pas les composants natifs de la plateforme (mais Material et Cupertino les imitent fidèlement).

CHAPITRE 02

Widgets de base

🧱 Tout est Widget
// Text
Text(
'Hello Flutter',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.blue),
)

// Image
Image.network('https://example.com/photo.jpg')
Image.asset('assets/logo.png')

// Icon
Icon(Icons.favorite, color: Colors.red, size: 30)

// Button
ElevatedButton(
onPressed: () ,
child: Text('Cliquer'),
)

TextButton(onPressed: () , child: Text('Texte'))
IconButton(onPressed: () , icon: Icon(Icons.add))
OutlinedButton(onPressed: () , child: Text('Outlined'))

// Container (équivalent div avec style)
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(blurRadius: 10, color: Colors.black26)],
),
child: Text('Card', style: TextStyle(color: Colors.white)),
)

// Card (Material Design)
Card(
elevation: 4,
child: Padding(
padding: EdgeInsets.all(16),
child: Text('Contenu de la card'),
),
)

En Flutter, tout est un widget. Le texte, les boutons, le padding, les colonnes, même l'app elle-même. Les widgets sont imbriqués en arbre (widget tree). Un widget décrit une partie de l'interface — Flutter le dessine à l'écran.

CHAPITRE 03

Layout (mise en page)

📐 Column, Row, Stack
// Column — empiler verticalement
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Titre', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 8), // Espacement
Text('Description'),
],
)

// Row — aligner horizontalement
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Gauche'),
Text('Droite'),
],
)

// Expanded — prendre l'espace restant
Row(
children: [
Expanded(child: Text('Prend le reste')),
SizedBox(width: 100, child: Text('Fixe')),
],
)

// Stack — superposer
Stack(
children: [
Image.network('photo.jpg'),
Positioned(
bottom: 16, left: 16,
child: Text('Overlay', style: TextStyle(color: Colors.white)),
),
],
)

// Padding
Padding(
padding: EdgeInsets.all(16),
child: Text('Avec padding'),
)

// SingleChildScrollView — contenu scrollable
SingleChildScrollView(
child: Column(children: […]),
)

CHAPITRE 04

StatefulWidget et état

🔄 StatelessWidget vs StatefulWidget
// StatelessWidget — pas d'état interne, immuable
class Greeting extends StatelessWidget

// StatefulWidget — état mutable, rebuild avec setState
class Counter extends StatefulWidget

class _CounterState extends State<Counter>

setState() déclenche un rebuild. Appelez setState(() ) chaque fois que vous modifiez une variable qui affecte l'UI. Sans setState, Flutter ne sait pas qu'il doit redessiner le widget.

CHAPITRE 05

Listes et grilles

📋 ListView
// ListView.builder — performant pour les grandes listes
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) ,
)

// GridView
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: products.length,
itemBuilder: (context, index) ,
)

// Séparateur
ListView.separated(
itemCount: items.length,
separatorBuilder: (_, __) => Divider(),
itemBuilder: (context, index) => ListTile(title: Text(items[index])),
)

CHAPITRE 06

Navigation et routing

🧭 GoRouter (recommandé)
// pubspec.yaml → dependencies: go_router: ^14.0.0
// flutter pub get

import 'package:go_router/go_router.dart';

final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/post/:id',
builder: (context, state) ,
),
],
);

// Dans MaterialApp
MaterialApp.router(routerConfig: router)

// Naviguer
context.go('/'); // Remplacer
context.push('/post/42'); // Empiler
context.pop(); // Retour

📱 Bottom Navigation
Scaffold(
body: _screens[_currentIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) ,
destinations: [
NavigationDestination(icon: Icon(Icons.home), label: 'Accueil'),
NavigationDestination(icon: Icon(Icons.search), label: 'Chercher'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profil'),
],
),
)

CHAPITRE 07

Formulaires

📝 Form et validation
class LoginForm extends StatefulWidget

class _LoginFormState extends State<LoginForm>

CHAPITRE 08

HTTP et API

🌐 Appels réseau avec Dio
// pubspec.yaml → dependencies: dio: ^5.0.0

import 'package:dio/dio.dart';

class ApiService

// Modèle avec fromJson / toJson
class Post
// Afficher les données avec FutureBuilder
FutureBuilder<List<Post>>(
future: apiService.getPosts(),
builder: (context, snapshot) ,
)

CHAPITRE 09

State management

📊 Riverpod (recommandé)
// pubspec.yaml → dependencies: flutter_riverpod: ^2.0.0

import 'package:flutter_riverpod/flutter_riverpod.dart';

// 1. Définir un provider
final counterProvider = StateProvider<int>((ref) => 0);

// Provider pour les données async
final postsProvider = FutureProvider<List<Post>>((ref) async );

// 2. Wrapper l'app avec ProviderScope
void main()

// 3. Consommer dans un widget
class CounterScreen extends ConsumerWidget

Solution Complexité Idéal pour
setState Simple État local d'un widget
Riverpod Moyen Apps moyennes à grandes (recommandé)
Bloc/Cubit Complexe Architecture stricte, apps enterprise
Provider Simple Petit état partagé (prédécesseur de Riverpod)

CHAPITRE 10

Stockage local

💾 SharedPreferences et Hive
// SharedPreferences — clé/valeur simple (settings, tokens)
// pubspec.yaml → shared_preferences: ^2.0.0

import 'package:shared_preferences/shared_preferences.dart';

// Sauvegarder
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', 'abc123');
await prefs.setBool('darkMode', true);
await prefs.setInt('launchCount', 5);

// Lire
final token = prefs.getString('token'); // String?
final dark = prefs.getBool('darkMode') ?? false;

// Supprimer
await prefs.remove('token');

// SQLite — base de données relationnelle locale
// pubspec.yaml → sqflite: ^2.0.0, path: ^1.0.0

// Hive — NoSQL ultra-rapide (alternative recommandée)
// pubspec.yaml → hive: ^2.0.0, hive_flutter: ^1.0.0

CHAPITRE 11

Publication

🚀 Publier sur les stores
# Build
flutter build apk # Android APK
flutter build appbundle # Android AAB (Play Store)
flutter build ios # iOS (nécessite Xcode + Mac)
flutter build web # Web → build/web/

# Icône de l'app
# pubspec.yaml → flutter_launcher_icons: ^0.14.0
flutter pub run flutter_launcher_icons

# Splash screen
# pubspec.yaml → flutter_native_splash: ^2.0.0
flutter pub run flutter_native_splash:create

Plateforme Fichier Store
Android .aab (App Bundle) Google Play Console (25$ une fois)
iOS .ipa App Store Connect (99$/an)
Web build/web/ Vercel, Firebase Hosting, Netlify
macOS .app / .dmg Mac App Store ou distribution directe

CHAPITRE 12

Bonnes pratiques

🧩 Packages essentiels
Besoin Package
HTTP dio
State management flutter_riverpod
Routing go_router
Stockage local shared_preferences, hive
Sérialisation JSON json_serializable + freezed
Icônes flutter_launcher_icons
Images cached_network_image
Firebase firebase_core + plugins
Animations flutter_animate
✅ Bonnes pratiques

✅ À FAIRE
const constructeurs (performance)
• Extraire les widgets en classes
dispose() les controllers
• Riverpod pour l'état partagé
• GoRouter pour la navigation
• Dio pour les appels HTTP
fromJson/toJson pour les modèles
• Thème centralisé (ThemeData)
• Tester sur iOS ET Android

❌ À ÉVITER
• Widget tree trop profond (un seul fichier)
setState pour l'état global
• Oublier dispose() (fuites mémoire)
• Logique métier dans les widgets
• FutureBuilder dans build() sans cache
• Ignorer les null-safety
• print() en production (utiliser logger)
• Hardcoder les couleurs/tailles
• Ignorer les performances (DevTools)

🧠 Quiz
Pourquoi utiliser const partout en Flutter ?
Les widgets const sont créés une seule fois en mémoire et ne sont jamais reconstruits lors des setState. Flutter peut comparer les références et sauter le rebuild. C'est une optimisation simple mais impactante pour la performance — utilisez const dès que possible.
FutureBuilder vs Riverpod pour les données async ?
FutureBuilder relance le Future à chaque rebuild (problème de performance). Riverpod FutureProvider cache automatiquement le résultat, gère le loading/error, et ne relance que si nécessaire. Pour les appels API, préférez toujours Riverpod.

Cours Flutter Complet — Widgets, state management, API et publication

Référence : docs.flutter.dev | pub.dev | Riverpod