O Android tem um recurso muito interessante da animação do View chamado CircularRevealAnimation
- literalmente "divulgação circular". O Flutter, por sua vez, embora tenha recursos avançados para animar widgets, não fornece essa animação imediatamente.

Este artigo mostra como implementar essa animação usando o Flutter e publica a biblioteca no pub.dev para facilitar o acesso e a distribuição.
Implementação de animação
No Flutter, tudo é um widget. E a animação não é exceção. Portanto, criamos a classe CircularRevealAnimation
, que estenderá a classe StatelessWidget
.
Iniciar, parar e outros controles de animação são feitos usando o AnimationController
. Para criar um AnimationController
você precisa herdar do StatefulWidget
e adicionar uma classe especial SingleTickerProviderStateMixin
ao State
.
Nossa classe de animação CircularRevealAnimation
não gerenciará de forma independente a animação, mas receberá a Animation<double>
como um parâmetro obrigatório do construtor, portanto, não há necessidade de herdar do StatefulWidget
. Isso é feito para que o CircularRevealAnimation
possa ser facilmente combinado com outras animações que usam o mesmo AnimationController
. Por exemplo, combine animação de divulgação com animação de alteração de transparência.
Outro parâmetro importante do construtor CircularRevealAnimation
é o child
, que é um widget filho da nossa animação e que aparecerá ou desaparecerá. Em geral, no Flutter, muitos widgets possuem um parâmetro child
. Esses widgets permitem alterar o comportamento, a renderização ou o layout de um widget filho. Ou adicione animação, como acontece com CircularRevealAnimation
.
Além disso, para especificar uma animação, você precisará de parâmetros como o centro da abertura (ou fechamento) da animação, bem como os raios mínimo e máximo de divulgação. Esses parâmetros são opcionais e podem ser especificados como null
ou não especificados ao criar uma animação. Nesse caso, os valores padrão serão usados: o centro de divulgação estará no centro do widget, o raio mínimo será zerado e o raio máximo será igual à distância do centro de divulgação ao vértice do widget que está mais distante do centro de divulgação.
O algoritmo de cálculo do raio máximo padrão é o seguinte. Primeiro, as distâncias horizontal e vertical do centro ao vértice mais distante do centro de divulgação são calculadas e, em seguida, a diagonal é calculada pelo 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); }
Agora você precisa implementar o corte do widget dentro do círculo durante a renderização. A classe ClipPath
nos ajudará com isso, o que permite cortar o widget de acordo com um modelo arbitrário. Como parâmetros, esse widget recebe o clipper
(um pouco mais tarde) e child
é o widget filho que precisa ser cortado.
O parâmetro clipper
do widget ClipPath
determina como o widget filho será cortado. Para criar seu próprio modelo de corte, crie a classe CircularRevealClipper
, que herda a CustomClipper<Path>
e redefina o Path getClip(Size size)
. Este método retorna um Path
delimitando a área de corte. No nosso caso, essa região é um círculo com um determinado centro. Para calcular o raio de um círculo, você precisa saber o valor atual da animação. Este valor é passado para CircularRevealClipper
como o parâmetro de fraction
. O raio do círculo é calculado usando interpolação linear entre os raios mínimo e máximo.
Depois disso, vamos seguir para a implementação do widget. É conveniente usar o AnimatedBuilder
para criar uma animação. O construtor AnimatedBuilder
aceita o objeto Animation<double>
e o builder
usado para criar widgets com base no valor atual da animação. No builder
criamos um ClipPath
e passamos o valor atual da animação ( fraction
) para o 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, ); }, ); } }
Isso completa a criação da CircularRevealAnimation
. Resta usá-lo. Para fazer isso, crie um StatefulWidget
, AnimationController
e passe o AnimationController
para CircularRevealAnimation
.
Exemplo 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(); } }), ); } }
Aplicativo de demonstração do Github .
Criação de biblioteca
Para criar uma biblioteca Dart ou Flutter, você precisa adicionar o arquivo pubspec.yaml
ao mesmo diretório que o diretório lib
com os arquivos Dart. Este arquivo contém uma descrição da biblioteca, informações sobre autores e dependências.
Também é uma boa prática criar um arquivo que define uma API pública. Este arquivo está localizado na pasta lib
e inclui o nome da biblioteca e uma lista de arquivos que precisam ser incluídos na API pública. Todos os outros arquivos Dart são colocados no diretório src
. Isso não apenas oculta arquivos que não estão incluídos na API pública, mas também permite importar a biblioteca usando uma única expressão de import
. O conteúdo deste arquivo :
library circular_reveal_animation; export 'package:circular_reveal_animation/src/circular_reveal_animation.dart';
Você pode ler mais sobre a criação de bibliotecas no Dart aqui .
A publicação de uma biblioteca Dart no pub.dev é muito simples. Tudo o que você precisa fazer é executar o comando flutter packages pub publish
partir do diretório raiz da biblioteca. A publicação é realizada em nome de uma conta do Google; portanto, durante o processo de publicação, será fornecido um link que deve ser aberto em um navegador e conectado ao Google. Posteriormente, as atualizações podem ser publicadas apenas usando a conta em cujo nome a primeira versão foi publicada.
Antes de publicar, é recomendável verificar se a biblioteca está correta usando o flutter packages pub publish --dry-run
do flutter packages pub publish --dry-run
.
Depois de executar os flutter packages pub publish
biblioteca ficará imediatamente disponível no pub.dev. E, como diz a documentação, "A publicação é para sempre" - mais tarde, você só pode enviar novas versões. Versões mais antigas também estarão disponíveis.
Embora a publicação de bibliotecas pareça simples, também pode ter armadilhas. Por exemplo, quando publiquei a primeira versão, obtive alguns pontos na classificação porque a descrição da biblioteca (em pubspec.yaml
) era muito curta.
Você pode ler mais sobre a publicação de bibliotecas aqui .
Na verdade, a biblioteca circular_reveal_animation
em pub.dev e github.com .
PS: usei ```java {...} ```
para destacar o código Dart. Seria bom adicionar o código do Dart destacado no habr.com.