Questions d'Entretien Technique
Développement Mobile - Flutter
Date: 23/12/2025
1. Quelle est la différence entre StatelessWidget et StatefulWidget ?
Réponse :
StatelessWidget :
• Widget immuable qui ne change pas d'état
• Reconstruit uniquement lorsque ses paramètres externes changent
• Plus léger et plus performant
• Utilisé pour l'affichage de contenu statique
• Exemple : Text, Icon, Image
StatefulWidget :
• Widget qui peut changer d'état dynamiquement
• Possède un objet State qui conserve les données modifiables
• Peut se reconstruire via setState()
• Utilisé pour du contenu interactif ou changeant
• Exemple : Checkbox, TextField, animations
Exemple de code :
// StatelessWidget class MyStatelessWidget extends StatelessWidget { @override
Widget build(BuildContext context) { return Text('Je ne change pas'); } } //
StatefulWidget class MyStatefulWidget extends StatefulWidget { @override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState(); } class
_MyStatefulWidgetState extends State<MyStatefulWidget> { int counter = 0; @override
Widget build(BuildContext context) { return Column( children: [ Text('Counter:
$counter'), ElevatedButton( onPressed: () => setState(() => counter++), child:
Text('Increment'), ), ], ); } }
2. Qu'est-ce que le widget tree et comment Flutter gère-t-il le rendu ?
Réponse :
Le Widget Tree (Arbre de Widgets) :
• Structure hiérarchique de tous les widgets de l'application
• Chaque widget est un nœud dans l'arbre
• Les widgets enfants sont contenus dans leurs parents
Processus de rendu Flutter :
1. Widget Tree : Configuration immuable de l'UI
2. Element Tree : Instance des widgets, gère le cycle de vie
3. Render Tree : Objets qui effectuent le rendu réel
Mécanisme de rebuild :
• Flutter compare les anciens et nouveaux widgets
• Seuls les widgets modifiés sont reconstruits (rebuild)
• Optimisation via les "keys" pour identifier les widgets
• Le framework minimise les opérations coûteuses
Avantages :
• Performance élevée (60 fps)
• Rendu efficace avec reconciliation intelligente
• Hot reload rapide grâce à cette architecture
3. Expliquez le cycle de vie d'un StatefulWidget
Réponse :
Phases du cycle de vie :
1. createState()
• Premier appel lors de la création du widget
• Crée l'objet State associé
2. initState()
• Appelé une seule fois après la création
• Initialisation des variables, abonnements, controllers
• Ne peut pas utiliser BuildContext ici pour certaines opérations
3. didChangeDependencies()
• Appelé après initState() et quand les dépendances changent
• Accès au BuildContext disponible
4. build()
• Appelé à chaque fois que le widget doit être rendu
• Retourne l'arbre de widgets à afficher
• Appelé après setState()
5. didUpdateWidget()
• Appelé quand le widget parent se reconstruit avec une nouvelle configuration
• Permet de comparer l'ancien et le nouveau widget
6. setState()
• Notifie Flutter qu'il y a un changement d'état
• Déclenche un nouveau build()
7. deactivate()
• Appelé quand le widget est retiré de l'arbre temporairement
8. dispose()
• Appelé quand le widget est définitivement retiré
• Libération des ressources (controllers, subscriptions, listeners)
class MyWidget extends StatefulWidget { @override _MyWidgetState createState() =>
_MyWidgetState(); } class _MyWidgetState extends State<MyWidget> { @override void
initState() { [Link](); print('1. initState - Initialisation'); }
@override void didChangeDependencies() { [Link](); print('2.
didChangeDependencies'); } @override Widget build(BuildContext context) { print('3.
build - Construction du widget'); return Container(); } @override void
didUpdateWidget(MyWidget oldWidget) { [Link](oldWidget); print('4.
didUpdateWidget'); } @override void dispose() { print('5. dispose - Nettoyage');
[Link](); } }
4. Quelle est la différence entre hot reload et hot restart ?
Réponse :
Hot Reload :
• Injecte les fichiers modifiés dans la Dart VM
• Préserve l'état de l'application (variables, données)
• Reconstruction rapide de l'UI uniquement
• Ne ré-exécute pas la fonction main()
• Idéal pour les changements d'UI
• Temps : ~1-2 secondes
• Raccourci : r dans le terminal, ou Ctrl+\ (VSCode)
Limitations du Hot Reload :
• Ne fonctionne pas pour les changements de structure de classe
• Ne recharge pas les variables globales ou statiques
• Ne s'applique pas aux changements d'énumérations
Hot Restart :
• Redémarre complètement l'application
• Perd tout l'état de l'application
• Ré-exécute la fonction main()
• Réinitialise toutes les variables
• Nécessaire pour les changements structurels
• Temps : ~5-10 secondes
• Raccourci : R dans le terminal, ou Ctrl+Shift+\ (VSCode)
Quand utiliser Hot Restart :
• Modifications de la fonction main()
• Changements dans les variables globales
• Modifications d'énumérations
• Ajout/suppression de fichiers
• Changements dans les assets ([Link])
5. Comment gérez-vous la navigation entre les écrans dans Flutter ?
Réponse :
Méthode 1 : Navigation basique avec Navigator
// Navigation vers un écran [Link]( context, MaterialPageRoute(builder:
(context) => SecondScreen()), ); // Retour à l'écran précédent
[Link](context); // Retour avec une valeur [Link](context,
'Résultat'); // Navigation avec remplacement [Link]( context,
MaterialPageRoute(builder: (context) => NewScreen()), );
Méthode 2 : Routes nommées
// Dans MaterialApp MaterialApp( initialRoute: '/', routes: { '/': (context) =>
HomeScreen(), '/second': (context) => SecondScreen(), '/third': (context) =>
ThirdScreen(), }, ); // Navigation avec route nommée [Link](context,
'/second'); // Avec arguments [Link]( context, '/second', arguments:
{'id': 123, 'name': 'Flutter'}, ); // Récupérer les arguments final args =
[Link](context)!.[Link] as Map;
Méthode 3 : Routes générées (recommandé pour grandes apps)
MaterialApp( onGenerateRoute: (settings) { if ([Link] == '/second') { final
args = [Link] as Map; return MaterialPageRoute( builder: (context) =>
SecondScreen(data: args), ); } return null; }, );
Méthodes avancées :
• pushAndRemoveUntil() : Navigation avec suppression de l'historique
• popUntil() : Retour jusqu'à une route spécifique
• canPop() : Vérifier s'il est possible de revenir en arrière
• Navigator 2.0 : Pour des besoins avancés (deep linking, web)
Packages populaires :
• go_router : Navigation déclarative moderne
• auto_route : Génération automatique de routes
• beamer : Navigation avancée avec support web
6. Qu'est-ce que le BuildContext et pourquoi est-il important ?
Réponse :
Définition :
• Référence à l'emplacement d'un widget dans l'arbre de widgets
• Handle vers l'arbre, permettant de naviguer dans la hiérarchie
• Chaque widget possède son propre BuildContext
Utilisations principales :
1. Accès au Theme :
final theme = [Link](context); final primaryColor = [Link]; final
textTheme = [Link];
2. Navigation :
[Link](context).push(...); [Link](context).pop();
3. Affichage de dialogs et snackbars :
[Link](context).showSnackBar( SnackBar(content: Text('Message')), );
showDialog( context: context, builder: (context) => AlertDialog(...), );
4. Accès aux dimensions de l'écran :
final size = [Link](context).size; final width = [Link]; final height =
[Link];
5. Localisation :
final locale = [Link](context);
Important :
• Le context doit appartenir à un widget sous celui qui fournit les données
• Erreur fréquente : utiliser le context du Scaffold pour afficher un SnackBar
Solution avec Builder :
Scaffold( body: Builder( builder: (BuildContext context) { return TextButton(
onPressed: () { [Link](context).showSnackBar( SnackBar(content:
Text('OK!')), ); }, child: Text('Show SnackBar'), ); }, ), );
7. Quelle est la différence entre MainAxisAlignment et
CrossAxisAlignment ?
Réponse :
Ces propriétés contrôlent l'alignement des enfants dans les widgets Row et Column.
MainAxisAlignment (Axe principal) :
• Pour Column : Axe vertical (haut → bas)
• Pour Row : Axe horizontal (gauche → droite)
Valeurs disponibles :
• start : Début de l'axe
• end : Fin de l'axe
• center : Centre
• spaceBetween : Espace égal entre les éléments
• spaceAround : Espace égal autour de chaque élément
• spaceEvenly : Espace égal partout (avant, entre, après)
CrossAxisAlignment (Axe perpendiculaire) :
• Pour Column : Axe horizontal
• Pour Row : Axe vertical
Valeurs disponibles :
• start : Début de l'axe perpendiculaire
• end : Fin de l'axe perpendiculaire
• center : Centre
• stretch : Étire les enfants pour remplir l'axe
• baseline : Aligne sur la ligne de base du texte
Exemples :
// Column Column( mainAxisAlignment: [Link], // Vertical
crossAxisAlignment: [Link], // Horizontal children: [
Text('Premier'), Text('Deuxième'), Text('Troisième'), ], ) // Row Row(
mainAxisAlignment: [Link], // Horizontal crossAxisAlignment:
[Link], // Vertical children: [ Icon([Link]),
Icon([Link]), Icon([Link]), ], )
8. Comment gérez-vous les appels API asynchrones dans Flutter ?
Réponse :
Méthode 1 : async/await avec Future
import 'dart:convert'; import 'package:http/[Link]' as http; // Fonction
asynchrone Future<List<User>> fetchUsers() async { final response = await [Link](
[Link]('[Link] ); if ([Link] == 200) {
List data = [Link]([Link]); return [Link]((json) =>
[Link](json)).toList(); } else { throw Exception('Erreur de chargement'); }
} // Utilisation dans un widget class UserList extends StatefulWidget { @override
_UserListState createState() => _UserListState(); } class _UserListState extends
State<UserList> { late Future<List<User>> futureUsers; @override void initState() {
[Link](); futureUsers = fetchUsers(); } @override Widget
build(BuildContext context) { return Scaffold( body: FutureBuilder<List<User>>(
future: futureUsers, builder: (context, snapshot) { if ([Link]) { return
[Link]( itemCount: [Link]!.length, itemBuilder: (context, index) {
return ListTile( title: Text([Link]![index].name), ); }, ); } else if
([Link]) { return Center(child: Text('Erreur: \${[Link]}')); }
return Center(child: CircularProgressIndicator()); }, ), ); } }
Méthode 2 : Gestion manuelle avec setState
class DataWidget extends StatefulWidget { @override _DataWidgetState createState()
=> _DataWidgetState(); } class _DataWidgetState extends State<DataWidget> {
List<User> users = []; bool isLoading = true; String? error; @override void
initState() { [Link](); loadData(); } Future<void> loadData() async {
setState(() { isLoading = true; error = null; }); try { final data = await
fetchUsers(); setState(() { users = data; isLoading = false; }); } catch (e) {
setState(() { error = [Link](); isLoading = false; }); } } @override Widget
build(BuildContext context) { if (isLoading) { return CircularProgressIndicator();
} if (error != null) { return Text('Erreur: $error'); } return [Link](
itemCount: [Link], itemBuilder: (context, index) { return ListTile(title:
Text(users[index].name)); }, ); } }
Méthode 3 : StreamBuilder pour données en temps réel
Stream<List<Message>> getMessagesStream() { return
[Link](Duration(seconds: 5), (_) { return fetchMessages();
}).asyncMap((future) => future); } // Dans le widget StreamBuilder<List<Message>>(
stream: getMessagesStream(), builder: (context, snapshot) { if ([Link]) {
return ListView( children: [Link]!.map((msg) => Text([Link])
).toList(), ); } return CircularProgressIndicator(); }, )
Bonnes pratiques :
• Toujours gérer les 3 états : loading, success, error
• Utiliser try-catch pour capturer les erreurs
• Annuler les requêtes si le widget est disposed
• Mettre en cache les résultats quand possible
• Utiliser des packages comme dio pour des fonctionnalités avancées
• Provider/Riverpod/Bloc pour la gestion d'état complexe
9. Qu'est-ce que [Link] et à quoi sert-il ?
Réponse :
Définition :
Le fichier [Link] est le fichier de configuration principal d'un projet Flutter/Dart. Il définit
les métadonnées du projet et ses dépendances.
Contenu principal :
1. Informations du projet :
name: mon_application description: Une application Flutter incroyable version:
1.0.0+1 publish_to: 'none' environment: sdk: '>=3.0.0 <4.0.0'
2. Dépendances (packages) :
dependencies: flutter: sdk: flutter # Packages externes http: ^1.1.0 provider:
^6.0.5 shared_preferences: ^2.2.0 dev_dependencies: flutter_test: sdk: flutter
flutter_lints: ^2.0.0
3. Assets (images, fonts, fichiers) :
flutter: uses-material-design: true # Images assets: - assets/images/ -
assets/images/[Link] - assets/data/[Link] # Polices personnalisées fonts: -
family: Roboto fonts: - asset: fonts/[Link] - asset:
fonts/[Link] weight: 700 - family: CustomFont fonts: - asset:
fonts/[Link]
4. Configuration Flutter :
flutter: # Activer Material Design uses-material-design: true # Générer des
fichiers de localisation generate: true
Commandes importantes :
• flutter pub get : Télécharger les dépendances
• flutter pub upgrade : Mettre à jour les packages
• flutter pub outdated : Voir les packages obsolètes
• flutter pub add [package] : Ajouter une dépendance
• flutter pub remove [package] : Supprimer une dépendance
Version des packages :
• ^1.2.3 : Compatible avec 1.x.x (≥1.2.3 <2.0.0)
• 1.2.3 : Version exacte uniquement
• >=1.2.3 <2.0.0 : Plage de versions
• any : N'importe quelle version (déconseillé)
Bonnes pratiques :
• Toujours spécifier des versions pour les dépendances
• Utiliser [Link] pour garantir la reproductibilité
• Ne pas commiter [Link] pour les packages
• Organiser les assets dans des dossiers dédiés
• Vérifier régulièrement les mises à jour de sécurité
10. Expliquez la différence entre Container, Padding et SizedBox
Réponse :
Ces trois widgets sont utilisés pour la mise en page, mais ont des objectifs différents.
1. Container - Le plus polyvalent
Fonctionnalités :
• Padding (espace interne)
• Margin (espace externe)
• Width et Height
• Color et Decoration (bordures, ombres, gradients)
• Alignment
• Transform (rotation, scale)
Exemple :
Container( width: 200, height: 100, padding: [Link](16), margin:
[Link](vertical: 8), decoration: BoxDecoration( color: [Link],
borderRadius: [Link](12), boxShadow: [ BoxShadow( color:
[Link](0.5), spreadRadius: 2, blurRadius: 5, offset: Offset(0, 3),
), ], ), child: Text('Hello', style: TextStyle(color: [Link])), )
2. Padding - Uniquement pour l'espacement interne
Fonctionnalités :
• Ajoute uniquement du padding autour de l'enfant
• Plus léger que Container si on a besoin que de padding
• Pas de decoration, color, ou autres propriétés
Exemple :
Padding( padding: [Link](16), child: Text('Texte avec espacement'), ) //
Différents types de padding Padding( padding: [Link](left: 10, top: 20),
child: Widget(), ) Padding( padding: [Link](horizontal: 16, vertical:
8), child: Widget(), )
3. SizedBox - Pour les dimensions fixes
Fonctionnalités :
• Définit une taille fixe (width, height)
• Très performant pour créer des espaces vides
• Peut contenir un enfant ou être vide
• Plus léger que Container pour les espaces
Exemples :
// Créer un espace vertical SizedBox(height: 20) // Créer un espace horizontal
SizedBox(width: 10) // Forcer une taille spécifique pour un widget SizedBox( width:
150, height: 50, child: ElevatedButton( onPressed: () {}, child: Text('Bouton'), ),
) // Prendre toute la largeur/hauteur disponible [Link]( child:
Container(color: [Link]), ) // Réduire à la taille minimale [Link]()
Comparaison et quand utiliser chaque widget :
Utilisez Container quand :
• Vous avez besoin de décoration (couleur, bordure, ombre)
• Vous voulez combiner padding ET margin
• Vous avez besoin d'alignment ou de transform
• C'est un élément visuel avec plusieurs propriétés
Utilisez Padding quand :
• Vous avez uniquement besoin d'espace interne
• Vous voulez un code plus lisible et explicite
• Performance légèrement meilleure que Container
Utilisez SizedBox quand :
• Vous créez des espaces entre widgets (SizedBox(height: 20))
• Vous forcez une taille fixe pour un widget
• Vous voulez le widget le plus léger possible
• Vous créez un widget invisible ([Link]())
Exemple de comparaison :
// Avec Container (plus lourd mais plus de fonctionnalités) Container( width: 100,
height: 100, padding: [Link](8), decoration: BoxDecoration(color:
[Link]), child: Text('Hello'), ) // Avec Padding + SizedBox (plus léger)
SizedBox( width: 100, height: 100, child: ColoredBox( color: [Link], child:
Padding( padding: [Link](8), child: Text('Hello'), ), ), ) // Pour les
espaces (utilisez TOUJOURS SizedBox) Column( children: [ Text('Premier'),
SizedBox(height: 16), // ✓ Bon Text('Deuxième'), Container(height: 16), // ✗
Mauvais (trop lourd) ], )
________________________________________________________________________________
Document généré pour l'entretien technique Flutter