AccueilAndroidCallbacks Flutter pour jeux et formulaires

Callbacks Flutter pour jeux et formulaires

VoidCallback envoie un signal sans donnée, tandis que Function Callback retourne une valeur au widget parent. Ce guide couvre l’implémentation, les différences techniques et le choix du callback adapté pour les apps Flutter de toutes tailles, y compris le développement de jeux et les interfaces professionnelles.

Dans l’architecture Flutter, la communication du widget enfant vers le parent est un défi classique. Deux solutions légères et natives sont VoidCallback et Function Callback. Toutes deux permettent à un enfant de notifier son parent sans gestion d’état lourde comme Provider ou Bloc. Cependant, de nombreux débutants choisissent le mauvais type, ce qui produit un code inefficace ou difficile à maintenir. Cet article explique les différences fondamentales, l’implémentation correcte et les cas d’usage idéaux, avec des exemples de code prêts à exécuter et des comparaisons de performances.

Les callbacks sont simplement des fonctions passées en paramètres. Dans Flutter, VoidCallback équivaut à void Function(), tandis que Function Callback peut être void Function(int) ou void Function(String, double) selon les besoins. Choisir le bon simplifie le flux de données et réduit les bugs.

Comprendre VoidCallback : Signal sans charge

VoidCallback est un typedef natif de Flutter : typedef VoidCallback = void Function(). Il ne prend aucun argument et ne retourne aucune valeur. Utilisez-le quand un widget enfant a seulement besoin d’informer son parent qu’un événement s’est produit, sans données supplémentaires. Exemples classiques : bouton « J’aime », bouton « Suivant » ou un interrupteur qui bascule un booléen dans le parent.

Le principal avantage de VoidCallback est sa simplicité. Le code reste lisible et ne gaspille pas de mémoire avec des données inutilisées. Mais la limitation est claire : le parent ne reçoit aucune information supplémentaire. Par exemple, si vous avez plusieurs boutons similaires, le parent ne peut pas distinguer lequel a été pressé en utilisant uniquement VoidCallback.

Function Callback : Envoyer des données au parent

Contrairement à VoidCallback, un Function Callback (souvent appelé callback avec paramètres) permet à un widget enfant d’envoyer une ou plusieurs valeurs au parent. Il est flexible : Function(int), Function(String) ou même Function(double, bool). Les données envoyées peuvent être une saisie utilisateur, une position de joueur dans un jeu ou des valeurs de formulaire.

Un exemple concret : un curseur de volume dans un widget enfant envoie une valeur double de 0.0 à 1.0 au parent, qui met alors à jour le niveau sonore de l’application. Sans Function Callback, le parent ne connaîtrait jamais la valeur exacte sélectionnée. Par conséquent, les composants interactifs comme Slider, TextField ou DropdownButton requièrent pratiquement Function Callback.

Quand utiliser VoidCallback ? Quand le parent a seulement besoin de savoir « que » quelque chose s’est produit, pas « quoi » ou « combien ». Exemples : boutons de navigation, boutons d’actualisation ou déclencheurs d’animation.
Quand utiliser Function Callback ? Quand le parent a besoin de données spécifiques de l’enfant, comme des nombres, du texte ou des objets. Exemples : formulaires de connexion, contrôles de jeu ou filtres de recherche.

Configuration d’un projet Flutter

Avant d’implémenter des callbacks, assurez-vous que votre environnement Flutter est prêt. Créez un nouveau projet avec flutter create callback_demo ou utilisez Android Studio. Pour les détails d’installation, consultez le guide officiel Flutter. Après avoir ouvert le projet, nettoyez le lib/main.dart par défaut et préparez la structure de fichiers suivante.

Structure recommandée pour séparer les responsabilités :

  • main.dart – point d’entrée, thème et routes.
  • parent_widget.dart – widget parent contenant l’état et les fonctions callback.
  • voidcallback_child_widget.dart – bouton avec VoidCallback.
  • function_child_widget.dart – bouton avec Function Callback (envoie un entier).
  • slider_child_widget.dart – (optionnel) exemple supplémentaire avec Function Callback envoyant un double.

Commencez par modifier main.dart comme ci-dessous.

import 'package:flutter/material.dart';
import 'parent_widget.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Callback Deep Dive',
      theme: ThemeData(primarySwatch: Colors.indigo),
      home: const ParentWidgetPage(title: 'VoidCallback vs Function Callback'),
    );
  }
}

Construction du widget parent avec état

Le widget parent maintient un compteur et fournit deux fonctions : _voidCallback (incrémente de 1) et _functionCallback(int i) (incrémente de la valeur donnée). Celles-ci seront passées aux widgets enfants comme propriétés de callback. Nous ajoutons également un bouton de réinitialisation pour plus de clarté.

import 'package:flutter/material.dart';
import 'voidcallback_child_widget.dart';
import 'function_child_widget.dart';

class ParentWidgetPage extends StatefulWidget {
  const ParentWidgetPage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<ParentWidgetPage> createState() => _ParentWidgetPageState();
}

class _ParentWidgetPageState extends State<ParentWidgetPage> {
  int _counter = 0;

  void _voidCallback() {
    setState(() {
      _counter++;
    });
  }

  void _functionCallback(int i) {
    setState(() {
      _counter += i;
    });
  }

  void _resetCounter() {
    setState(() {
      _counter = 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Nombre total de clics (ou valeur accumulée) :',
                style: TextStyle(fontSize: 16)),
            Text('$_counter',
                style: Theme.of(context).textTheme.headline4?.copyWith(
                      fontWeight: FontWeight.bold,
                      color: Colors.indigo,
                    )),
            const SizedBox(height: 20),
            ElevatedButton.icon(
              onPressed: _resetCounter,
              icon: const Icon(Icons.refresh),
              label: const Text('Réinitialiser le compteur'),
              style: ElevatedButton.styleFrom(backgroundColor: Colors.grey),
            ),
          ],
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButton: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            VoidChildWidgetPage(voidCallback: _voidCallback),
            FunctionChildWidgetPage(functionCallback: _functionCallback),
          ],
        ),
      ),
    );
  }
}

Dans le code ci-dessus, setState met à jour l’interface chaque fois qu’un callback est déclenché. Les boutons flottants sont placés dans une rangée avec deux widgets enfants, chacun recevant le callback approprié. Le bouton de réinitialisation facilite la démonstration sans redémarrer l’application.

Création d’un widget enfant avec VoidCallback

Le widget enfant pour VoidCallback est très simple. Il accepte une propriété voidCallback de type VoidCallback et l’appelle lorsque le bouton est pressé. Aucune donnée n’est renvoyée. Pour le rendre plus informatif, nous ajoutons une icône et une infobulle.

import 'package:flutter/material.dart';

class VoidChildWidgetPage extends StatelessWidget {
  final VoidCallback voidCallback;

  const VoidChildWidgetPage({Key? key, required this.voidCallback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Tooltip(
      message: 'Ajoute 1 au compteur sans envoyer de donnée',
      child: ElevatedButton.icon(
        onPressed: () => voidCallback(),
        icon: const Icon(Icons.notifications_none),
        label: const Text("Void Callback"),
        style: ElevatedButton.styleFrom(backgroundColor: Colors.teal),
      ),
    );
  }
}

Ce bouton notifie seulement le parent qu’il a été tapé. Chaque appel à _voidCallback dans le parent incrémente le compteur de 1. Dans les scénarios de jeu, VoidCallback est idéal pour des actions comme « sauter » ou « recommencer le niveau » qui n’ont pas besoin de paramètres.

Création d’un widget enfant avec Function Callback

Contrairement à VoidCallback, Function Callback permet de transmettre des données. Nous utilisons ici Function(int) pour que l’enfant puisse envoyer l’entier 5 à chaque pression sur le bouton. Le parent le reçoit comme argument i dans _functionCallback. Pour varier, vous pourriez aussi créer un enfant qui envoie des valeurs dynamiques depuis un curseur.

import 'package:flutter/material.dart';

class FunctionChildWidgetPage extends StatelessWidget {
  final Function(int) functionCallback;

  const FunctionChildWidgetPage({Key? key, required this.functionCallback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Tooltip(
      message: 'Ajoute 5 au compteur en envoyant le nombre 5 au parent',
      child: ElevatedButton.icon(
        onPressed: () => functionCallback(5),
        icon: const Icon(Icons.data_usage),
        label: const Text("Fx Callback (+5)"),
        style: ElevatedButton.styleFrom(backgroundColor: Colors.deepPurple),
      ),
    );
  }
}

Lorsque vous cliquez sur « Fx Callback (+5) », l’enfant appelle functionCallback(5). Le parent exécute alors _functionCallback(5), ajoutant 5 au compteur. La valeur envoyée peut être dynamique (par exemple depuis un curseur ou un champ texte), de sorte que le parent reçoit toujours des données fraîches. Pour le développement de jeux, Function Callback est idéal pour envoyer les scores, positions ou points de vie au widget parent.

VoidCallback et Function Callback Flutter sur émulateur Android
Figure 1 : Application Flutter avec deux boutons d’action. Bouton gauche (Void Callback) envoie seulement un signal, bouton droit (Fx Callback) envoie la valeur 5. Le compteur central affiche les résultats accumulés.

Exécutez le projet avec flutter run ou le bouton play dans Android Studio. Vous verrez deux boutons principaux. Cliquez sur « Void Callback » pour augmenter le compteur de 1, ou sur « Fx Callback (+5) » pour ajouter 5 d’un coup. Ce comportement prouve que Function Callback peut retourner des valeurs au parent, contrairement à VoidCallback. Utilisez le bouton de réinitialisation pour recommencer.

Études de cas : Jeux et formulaires

Dans un jeu mobile simple de devinette, les widgets enfants peuvent être des boutons numériques de 1 à 9. Chaque bouton utilise Function Callback avec son numéro comme paramètre. Le parent compare alors le nombre à la bonne réponse. Sans Function Callback, le parent ne saurait jamais quel numéro a été pressé.

Autre exemple : un formulaire d’inscription. Chaque TextField peut avoir un Function Callback qui envoie son texte au parent à chaque modification. Le parent valide et active le bouton d’envoi uniquement lorsque tous les champs sont remplis. Ce motif est très courant et constitue la base du lifting state up (remontée d’état).

Pour les développeurs habitués aux architectures orientées événements, les callbacks Flutter sont similaires aux écouteurs d’événements JavaScript. Cependant, ils sont sûrs au niveau des types car la vérification statique de Dart détecte les incohérences précocement.

Performances et alternatives modernes

Ces deux approches sont légères et faciles à comprendre, ce qui les rend parfaites pour les petits projets ou les composants d’interface qui n’ont pas besoin d’une gestion d’état complexe. Mais dans les applications à grande échelle, un excès de callbacks peut conduire à un callback hell (code imbriqué et difficile à lire). Des alternatives comme ValueNotifier, InheritedWidget ou le package Provider offrent plus de structure.

Pour le développement de jeux mobiles, VoidCallback est souvent utilisé pour des actions comme sauter ou tirer, tandis que Function Callback est idéal pour envoyer les scores, positions ou durées au widget parent.

Côté performances, les callbacks n’ont presque pas de surcharge car ce sont des appels directs à des fonctions. Mais si des dizaines de callbacks se déclenchent dans une seule image (par exemple lors du suivi de gestes), envisagez d’utiliser ValueNotifier avec AnimatedBuilder pour ne reconstruire que les parties qui changent.

Conclusion et recommandations pratiques

VoidCallback et Function Callback sont des outils fondamentaux mais puissants pour la communication entre widgets dans Flutter. Utilisez VoidCallback quand l’enfant a seulement besoin de signaler sans données (comme les boutons de navigation ou les déclencheurs d’animation). Utilisez Function Callback quand l’enfant doit renvoyer des valeurs (comme les données de formulaire, les événements de jeu ou les valeurs de curseur). Les deux sont faciles à implémenter, ne nécessitent aucune dépendance externe et supportent parfaitement le motif de remontée d’état (lifting state up).

Pour les projets plus vastes, envisagez de migrer vers Provider ou Bloc, mais les callbacks restent une base que tout développeur Flutter doit maîtriser. Comprendre ces différences vous permettra de construire des interfaces plus réactives, maintenables et sans bogues. Essayez de modifier l’exemple : créez un Function Callback qui envoie une valeur depuis un Slider ou TextField, puis observez comment le parent réagit en temps réel. Bonne programmation !

Articles Récents