Créez votre première app mobile avec Flutter
De zéro à une application fonctionnelle sur Android et iOS — le guide complet en français pour débutants.
Introduction : Pourquoi Flutter en 2026 ?
Vous voulez créer une application mobile mais vous ne savez pas par où commencer ? Vous avez entendu parler de Flutter mais ça reste flou ? Ce guide est fait pour vous.
Flutter est un framework open source créé par Google qui permet de développer des applications pour 6 plateformes (iOS, Android, Web, Windows, macOS, Linux) avec un seul code. En 2026, il est devenu le framework cross-platform le plus utilisé au monde, et pour de bonnes raisons.
Flutter en est aujourd'hui à la version 3.41.5 (mai 2026), avec le moteur Impeller stable, le support complet d'iOS 26 et Android 17, et la syntaxe Dart dot shorthands qui rend le code plus propre que jamais.
Flutter vs React Native vs Natif
Avant de commencer, comparons les options qui s'offrent à vous :
| Critère | Flutter | React Native | Natif |
|---|---|---|---|
| Langage | Dart | JavaScript | Swift / Kotlin |
| Plateformes | 6 (mobile+web+desktop) | Mobile + Web | 1 par OS |
| Performance | Natif-like (Impeller) | Bridge JavaScript | Maximum |
| UI cohérence | 100% identique | Varie par OS | Différent |
| Hot Reload | Sub-seconde | Fast Refresh | Non / Lent |
| Temps de dev MVP | 12-16 semaines | 14-20 semaines | 20-28 semaines |
| Coût relatif | 1x | 1.3x | 2-3x |
Pour 90% des projets, Flutter offre le meilleur rapport qualité/prix/temps. Les seules exceptions : si votre équipe est déjà experte React/JavaScript (React Native), ou si vous avez besoin d'un accès jour-1 aux dernières APIs hardware (natif).
Ce dont vous avez besoin
Matériel requis
- Ordinateur : Windows 10+, macOS 12+, ou Ubuntu 22.04+
- RAM : 8 GB minimum (16 GB recommandé)
- Disque : 10 GB d'espace libre
- Pour tester sur iOS : Un Mac est obligatoire (Apple l'exige)
Connaissances recommandées
- Notions de base en programmation (variables, boucles, fonctions)
- Connaissance d'un langage orienté objet est un plus (Java, Python, C#...)
- Pas besoin de connaître Dart — on l'apprend ensemble dans ce tutoriel
Installation complète
Étape 1 : Installer Flutter SDK
Sur Linux (Ubuntu/Debian)
# Installer les dépendances
sudo apt update
sudo apt install -y curl git unzip xz-utils zip
# Télécharger Flutter
cd ~
git clone https://github.com/flutter/flutter.git -b stable
# Ajouter au PATH
echo 'export PATH="$HOME/flutter/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Vérifier l'installation
flutter --version
Sur Windows
- Téléchargez Flutter SDK depuis flutter.dev
- Extrayez dans
C:\flutter\ - Ajoutez
C:\flutter\binà votre variable PATH - Ouvrez PowerShell et tapez
flutter --version
Sur macOS
# Avec Homebrew (recommandé)
brew install flutter
# Ou manuellement
cd ~
git clone https://github.com/flutter/flutter.git -b stable
echo 'export PATH="$HOME/flutter/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
Étape 2 : Configurer un éditeur
Installez VS Code (gratuit) ou Android Studio, puis ajoutez les extensions :
- Flutter (extension officielle)
- Dart (extension officielle)
Étape 3 : Vérifier l'installation
flutter doctor
Cette commande vérifie que tout est correctement installé. Corrigez les éventuels problèmes signalés avant de continuer.
Étape 4 : Créer votre premier projet
# Créer un nouveau projet
flutter create mon_app
# Ouvrir le projet
cd mon_app
# Lancer l'app (assurez-vous d'avoir un émulateur ou un appareil connecté)
flutter run
Si tout fonctionne, vous verrez l'app de démonstration Flutter avec un compteur. Félicitations, votre environnement est prêt !
Les bases de Dart en 10 minutes
Dart est le langage utilisé par Flutter. Si vous connaissez Java, JavaScript ou Python, vous serez en terrain familier. Voici l'essentiel.
Variables et types
// Types explicites
String nom = 'Matteo';
int age = 25;
double pi = 3.14;
bool estDev = true;
List<String> langages = ['Dart', 'Python', 'PHP'];
// Inférence de type (recommandé)
var ville = 'Port-Louis'; // Dart devine que c'est un String
final pays = 'Île Maurice'; // Constante (ne peut pas être réassignée)
const gravite = 9.81; // Constante de compilation
Fonctions
// Fonction classique
String saluer(String nom) {
return 'Bonjour, $nom !';
}
// Fonction fléchée (une ligne)
String saluer2(String nom) => 'Bonjour, $nom !';
// Paramètres optionnels nommés
void creerUtilisateur({
required String nom,
int age = 18,
String? email, // Nullable (peut être null)
}) {
print('$nom a $age ans');
}
Classes
class Utilisateur {
final String nom;
final int age;
// Constructeur court (Dart style)
Utilisateur({required this.nom, required this.age});
// Méthode
String sePresenter() => 'Je suis $nom, $age ans';
}
// Utilisation
final user = Utilisateur(nom: 'Matteo', age: 25);
print(user.sePresenter());
Null Safety
// Dart utilise le "sound null safety"
// Une variable ne peut PAS être null sauf si vous le dites explicitement
String nom = 'Matteo'; // Ne peut JAMAIS être null
String? email; // Peut être null (noté avec ?)
// Vérification
if (email != null) {
print(email.length); // Safe
}
// Opérateur ?? (valeur par défaut)
String affichage = email ?? 'Pas d\'email';
// Opérateur ?. (appel conditionnel)
int? longueur = email?.length; // null si email est null
Nouveauté 2026 : Dot Shorthands
// Avant
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
)
// Maintenant (Dart 3.10+)
Column(
mainAxisAlignment: .center,
crossAxisAlignment: .start,
)
Cette syntaxe inspirée de Swift rend le code Flutter beaucoup plus lisible.
Comprendre les Widgets
En Flutter, tout est un Widget. Un bouton, un texte, une image, une page entière — tout est un widget. Il existe deux types :
StatelessWidget — Exemple
class CarteProfile extends StatelessWidget {
final String nom;
final String role;
const CarteProfile({
super.key,
required this.nom,
required this.role,
});
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
CircleAvatar(radius: 40, child: Text(nom[0])),
SizedBox(height: 12),
Text(nom, style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
)),
Text(role, style: TextStyle(color: Colors.grey)),
],
),
);
}
}
StatefulWidget — Exemple
class Compteur extends StatefulWidget {
const Compteur({super.key});
@override
State<Compteur> createState() => _CompteurState();
}
class _CompteurState extends State<Compteur> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: .center,
children: [
Text(
'$_count',
style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: Text('Incrémenter'),
),
],
);
}
}
Les widgets de layout essentiels
| Widget | Rôle | Analogie HTML |
|---|---|---|
Column | Empile les enfants verticalement | div avec flex-direction: column |
Row | Aligne les enfants horizontalement | div avec flex-direction: row |
Container | Boîte avec padding, margin, décoration | div avec du CSS |
Scaffold | Structure de page (appbar, body, fab) | body + header + main |
ListView | Liste scrollable | div avec overflow: scroll |
Stack | Superpose les enfants | position: relative/absolute |
Padding | Ajoute de l'espace intérieur | padding en CSS |
SizedBox | Espace fixe entre les widgets | margin ou gap |
Construire l'app pas à pas
Construisons une application concrète : une app de tâches (Todo List) avec ajout, suppression et persistance locale.
Structure du projet
lib/
├── main.dart # Point d'entrée
├── models/
│ └── task.dart # Modèle de données
├── screens/
│ └── home_screen.dart # Page principale
└── widgets/
└── task_tile.dart # Widget d'une tâche
1. Le modèle de données
// lib/models/task.dart
class Task {
final String id;
final String title;
bool isDone;
Task({
required this.id,
required this.title,
this.isDone = false,
});
}
2. Le widget d'une tâche
// lib/widgets/task_tile.dart
import 'package:flutter/material.dart';
import '../models/task.dart';
class TaskTile extends StatelessWidget {
final Task task;
final VoidCallback onToggle;
final VoidCallback onDelete;
const TaskTile({
super.key,
required this.task,
required this.onToggle,
required this.onDelete,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Checkbox(
value: task.isDone,
onChanged: (_) => onToggle(),
),
title: Text(
task.title,
style: TextStyle(
decoration: task.isDone
? TextDecoration.lineThrough
: TextDecoration.none,
color: task.isDone ? Colors.grey : Colors.white,
),
),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red[300]),
onPressed: onDelete,
),
);
}
}
3. L'écran principal
// lib/screens/home_screen.dart
import 'package:flutter/material.dart';
import '../models/task.dart';
import '../widgets/task_tile.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final List<Task> _tasks = [];
final _controller = TextEditingController();
void _addTask() {
if (_controller.text.trim().isEmpty) return;
setState(() {
_tasks.add(Task(
id: DateTime.now().toString(),
title: _controller.text.trim(),
));
_controller.clear();
});
}
void _toggleTask(int index) {
setState(() => _tasks[index].isDone = !_tasks[index].isDone);
}
void _deleteTask(int index) {
setState(() => _tasks.removeAt(index));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Mes Tâches'),
centerTitle: true,
),
body: Column(
children: [
// Barre d'ajout
Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Nouvelle tâche...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
onSubmitted: (_) => _addTask(),
),
),
SizedBox(width: 12),
FloatingActionButton(
onPressed: _addTask,
child: Icon(Icons.add),
),
],
),
),
// Liste des tâches
Expanded(
child: _tasks.isEmpty
? Center(child: Text('Aucune tâche'))
: ListView.builder(
itemCount: _tasks.length,
itemBuilder: (context, index) => TaskTile(
task: _tasks[index],
onToggle: () => _toggleTask(index),
onDelete: () => _deleteTask(index),
),
),
),
],
),
);
}
}
4. Le point d'entrée
// lib/main.dart
import 'package:flutter/material.dart';
import 'screens/home_screen.dart';
void main() => runApp(const MonApp());
class MonApp extends StatelessWidget {
const MonApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo App',
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(useMaterial3: true).copyWith(
colorScheme: ColorScheme.dark(
primary: Colors.cyanAccent,
surface: Color(0xFF121212),
),
),
home: HomeScreen(),
);
}
}
flutter run et vous avez une Todo app complète fonctionnelle ! Ajoutez des tâches, cochez-les, supprimez-les.Gérer l'état (State Management)
Pour des apps simples, setState suffit. Mais quand l'app grandit (plusieurs pages qui partagent des données), vous avez besoin d'une solution plus robuste.
| Solution | Complexité | Idéal pour |
|---|---|---|
| setState | Simple | Apps de 1-2 pages |
| Provider | Simple | Apps moyennes, débutants |
| Riverpod | Moyen | Apps complexes (recommandé en 2026) |
| Bloc / Cubit | Avancé | Apps enterprise, équipes |
En 2026, Riverpod est devenu le standard de facto. Il est type-safe, testable, et bien documenté.
Navigation entre les pages
Utilisez go_router, le package de navigation officiel recommandé par Google :
// pubspec.yaml
dependencies:
go_router: ^14.0.0
// Configuration des routes
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/details/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailsScreen(taskId: id);
},
),
],
);
// Naviguer
context.go('/details/123');
Connecter une API REST
Utilisez le package dio pour les appels HTTP :
// pubspec.yaml
dependencies:
dio: ^5.4.0
import 'package:dio/dio.dart';
class ApiService {
final _dio = Dio(BaseOptions(
baseUrl: 'https://jsonplaceholder.typicode.com',
));
Future<List<dynamic>> getTodos() async {
final response = await _dio.get('/todos');
return response.data;
}
Future<void> createTodo(String title) async {
await _dio.post('/todos', data: {
'title': title,
'completed': false,
});
}
}
Les packages essentiels en 2026
| Package | Rôle | Commande |
|---|---|---|
| riverpod | State management | flutter pub add flutter_riverpod |
| go_router | Navigation déclarative | flutter pub add go_router |
| dio | HTTP / appels API | flutter pub add dio |
| hive | Base de données locale rapide | flutter pub add hive |
| freezed | Data classes immuables | flutter pub add freezed_annotation |
| cached_network_image | Images avec cache | flutter pub add cached_network_image |
| easy_localization | Traductions FR/EN | flutter pub add easy_localization |
| flutter_animate | Animations simples | flutter pub add flutter_animate |
Compiler et déployer
Générer l'APK Android
# APK de debug (pour tester)
flutter build apk --debug
# APK de release (pour publier)
flutter build apk --release
# App Bundle (recommandé pour le Play Store)
flutter build appbundle --release
L'APK se trouve dans build/app/outputs/flutter-apk/
Générer l'app iOS
# Nécessite un Mac avec Xcode
flutter build ios --release
Générer l'app Web
flutter build web --release
# Le dossier build/web/ peut être déployé sur n'importe quel hébergeur
Erreurs courantes et solutions
"Gradle build failed" (Android)
- Vérifiez que
minSdkVersionest à 21+ dansandroid/app/build.gradle - Lancez
flutter cleanpuisflutter pub get - Vérifiez votre version de Java (Java 17 recommandé)
"Widget overflow" (écran jaune/noir)
- Encapsulez les listes dans
ExpandedouFlexible - Utilisez
SingleChildScrollViewpour les pages qui dépassent l'écran - Vérifiez les
Columnà l'intérieur deColumn(problème classique)
"setState() called after dispose()"
- Vérifiez que le widget est toujours monté :
if (mounted) setState(...); - Annulez les timers et subscriptions dans
dispose()
Hot Reload ne fonctionne plus
- Arrêtez l'app et relancez avec
flutter run - Certains changements (ajout de plugins, changements natifs) nécessitent un hot restart (
Rmajuscule dans le terminal)
Pour aller plus loin
Vous avez maintenant les bases solides pour construire des applications Flutter. Voici la suite du parcours :
Ressources recommandées
- Documentation officielle : docs.flutter.dev
- Dart : dart.dev
- Codelabs Google : Tutoriels interactifs Flutter
- pub.dev : pub.dev — Le registre officiel des packages Dart/Flutter
- YouTube : Fireship, The Net Ninja, Robert Brunhage
Conclusion
Flutter en 2026 n'est plus un framework expérimental — c'est un outil de production utilisé par des entreprises du Fortune 500. Avec Impeller, le AI Toolkit, et l'écosystème mature de packages, c'est le meilleur moment pour commencer.
Ce que vous avez appris dans ce tutoriel : les bases de Dart et du null safety, le système de widgets (Stateless et Stateful), la construction d'une app complète (Todo List), le state management et la navigation, les appels API et les packages essentiels, et la compilation et le déploiement.
La prochaine étape ? Construisez quelque chose. Le meilleur moyen d'apprendre Flutter, c'est de coder. Prenez une idée simple et réalisez-la. Votre première app ne sera pas parfaite, et c'est normal.
Retrouvez plus de tutoriels, projets et ressources sur mat-univer.tech. Et n'hésitez pas à partager vos créations Flutter dans les commentaires !