Les animacions són una part essencial de les aplicacions modernes, ja que milloren l'experiència de l'usuari fent que les interfícies siguin més atractives i intuïtives. En aquest tema, aprendrem com crear animacions en Flutter utilitzant diverses tècniques i widgets.
Continguts
Introducció a les Animacions
Les animacions en Flutter es poden dividir en dues categories principals:
- Animacions Implicites: Són fàcils d'implementar i no requereixen gaire codi.
- Animacions Explícites: Ofereixen més control i flexibilitat, però requereixen més codi i configuració.
Animacions Implicites
Les animacions implicites són fàcils d'utilitzar i es basen en widgets que canvien automàticament les seves propietats amb el temps. Alguns dels widgets més comuns són:
AnimatedContainerAnimatedOpacityAnimatedAlignAnimatedPositioned
Exemple: AnimatedContainer
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Animacions Implicites')),
body: Center(child: AnimatedContainerExample()),
),
);
}
}
class AnimatedContainerExample extends StatefulWidget {
@override
_AnimatedContainerExampleState createState() => _AnimatedContainerExampleState();
}
class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _isExpanded ? 200 : 100,
height: _isExpanded ? 200 : 100,
color: _isExpanded ? Colors.blue : Colors.red,
alignment: _isExpanded ? Alignment.center : AlignmentDirectional.topCenter,
child: FlutterLogo(size: 75),
),
);
}
}Explicació del Codi
AnimatedContainer: Aquest widget canvia les seves propietats (com ara la mida, el color i l'alineació) de manera animada.GestureDetector: Detecta els tocs de l'usuari per canviar l'estat de_isExpanded.setState: Actualitza l'estat de_isExpandedi redibuixa el widget amb les noves propietats.
Animacions Explícites
Les animacions explícites ofereixen més control sobre el procés d'animació. Utilitzen classes com AnimationController i Tween.
Exemple: AnimationController i Tween
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Animacions Explícites')),
body: Center(child: ExplicitAnimationExample()),
),
);
}
}
class ExplicitAnimationExample extends StatefulWidget {
@override
_ExplicitAnimationExampleState createState() => _ExplicitAnimationExampleState();
}
class _ExplicitAnimationExampleState extends State<ExplicitAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0, end: 300).animate(_controller)
..addListener(() {
setState(() {});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.blue,
);
}
}Explicació del Codi
AnimationController: Controla la durada i l'estat de l'animació.Tween: Defineix els valors inicials i finals de l'animació.addListener: Escolta els canvis en l'animació i actualitza l'estat del widget.
Animacions amb Hero
El widget Hero permet crear animacions de transició entre pantalles.
Exemple: Hero
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Primera Pantalla')),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) => SecondScreen()));
},
child: Hero(
tag: 'hero-logo',
child: FlutterLogo(size: 100),
),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Segona Pantalla')),
body: Center(
child: Hero(
tag: 'hero-logo',
child: FlutterLogo(size: 200),
),
),
);
}
}Explicació del Codi
Hero: El widgetHerocrea una animació de transició entre dues pantalles.tag: Identifica els widgets que participen en l'animació de transició.
Animacions Personalitzades
Les animacions personalitzades permeten crear efectes únics utilitzant CustomPainter i altres tècniques avançades.
Exemple: CustomPainter
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Animacions Personalitzades')),
body: Center(child: CustomAnimationExample()),
),
);
}
}
class CustomAnimationExample extends StatefulWidget {
@override
_CustomAnimationExampleState createState() => _CustomAnimationExampleState();
}
class _CustomAnimationExampleState extends State<CustomAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CirclePainter(_controller),
child: Container(),
);
}
}
class CirclePainter extends CustomPainter {
final Animation<double> animation;
CirclePainter(this.animation) : super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
final radius = 50.0 + 50.0 * animation.value;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), radius, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}Explicació del Codi
CustomPainter: Permet dibuixar formes personalitzades en unCanvas.AnimationController: Controla l'animació delCustomPainter.
Exercicis Pràctics
- Animació de Color: Crea una animació que canviï el color d'un
Containerde vermell a verd. - Animació de Posició: Crea una animació que mogui un
Containerd'una posició a una altra. - Animació de Forma: Crea una animació que canviï la forma d'un
Containerde quadrat a cercle.
Solucions
- Animació de Color
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Animació de Color')),
body: Center(child: ColorAnimationExample()),
),
);
}
}
class ColorAnimationExample extends StatefulWidget {
@override
_ColorAnimationExampleState createState() => _ColorAnimationExampleState();
}
class _ColorAnimationExampleState extends State<ColorAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Color> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = ColorTween(begin: Colors.red, end: Colors.green).animate(_controller)
..addListener(() {
setState(() {});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: _animation.value,
);
}
}- Animació de Posició
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Animació de Posició')),
body: Center(child: PositionAnimationExample()),
),
);
}
}
class PositionAnimationExample extends StatefulWidget {
@override
_PositionAnimationExampleState createState() => _PositionAnimationExampleState();
}
class _PositionAnimationExampleState extends State<PositionAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<Offset>(begin: Offset(0, 0), end: Offset(100, 100)).animate(_controller)
..addListener(() {
setState(() {});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: _animation.value,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
}
}- Animació de Forma
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Animació de Forma')),
body: Center(child: ShapeAnimationExample()),
),
);
}
}
class ShapeAnimationExample extends StatefulWidget {
@override
_ShapeAnimationExampleState createState() => _ShapeAnimationExampleState();
}
class _ShapeAnimationExampleState extends State<ShapeAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0, end: 1).animate(_controller)
..addListener(() {
setState(() {});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(_animation.value * 50),
),
);
}
}Conclusió
En aquest tema, hem après com crear animacions en Flutter utilitzant tant tècniques implicites com explícites. També hem vist com utilitzar el widget Hero per a animacions de transició entre pantalles i com crear animacions personalitzades amb CustomPainter. Les animacions són una eina poderosa per millorar l'experiència de l'usuari i fer que les aplicacions siguin més atractives i intuïtives.
En el següent tema, explorarem la pintura personalitzada i l'ús de Canvas per crear gràfics i efectes visuals únics en les nostres aplicacions Flutter.
Curs de Desenvolupament Flutter
Mòdul 1: Introducció a Flutter
- Què és Flutter?
- Configuració de l'Entorn de Desenvolupament
- Comprensió de l'Arquitectura de Flutter
- Creació de la Teva Primera Aplicació Flutter
Mòdul 2: Conceptes Bàsics de Programació en Dart
- Introducció a Dart
- Variables i Tipus de Dades
- Sentències de Flux de Control
- Funcions i Mètodes
- Programació Orientada a Objectes en Dart
Mòdul 3: Widgets de Flutter
- Introducció als Widgets
- Widgets Stateless vs Stateful
- Widgets Bàsics
- Widgets de Disseny
- Widgets d'Entrada i Formulari
Mòdul 4: Gestió de l'Estat
Mòdul 5: Navegació i Enrutament
- Introducció a la Navegació
- Navegació Bàsica
- Rutes Nomenades
- Passar Dades Entre Pantalles
- Deep Linking
Mòdul 6: Xarxes i APIs
- Obtenir Dades d'Internet
- Analitzar Dades JSON
- Gestió d'Errors de Xarxa
- Ús d'APIs REST
- Integració de GraphQL
Mòdul 7: Persistència i Emmagatzematge
- Introducció a la Persistència
- Preferències Compartides
- Emmagatzematge de Fitxers
- Base de Dades SQLite
- Ús de Hive per a l'Emmagatzematge Local
Mòdul 8: Conceptes Avançats de Flutter
- Animacions en Flutter
- Pintura Personalitzada i Canvas
- Canals de Plataforma
- Isolates i Concurrència
- Optimització del Rendiment
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries
- Proves de Widgets
- Proves d'Integració
- Tècniques de Depuració
Mòdul 10: Desplegament i Manteniment
- Preparació per al Llançament
- Construcció per a iOS
- Construcció per a Android
- Integració i Desplegament Continu (CI/CD)
- Manteniment i Actualització de la Teva Aplicació
