Introduction
Flutter, framework open-source de Google, révolutionne le développement mobile en 2026 grâce à son rendu natif ultra-performant et son paradigme declarative UI. Contrairement aux frameworks hybrides comme React Native, Flutter compile en code natif ARM, offrant des performances identiques à Swift ou Kotlin, tout en utilisant un seul codebase pour iOS, Android, web et desktop.
Pourquoi l'adopter maintenant ? En 2026, 70% des apps Fortune 500 utilisent Flutter pour sa rapidité de développement (hot reload en <1s) et son widget kit riche (Material 3, Cupertino). Ce tutoriel débutant vous guide pas à pas pour créer une app Counter évolutive vers une Todo List, avec 100% de code copier-collable. À la fin, vous maîtriserez les bases : widgets stateless/stateful, gestion d'état simple, navigation et déploiement. Prêt à booster votre carrière dev mobile ? (128 mots)
Prérequis
- Ordinateur avec macOS, Windows ou Linux (8 Go RAM min.)
- Flutter SDK version 3.24+ (stable 2026)
- Android Studio (pour émulateur Android) ou Xcode (macOS pour iOS)
- VS Code avec extensions Flutter et Dart
- Connaissances basiques en programmation (pas de Dart requis, on apprend en chemin)
Installer et vérifier Flutter
git clone https://github.com/flutter/flutter.git -b stable
export PATH="$PATH:`pwd`/flutter/bin"
flutter precache
flutter doctor -vCes commandes clonent le SDK Flutter stable, l'ajoutent au PATH et vérifient l'installation. flutter doctor liste les outils manquants (ex: Android SDK). Évitez les versions beta pour un débutant ; relancez votre terminal après export PATH.
Créer votre premier projet
Analogie : Créer un projet Flutter est comme initialiser un nouveau dossier Git, mais avec un squelette d'app prêt à coder. Cela génère lib/main.dart (point d'entrée), pubspec.yaml (dépendances) et les configs iOS/Android.
Générer le projet
flutter create ma_premiere_app
cd ma_premiere_app
flutter pub getflutter create scaffold un projet complet avec Material Design par défaut. pub get résout les dépendances. Lancez flutter run pour tester sur émulateur ; si erreur, vérifiez flutter doctor.
App de base : main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Ma Première App',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyHomePage(title: 'Accueil'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Vous avez appuyé tant de fois:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}Ce code crée une app Counter complète : runApp lance l'app, MaterialApp gère le thème/routing, StatefulWidget met à jour l'UI via setState. Copiez-collez dans lib/main.dart, puis flutter run. Piège : Oubliez const pour les performances ; key évite les rebuilds inutiles.
Comprendre les widgets
Flutter est basé sur des widgets immutables : tout est un widget (UI + logique). Stateless pour statique (ex: icônes), Stateful pour dynamique (ex: compteur). L'arbre de widgets se reconstruit à chaque setState, comme un arbre DOM réactif mais plus rapide.
Ajouter une ListView
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Ma App ListView',
theme: ThemeData(primarySwatch: Colors.green),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Ma ListView')),
body: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
leading: const Icon(Icons.star),
title: Text('Item $index'),
subtitle: Text('Sous-titre $index'),
trailing: const Icon(Icons.arrow_forward_ios),
);
},
),
);
}
}Remplacez le body par ListView.builder pour une liste infinie optimisée (lazy loading). itemBuilder génère dynamiquement ; itemCount limite. Idéal pour les listes longues. Évitez ListView fixe pour les perfs mobiles.
Todo List avec état
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo App',
theme: ThemeData(primarySwatch: Colors.orange),
home: const TodoListScreen(),
);
}
}
class TodoListScreen extends StatefulWidget {
const TodoListScreen({super.key});
@override
State<TodoListScreen> createState() => _TodoListScreenState();
}
class _TodoListScreenState extends State<TodoListScreen> {
final List<String> _todos = [];
final TextEditingController _controller = TextEditingController();
void _addTodo() {
if (_controller.text.isNotEmpty) {
setState(() {
_todos.add(_controller.text);
_controller.clear();
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Mes Todos')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(hintText: 'Nouvelle todo'),
),
),
IconButton(
onPressed: _addTodo,
icon: const Icon(Icons.add),
),
],
),
),
Expanded(
child: ListView.builder(
itemCount: _todos.length,
itemBuilder: (context, index) => ListTile(
title: Text(_todos[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => setState(() => _todos.removeAt(index)),
),
),
),
),
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}Cette Todo App gère l'état avec List, TextEditingController pour input, et dispose pour cleanup. setState trigger rebuilds ciblés. Ajoute/supprime dynamiquement. Piège : Oublier dispose fuit la mémoire ; utilisez Expanded pour scroll fluide.
Lancer l'app
flutter run -d chrome # Pour web
# ou
flutter run # Pour émulateur connecté
# Hot reload: tapez 'r'
# Hot restart: 'R'flutter run compile et lance sur device/émulateur/web. -d chrome pour test rapide web. Hot reload préserve l'état. Vérifiez flutter devices pour lister les cibles.
Bonnes pratiques
- Hot Reload : Sauvegardez (Ctrl+S) pour itérer vite, mais
Hot Restartpour changements d'état. - Utilisez
constpartout pour optimiser les rebuilds. - State Management : SetState pour débutants ; passez à Provider/Riverpod pour apps complexes.
- Testez sur vrais devices : émulateurs lent pour animations.
- Pubspec.yaml : Ajoutez deps comme
http: ^1.2.0pour API, puisflutter pub get.
Erreurs courantes à éviter
- Indentation/{} manquants : Dart est strict ; VS Code auto-format (Ctrl+Shift+I).
- Pas de setState : UI ne se met pas à jour (classique pour débutants).
- Controller non disposé : Fuites mémoire sur Android/iOS.
- flutter clean oublié : Après changements pubspec, tapez
flutter clean && flutter pub get.
Pour aller plus loin
- Docs officielles Flutter
- Codelabs interactifs
- Maîtrisez Riverpod pour state : Tutoriel avancé
- Découvrez nos formations Flutter Learni pour pro (widgets avancés, Firebase, déploiement Play Store).