Hero Widget in Flutter

Hero Widget in Flutter

The Hero widget in Flutter is used to create a shared transition between two pages (or screens) in an app. It allows an element on one page to transition smoothly to a corresponding element on another page, creating a seamless visual effect. The Hero widget is typically used to animate an image or other visual element as it is passed from one page to another. The widget can be used in combination with other animations, such as page transitions, to create a more dynamic user experience.

Properties

Let us discuss some properties of the Hero widget:

  • child: The child widget will be animated during the transition.

  • tag: A unique identifier used to match the Hero widget on one page to its corresponding element on the other page.

  • flightShuttleBuilder: A callback that is used to customize the animation of the Hero widget.

  • placeholderBuilder: A callback that is used to create a placeholder widget that is displayed during the transition.

  • createRectTween: A callback that is used to create a tween that animates the size of the Hero widget during the transition.

  • onPlaceholderTap: A callback that is called when the placeholder widget is tapped.

  • transitionDuration: The duration of the transition animation.

  • transitionBuilder: A callback that is used to customize the transition animation.

  • crossFadeStateChanged: A callback that is called when the state of the cross-fade animation changes.

  • heroAttributes: A callback that is used to customize attributes of the hero widget during the transition, such as color, size, etc.

Example

Suppose we have a ListView widget in Flutter with a ListTile:

ListView(
  children: [
    ListTile()
  ],
),

Inside the ListTile, for the title give an appropriate text and for Trailing, add the Hero widget. In the Hero widget, add the tag(we are going to need it later) and the child as an icon for a person as follows:

ListTile(
  title: Text("Person 1"),
  trailing: Hero(
    tag: 'person1',
    child: CircleAvatar(
      backgroundImage:
          NetworkImage('https://picsum.photos/250?image=9'),
    ),
  ),
),

For the ListTile, add the onTap() function where we are going to navigate to a new screen.

ListTile(
  title: const Text("Person 1"),
  trailing: const Hero(
    tag: 'person1',
    child: CircleAvatar(
      backgroundImage:
          NetworkImage('https://picsum.photos/250?image=9'),
    ),
  ),
  onTap: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => const DetailScreen(tag: 'person1'),
      ),
    );
  },
),

Here is the Navigation tutorial.

The DetailScreen would again contain a Hero widget, with a child widget ( which may obviously be different). Here is an example DetailScreen that we are going to use for this example:

class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key, required this.tag});
  final String tag;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Detail Screen'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: const [
            Hero(
              tag: 'person1',
              child: CircleAvatar(
                radius: 50,
                backgroundImage:
                    NetworkImage('https://picsum.photos/250?image=9'),
              ),
            ),
            Text('Person 1'),
          ],
        ),
      ),
    );
  }
}

Output

Here is the Full Code

main.dart

import 'package:flutter/material.dart';

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

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


class HeroTutorial extends StatefulWidget {
  const HeroTutorial({super.key});

  @override
  State<HeroTutorial> createState() => _HeroTutorialState();
}

class _HeroTutorialState extends State<HeroTutorial> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Hero Tutorial'),
      ),
      body: ListView(
        children: [
          ListTile(
            title: const Text("Person 1"),
            trailing: const Hero(
              tag: 'person1',
              child: CircleAvatar(
                backgroundImage:
                    NetworkImage('https://picsum.photos/250?image=9'),
              ),
            ),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const DetailScreen(tag: 'person1'),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key, required this.tag});
  final String tag;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Detail Screen'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: const [
            Hero(
              tag: 'person1',
              child: CircleAvatar(
                radius: 50,
                backgroundImage:
                    NetworkImage('https://picsum.photos/250?image=9'),
              ),
            ),
            Text('Person 1'),
          ],
        ),
      ),
    );
  }
}

Here is the code attached.

https://dartpad.dev/?id=bca09cb0b9f68d345064444fdcc01afc

Did you find this article valuable?

Support All About Flutter | Flutter and Dart by becoming a sponsor. Any amount is appreciated!