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:

  • AnimatedContainer
  • AnimatedOpacity
  • AnimatedAlign
  • AnimatedPositioned

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

  1. AnimatedContainer: Aquest widget canvia les seves propietats (com ara la mida, el color i l'alineació) de manera animada.
  2. GestureDetector: Detecta els tocs de l'usuari per canviar l'estat de _isExpanded.
  3. setState: Actualitza l'estat de _isExpanded i 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

  1. AnimationController: Controla la durada i l'estat de l'animació.
  2. Tween: Defineix els valors inicials i finals de l'animació.
  3. 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

  1. Hero: El widget Hero crea una animació de transició entre dues pantalles.
  2. 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

  1. CustomPainter: Permet dibuixar formes personalitzades en un Canvas.
  2. AnimationController: Controla l'animació del CustomPainter.

Exercicis Pràctics

  1. Animació de Color: Crea una animació que canviï el color d'un Container de vermell a verd.
  2. Animació de Posició: Crea una animació que mogui un Container d'una posició a una altra.
  3. Animació de Forma: Crea una animació que canviï la forma d'un Container de quadrat a cercle.

Solucions

  1. 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,
    );
  }
}
  1. 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,
      ),
    );
  }
}
  1. 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

Mòdul 2: Conceptes Bàsics de Programació en Dart

Mòdul 3: Widgets de Flutter

Mòdul 4: Gestió de l'Estat

Mòdul 5: Navegació i Enrutament

Mòdul 6: Xarxes i APIs

Mòdul 7: Persistència i Emmagatzematge

Mòdul 8: Conceptes Avançats de Flutter

Mòdul 9: Proves i Depuració

Mòdul 10: Desplegament i Manteniment

Mòdul 11: Flutter per a Web i Escriptori

© Copyright 2024. Tots els drets reservats