Introduzione
Le animazioni sono uno degli aspetti che distinguono un'app mediocre da una eccellente. Flutter offre un sistema di animazioni potente e flessibile, capace di garantire 60 (o anche 120) frame al secondo. In questo articolo vedremo le due grandi famiglie di animazioni: implicite ed esplicite.
Animazioni implicite
Le animazioni implicite sono il modo più semplice per animare un widget. Basta cambiare un valore e Flutter si occupa di interpolare automaticamente la transizione. Tutti questi widget iniziano con il prefisso Animated.
class BoxDemo extends StatefulWidget {
const BoxDemo({super.key});
@override
State<BoxDemo> createState() => _BoxDemoState();
}
class _BoxDemoState extends State<BoxDemo> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _expanded = !_expanded),
child: AnimatedContainer(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
color: _expanded ? Colors.deepPurple : Colors.teal,
),
);
}
}
Ogni volta che cambia lo stato, AnimatedContainer anima la transizione tra i vecchi e i nuovi valori. Tra gli altri widget impliciti più utili troviamo:
AnimatedOpacityper dissolvenzeAnimatedPaddingeAnimatedAlignper spostamentiAnimatedDefaultTextStyleper il testoTweenAnimationBuilderper animare qualsiasi valore
TweenAnimationBuilder
Quando serve animare una proprietà non coperta da un widget specifico, TweenAnimationBuilder è la scelta ideale:
TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1),
duration: const Duration(seconds: 1),
builder: (context, value, child) {
return Transform.rotate(
angle: value * 2 * 3.1415,
child: child,
);
},
child: const Icon(Icons.refresh, size: 64),
)
Animazioni esplicite
Quando hai bisogno di un controllo totale (ripetizione, inversione, sincronizzazione tra più animazioni) entrano in gioco le animazioni esplicite, basate su AnimationController.
L'AnimationController richiede un TickerProvider, che si ottiene tipicamente con il mixin SingleTickerProviderStateMixin.
class PulseDemo extends StatefulWidget {
const PulseDemo({super.key});
@override
State<PulseDemo> createState() => _PulseDemoState();
}
class _PulseDemoState extends State<PulseDemo>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _scale;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
)..repeat(reverse: true);
_scale = Tween<double>(begin: 0.8, end: 1.2).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: _scale,
child: const FlutterLogo(size: 100),
);
}
}
Punti chiave
- Ricorda sempre
dispose()del controller per evitare memory leak. - I
Tweendefiniscono l'intervallo di valori, leCurvela velocità nel tempo. - I widget
*Transition(FadeTransition,SlideTransition,RotationTransition) consumano direttamente un'Animation.
AnimatedBuilder
Per logiche personalizzate, AnimatedBuilder evita di ricostruire l'intero albero dei widget:
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Opacity(
opacity: _controller.value,
child: child,
);
},
child: const Text('Ciao Flutter'),
)
Il parametro child viene costruito una sola volta e passato al builder, ottimizzando le prestazioni.
Quando usare cosa
- Animazioni implicite: transizioni semplici di stato, codice minimale.
- Animazioni esplicite: cicli, sincronizzazioni, controllo manuale e interazioni complesse.
Conclusione
Flutter rende le animazioni accessibili senza sacrificare la potenza. Inizia con i widget impliciti per i casi comuni e passa agli AnimationController quando hai bisogno di un controllo fine. Per esigenze ancora più avanzate, dai un'occhiata a librerie come Rive, Lottie o al pacchetto flutter_animate, che permette di concatenare animazioni con poche righe di codice.
