Cos'è flutter_hooks
Chiunque abbia scritto applicazioni Flutter conosce bene la verbosità degli StatefulWidget: bisogna creare una classe separata per lo State, gestire manualmente initState, dispose, AnimationController e ricordarsi di liberare le risorse. La libreria flutter_hooks, ispirata ai React Hooks, propone un approccio più conciso e componibile per gestire lo stato locale e il ciclo di vita all'interno di un HookWidget.
In questo articolo vedremo come integrarla, gli hook più utili e quando conviene davvero adottarla.
Installazione
Aggiungi la dipendenza al tuo pubspec.yaml:
dependencies:
flutter_hooks: ^0.20.5
Poi esegui flutter pub get.
Dal StatefulWidget all'HookWidget
Vediamo un classico contatore scritto con uno StatefulWidget:
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() => _count++),
child: Text('Valore: $_count'),
);
}
}
Lo stesso risultato con flutter_hooks:
class Counter extends HookWidget {
const Counter({super.key});
@override
Widget build(BuildContext context) {
final count = useState(0);
return ElevatedButton(
onPressed: () => count.value++,
child: Text('Valore: ${count.value}'),
);
}
}
Niente classe State, niente setState: useState restituisce un ValueNotifier il cui valore, quando modificato, ricostruisce automaticamente il widget.
Gli hook più utili
useState
Come visto, mantiene un valore mutabile tra una build e l'altra.
useEffect
Esegue effetti collaterali (chiamate di rete, sottoscrizioni) e gestisce la pulizia, sostituendo initState e dispose:
useEffect(() {
final subscription = stream.listen(print);
// la funzione restituita viene chiamata al dispose
return subscription.cancel;
}, const []); // [] = eseguito una sola volta
Il secondo parametro è la lista delle dipendenze: l'effetto viene rieseguito solo quando uno dei valori cambia.
useMemoized e useFuture
Utili per memorizzare risultati costosi o gestire future:
final future = useMemoized(() => api.fetchUser(id), [id]);
final snapshot = useFuture(future);
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return Text(snapshot.data?.name ?? 'Nessun dato');
useTextEditingController e useAnimationController
Gli hook eliminano la necessità di dichiarare e fare il dispose manuale dei controller:
class LoginForm extends HookWidget {
const LoginForm({super.key});
@override
Widget build(BuildContext context) {
final emailController = useTextEditingController();
final animation = useAnimationController(
duration: const Duration(milliseconds: 300),
);
return TextField(controller: emailController);
}
}
Il dispose viene gestito automaticamente dall'hook.
Creare un hook personalizzato
Uno dei vantaggi maggiori è la possibilità di estrarre logica riutilizzabile in hook custom:
ValueNotifier<int> useCounter({int initial = 0}) {
final count = useState(initial);
// logica aggiuntiva riutilizzabile
return count;
}
Un hook custom è semplicemente una funzione che usa altri hook, favorendo la composizione.
Regole da rispettare
Gli hook seguono regole precise, analoghe a quelle di React:
- Vanno chiamati sempre nello stesso ordine.
- Non vanno mai chiamati dentro condizioni, cicli o funzioni annidate.
- Vanno usati solo all'interno del metodo
builddi unHookWidget.
Violare queste regole porta a comportamenti imprevedibili, perché gli hook si basano sull'ordine di chiamata per associare lo stato.
Quando usarli
flutter_hooks brilla quando hai molti controller o logiche di ciclo di vita da gestire in un singolo widget, riducendo drasticamente il boilerplate. Si integra inoltre molto bene con Riverpod, tramite il pacchetto hooks_riverpod e il widget HookConsumerWidget.
Non è però una soluzione di gestione dello stato globale: per quello restano necessari pattern come Riverpod, Bloc o Provider. Gli hook gestiscono lo stato locale del widget.
Conclusione
flutter_hooks offre un modo elegante e componibile per ridurre la verbosità degli StatefulWidget, eliminando il rischio di dimenticare il dispose delle risorse. Pur richiedendo qualche regola da rispettare, l'aumento di leggibilità e riuso del codice ne fa uno strumento prezioso per progetti di medie e grandi dimensioni.
