Como Usar o Widget Hero no Flutter para Animações de Transição

Introdução

O widget Hero no Flutter é uma ferramenta poderosa para criar transições animadas suaves entre telas. Ele é especialmente útil quando você deseja que um elemento mantenha sua aparência ao navegar entre páginas, proporcionando uma experiência visualmente fluida.

Neste tutorial, vamos explorar o uso do Hero para criar animações de transição entre telas e aprimorar a experiência do usuário.

1. O Que é o Widget Hero?

O Hero é um widget que permite a animação entre duas páginas ao usar a mesma tag. Quando um widget envolvido em um Hero é navegado para outra tela, ele realiza uma transição suave para a nova posição.

Isso é particularmente útil para criar efeitos visuais impressionantes ao navegar em listas, imagens e outros elementos de UI.

2. Implementando um Hero Básico

Vamos começar com um exemplo simples de transição entre duas telas usando o Hero.

Passo 1: Criando a Tela Inicial


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Hero Example')),
      body: Center(
        child: GestureDetector(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const DetailScreen(),
              ),
            );
          },
          child: Hero(
            tag: 'hero-tag',
            child: ClipRRect(
              borderRadius: BorderRadius.circular(15),
              child: Image.network(
                'https://source.unsplash.com/random/200x200',
                width: 100,
                height: 100,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Passo 2: Criando a Tela de Detalhes


class DetailScreen extends StatelessWidget {
  const DetailScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Detalhes')),
      body: Center(
        child: Hero(
          tag: 'hero-tag',
          child: ClipRRect(
            borderRadius: BorderRadius.circular(15),
            child: Image.network(
              'https://source.unsplash.com/random/200x200',
              width: 300,
              height: 300,
            ),
          ),
        ),
      ),
    );
  }
}

Nesse exemplo, a imagem da tela inicial se expande suavemente para a tela de detalhes.

3. Melhorando a Animação

O Flutter permite personalizar a animação do Hero para que fique mais fluida e sofisticada. Podemos modificar a animação adicionando um FlightShuttleBuilder para controlar a aparência durante a transição.


Hero(
  tag: 'hero-tag',
  flightShuttleBuilder: (flightContext, animation, direction, fromContext, toContext) {
    return ScaleTransition(
      scale: animation.drive(Tween(begin: 0.5, end: 1.0)),
      child: toContext.widget,
    );
  },
  child: ClipRRect(
    borderRadius: BorderRadius.circular(15),
    child: Image.network(
      'https://source.unsplash.com/random/200x200',
      width: 100,
      height: 100,
    ),
  ),
)

Com isso, a imagem escala suavemente durante a animação.

4. Hero com ListView

O Hero também pode ser usado dentro de listas para criar transições suaves entre itens de uma lista e uma página de detalhes.


class HomeScreen extends StatelessWidget {
  final List images = List.generate(
    10,
    (index) => 'https://source.unsplash.com/random/200x200?sig=$index',
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Lista de Imagens')),
      body: ListView.builder(
        itemCount: images.length,
        itemBuilder: (context, index) {
          return ListTile(
            leading: Hero(
              tag: 'hero-tag-$index',
              child: ClipRRect(
                borderRadius: BorderRadius.circular(10),
                child: Image.network(
                  images[index],
                  width: 50,
                  height: 50,
                ),
              ),
            ),
            title: Text('Imagem $index'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen(imageUrl: images[index], tag: 'hero-tag-$index'),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  final String imageUrl;
  final String tag;

  const DetailScreen({Key? key, required this.imageUrl, required this.tag}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Detalhes')),
      body: Center(
        child: Hero(
          tag: tag,
          child: ClipRRect(
            borderRadius: BorderRadius.circular(15),
            child: Image.network(imageUrl, width: 300, height: 300),
          ),
        ),
      ),
    );
  }
}

5. Boas Práticas

  • Garanta que a tag do Hero seja única para cada transição.
  • Use o FlightShuttleBuilder para personalizar a animação.
  • Evite mudanças drásticas no tamanho do widget para manter a animação fluida.

Publicar comentário