O Widget Hero do Flutter tem um Superpoder Oculto (Que a maioria dos desenvolvedores nunca usa)

O widget Hero do Flutter é famoso. Todo mundo já viu: você toca em uma imagem e ela faz uma transição suave para uma nova tela. Magia instantânea. Mas aqui está o detalhe: a maioria dos desenvolvedores para no básico. Eles usam a animação padrão do Hero e dão o trabalho por encerrado.

Eu também era assim, até que encontrei um short (vídeo curto) no meu feed. Após assisti-lo, a ficha caiu: nós podemos, de fato, modificar as animações do Hero! 😲

Eu sempre presumi que fosse apenas uma “bruxaria” baseada em tags — mas não, há muito mais sob o capô.

Conheça o flightShuttleBuilder.

O widget Hero esconde um superpoder secreto: o flightShuttleBuilder.

Pense nele como o diretor de efeitos especiais da sua transição. Normalmente, o Flutter decide como um Hero “voa” entre as telas — um simples esmaecer (fade) e redimensionar (scale). Legal, mas um pouco… previsível.

Com o flightShuttleBuilder, você assume a cadeira do diretor. Você pode fazer seu Hero quicar, girar, transformar-se em outra coisa ou, sim — até voar pela tela como uma batata, se essa for a sua vibe.

Mas antes de mergulharmos em uma demonstração no DartPad, vamos analisar rapidamente os parâmetros que este callback te entrega.

Widget flightShuttleBuilder(
  BuildContext flightContext,
  Animation<double> animation,
  HeroFlightDirection flightDirection,
  BuildContext fromHeroContext,
  BuildContext toHeroContext,
)

Aqui está o que cada parâmetro te oferece:

  • flightContext → O contexto de construção (build context) do Hero em pleno voo (raramente usado, mas útil se você precisar de temas ou media queries).

  • animation → Um valor de 0 a 1 que indica o progresso do “voo” do Hero. Perfeito para conectar a widgets como FadeTransition, ScaleTransition, etc.

  • flightDirection → Informa se o Hero está indo para frente (push) ou voltando para trás (pop). Você pode fazer seu Hero se comportar de forma diferente ao retornar.

  • fromHeroContext → O widget Hero na página de origem (aquele em que o usuário tocou).

  • toHeroContext → O widget Hero na página de destino (aquele para onde você está voando).

Aqui está um exemplo que mostra duas animações customizadas de Hero: uma que quica e outra que gira.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hero Animation Shortcut',
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Hero Animations")),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
        
          GestureDetector(
            onTap: () => Navigator.push(context,
                MaterialPageRoute(builder: (_) => const BouncyPage())),
            child: Hero(
              tag: 'bouncy-hero',
              flightShuttleBuilder:
                  (ctx, animation, direction, from, to) {
                return ScaleTransition(
                  scale: animation.drive(
                    Tween(begin: 0.5, end: 1.2).chain(
                      CurveTween(curve: Curves.elasticOut),
                    ),
                  ),
                  child: to.widget,
                );
              },
              child: const CircleAvatar(
                radius: 40,
                backgroundColor: Colors.blue,
                child:
                    Icon(Icons.flutter_dash, color: Colors.white, size: 40),
              ),
            ),
          ),
          const SizedBox(height: 50),

     
          GestureDetector(
            onTap: () => Navigator.push(context,
                MaterialPageRoute(builder: (_) => const SpinPage())),
            child: Hero(
              tag: 'spin-hero',
              flightShuttleBuilder:
                  (ctx, animation, direction, from, to) {
                return RotationTransition(
                  turns: animation,
                  child: FadeTransition(
                    opacity: animation,
                    child: to.widget,
                  ),
                );
              },
              child: const CircleAvatar(
                radius: 40,
                backgroundColor: Colors.green,
                child: Icon(Icons.star, color: Colors.white, size: 40),
              ),
            ),
          ),
        ],
      ),
    );
  }
}


class BouncyPage extends StatelessWidget {
  const BouncyPage({super.key});
  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text("Bouncy Hero")),
        body: Center(
          child: Hero(
            tag: 'bouncy-hero',
            child: const CircleAvatar(
              radius: 120,
              backgroundColor: Colors.purple,
              child: Icon(Icons.flutter_dash,
                  color: Colors.white, size: 120),
            ),
          ),
        ),
      );
}

class SpinPage extends StatelessWidget {
  const SpinPage({super.key});
  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text("Spinning Hero")),
        body: Center(
          child: Hero(
            tag: 'spin-hero',
            child: const CircleAvatar(
              radius: 120,
              backgroundColor: Colors.red,
              child: Icon(Icons.star, color: Colors.white, size: 120),
            ),
          ),
        ),
      );
}

As possibilidades são infinitas — e assim que você começar a experimentar, nunca mais verá as animações de Hero da mesma forma.

Então, na próxima vez que alguém lhe disser que ‘as animações do Flutter são todas iguais’, apenas sorria… e mostre a eles o seu Hero de batata.

Please follow and like us:
error0
fb-share-icon
Tweet 20
fb-share-icon20