Android tiene una característica muy interesante de la animación View llamada CircularRevealAnimation
, literalmente "divulgación circular". Flutter, a su vez, aunque tiene una gran capacidad para animar widgets, no proporciona dicha animación desde el primer momento.

Este artículo mostrará cómo implementar dicha animación usando Flutter y publicar la biblioteca en pub.dev para facilitar el acceso y la distribución.
Implementación de animación
En Flutter, todo es un widget. Y la animación no es la excepción. Por lo tanto, creamos la clase CircularRevealAnimation
, que extenderá la clase StatelessWidget
.
El inicio, la detención y otros controles de animación se realizan utilizando AnimationController
. Para crear un AnimationController
debe heredar de StatefulWidget
y agregar una clase especial SingleTickerProviderStateMixin
a State
.
Nuestra clase de animación CircularRevealAnimation
no será responsable de administrar la animación por sí sola, pero recibirá Animation<double>
como un parámetro de constructor requerido, por lo que no es necesario heredar de StatefulWidget
. Esto se hace para que CircularRevealAnimation
se pueda combinar fácilmente con otras animaciones que usan el mismo AnimationController
. Por ejemplo, combine la animación de divulgación con la animación de cambio de transparencia.
Otro parámetro importante del constructor CircularRevealAnimation
es el elemento child
, que es un widget secundario de nuestra animación y que aparecerá o desaparecerá. En general, en Flutter, muchos widgets tienen un parámetro child
. Dichos widgets le permiten cambiar el comportamiento, la representación o el diseño de un widget secundario. O agregue animación, como sucede con CircularRevealAnimation
.
Además, para especificar una animación, necesitará parámetros como el centro de la apertura (o cierre) de la animación, así como los radios de revelación mínimos y máximos. Estos parámetros son opcionales y pueden especificarse como null
o no especificados en absoluto al crear una animación. En este caso, se utilizarán los valores predeterminados: el centro de divulgación estará en el centro del widget, el radio mínimo estará herido a cero, y el radio máximo será igual a la distancia desde el centro de divulgación hasta el vértice del widget que está más alejado del centro de divulgación.
El algoritmo predeterminado de cálculo del radio máximo es el siguiente. Primero, se calculan las distancias horizontales y verticales desde el centro hasta el vértice más alejado del centro de revelación, y luego la diagonal se calcula mediante el teorema de Pitágoras.
static double calcMaxRadius(Size size, Offset center) { final w = max(center.dx, size.width - center.dx); final h = max(center.dy, size.height - center.dy); return sqrt(w * w + h * h); }
Ahora necesita implementar el recorte del widget dentro del círculo durante el renderizado. La clase ClipPath
nos ayudará con esto, lo que le permite recortar el widget de acuerdo con una plantilla arbitraria. Como parámetros, este widget se pasa a clipper
(aproximadamente un poco más tarde) y el elemento child
es el widget secundario que debe recortarse.
El parámetro clipper
del widget ClipPath
determina cómo se recortará el widget secundario. Para crear su propia plantilla de recorte, cree la clase CircularRevealClipper
, que hereda la clase CustomClipper<Path>
y redefina el Path getClip(Size size)
. Este método devuelve una Path
limita el área de recorte. En nuestro caso, esta región es un círculo con un centro dado. Para calcular el radio de un círculo, debe conocer el valor actual de la animación. Este valor se pasa a CircularRevealClipper
como el parámetro de fraction
. El radio del círculo se calcula mediante interpolación lineal entre los radios mínimo y máximo.
Después de eso, pasemos a la implementación del widget. Es conveniente usar AnimatedBuilder
para crear una animación. El constructor AnimatedBuilder
acepta el objeto Animation<double>
y el builder
utilizado para crear widgets basados en el valor actual de la animación. En el builder
creamos un ClipPath
y pasamos el valor actual de la animación ( fraction
) al CircularRevealClipper
.
class CircularRevealAnimation extends StatelessWidget { ... @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget _) { return ClipPath( clipper: CircularRevealClipper( fraction: animation.value, center: center, minRadius: minRadius, maxRadius: maxRadius, ), child: this.child, ); }, ); } }
Esto completa la creación de CircularRevealAnimation
. Queda por usarlo. Para hacer esto, cree un StatefulWidget
, AnimationController
y pase el AnimationController
a CircularRevealAnimation
.
Ejemplo de uso import 'package:flutter/material.dart'; import 'package:circular_reveal_animation/circular_reveal_animation.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'CRA Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation<double> animation; @override void initState() { super.initState(); animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000), ); animation = CurvedAnimation( parent: animationController, curve: Curves.easeIn, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("CRA Demo"), ), body: Padding( padding: const EdgeInsets.all(16.0), child: CircularRevealAnimation( minRadius: 12, maxRadius: 200, center: Offset(0, 300), child: Container(color: Colors.red), animation: animation, ), ), floatingActionButton: FloatingActionButton(onPressed: () { if (animationController.status == AnimationStatus.forward || animationController.status == AnimationStatus.completed) { animationController.reverse(); } else { animationController.forward(); } }), ); } }
Aplicación de demostración de Github .
Creación de la biblioteca
Para crear una biblioteca Dart o Flutter, debe agregar el archivo pubspec.yaml
al mismo directorio que el directorio lib
con los archivos Dart. Este archivo contiene una descripción de la biblioteca, información sobre autores y dependencias.
También es una buena práctica crear un archivo que defina una API pública. Este archivo se encuentra en la carpeta lib
e incluye el nombre de la biblioteca y una lista de archivos que deben incluirse en la API pública. Todos los demás archivos Dart se colocan en el directorio src
. Esto no solo oculta los archivos que no están incluidos en la API pública, sino que también le permite importar la biblioteca utilizando una sola expresión de import
. El contenido de este archivo :
library circular_reveal_animation; export 'package:circular_reveal_animation/src/circular_reveal_animation.dart';
Puedes leer más sobre cómo crear bibliotecas en Dart aquí .
Publicar una biblioteca Dart en pub.dev es muy simple. Todo lo que necesita hacer es ejecutar el comando de flutter packages pub publish
desde el directorio raíz de la biblioteca. La publicación se realiza en nombre de una cuenta de Google, por lo que durante el proceso de publicación se proporcionará un enlace que debe abrirse en un navegador e iniciar sesión en Google. Posteriormente, las actualizaciones solo se pueden publicar utilizando la cuenta en cuyo nombre se publicó la primera versión.
Antes de publicar, se recomienda verificar que la biblioteca sea correcta utilizando el flutter packages pub publish --dry-run
.
Después de ejecutar la flutter packages pub publish
biblioteca estará inmediatamente disponible en pub.dev. Y, como dice la documentación, "Publicar es para siempre", más tarde solo puede cargar nuevas versiones. Las versiones anteriores también estarán disponibles.
Aunque la publicación en la biblioteca parece simple, también puede tener dificultades. Por ejemplo, al publicar la primera versión, me pubspec.yaml
algunos puntos en la calificación porque la descripción de la biblioteca (en pubspec.yaml
) era demasiado corta.
Puede leer más sobre la publicación de bibliotecas aquí .
En realidad, la biblioteca circular_reveal_animation
en pub.dev y github.com .
PD: utilicé ```java {...} ```
para resaltar el código Dart. Sería bueno agregar resaltado de código Dart en habr.com.