In this guide, we are going to learn the concept of a hero animation in Flutter, how to create it, and learn different properties of it, and we will look at examples of hero animation. First of all, we should know what is hero animation in a flutter.

The Hero refers to the widget that flies between screens. It generally flies the hero from one screen to another. The Hero widget in flutter implements a style of animation commonly known as shared element transition or shared element animation. if we have multiply heroes on the same page each hero knows where to fly to. Hero Animations take an element like an icon which is now called a “Hero” and once the page transition is triggered, usually by clicking on the icon, the hero “flies” to the next page. When the user navigates back to the earlier page, the animation goes in the other direction and the icon goes back to its place. It required two-argument

  1. tag: An object that identifies the ‘Hero’, which must be same on both screen
  2. child : The widget to animate across screen

Red more: Tween Animation in Flutter

Creating a Hero Animation

Hero animations are one of the easiest animations to do in Flutter and don’t require much setup. Let’s take a basic example of how to use a Hero animation in our application.

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 const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: TextScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Hero Animation'),
        centerTitle: true,
        backgroundColor: Colors.teal,
      ),
      body: Container(
        alignment: Alignment.bottomCenter,
        child: GestureDetector(
            onTap: () {
              Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => const SecondPage()));
            },
            child: const Hero(
              tag: "hero1",
              child: CircleAvatar(
                  radius: 70,
                  backgroundImage: NetworkImage(
                      'https://scontent.fktm8-1.fna.fbcdn.net/v/t39.30808-6/262951063_4473265099459320_7674897967916414703_n.jpg?_nc_cat=103&ccb=1-5&_nc_sid=09cbfe&_nc_ohc=reWiQX3MrJYAX8D9BI_&tn=8cvU-xCDYc34zfNx&_nc_ht=scontent.fktm8-1.fna&oh=00_AT-jym4u-RDEKdmSU_5s1i2tYW9IOW74V7IGTX5KxM4kxg&oe=61D7E1BC')),
            )),
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("Second Screen"),
          centerTitle: true,
        ),
        body: Hero(
          tag: "hero1",
          child: Container(
            alignment: Alignment.topCenter,
            width: double.infinity,
            height: 300,
            decoration: const BoxDecoration(
                image: DecorationImage(
              image: NetworkImage(
                  'https://scontent.fktm8-1.fna.fbcdn.net/v/t39.30808-6/262951063_4473265099459320_7674897967916414703_n.jpg?_nc_cat=103&ccb=1-5&_nc_sid=09cbfe&_nc_ohc=reWiQX3MrJYAX8D9BI_&tn=8cvU-xCDYc34zfNx&_nc_ht=scontent.fktm8-1.fna&oh=00_AT-jym4u-RDEKdmSU_5s1i2tYW9IOW74V7IGTX5KxM4kxg&oe=61D7E1BC'),
            )),
          ),
        ));
  }
}

Here in the above code, We generally supply a tag to give the specific hero a name. This is necessary because if we have multiple heroes on the same page, each hero knows where to fly to. These will know that there is a hero widget that wants to fly to the next page. As we know that the hero widget on the second page will have also the same tag

Meanwhile, when we run this application in the emulator or device, we should get the UI similar to the screenshot below.

We can also Customize the Hero animation by adding some properties of the hero animation.

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 const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: TextScreen(),
    );
  }
}

class TextScreen extends StatefulWidget {
  const TextScreen({
    Key? key,
  }) : super(key: key);

  @override
  State<TextScreen> createState() => _TextScreenState();
}

class _TextScreenState extends State<TextScreen> {
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Hero Animation'),
        centerTitle: true,
        backgroundColor: Colors.teal,
      ),
      body: Column(
        children: [
         
 Container(
            alignment: Alignment.bottomCenter,
            child: GestureDetector(
                onTap: () {
                  Navigator.of(context).push(MaterialPageRoute(
                      builder: (context) => const SecondPage()));
                },
                child: Hero(
                  tag: "hero1",
                  child: const CircleAvatar(
                      radius: 70,
                      backgroundImage: NetworkImage(
                          'https://scontent.fktm8-1.fna.fbcdn.net/v/t39.30808-6/262951063_4473265099459320_7674897967916414703_n.jpg?_nc_cat=103&ccb=1-5&_nc_sid=09cbfe&_nc_ohc=reWiQX3MrJYAX8D9BI_&tn=8cvU-xCDYc34zfNx&_nc_ht=scontent.fktm8-1.fna&oh=00_AT-jym4u-RDEKdmSU_5s1i2tYW9IOW74V7IGTX5KxM4kxg&oe=61D7E1BC')),
                  placeholderBuilder: (context, size, widget) {
                    return const SizedBox(
                      height: 150.0,
                      width: 150.0,
                      child: CircularProgressIndicator(),
                    );
                  },
                  flightShuttleBuilder: (flightContext, animation, direction,
                      fromContext, toContext) {
                    return const Icon(
                      Icons.add,
                      size: 150.0,
                    );
                  },
                  transitionOnUserGestures: true,
                )),
          ),
        ],
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("Second Screen"),
          centerTitle: true,
        ),
        body: Hero(
          tag: "hero1",
          child: Container(
            alignment: Alignment.topCenter,
            height: 300,
            decoration: const BoxDecoration(
                shape: BoxShape.circle,
                image: DecorationImage(
                  image: NetworkImage(
                      'https://scontent.fktm8-1.fna.fbcdn.net/v/t39.30808-6/262951063_4473265099459320_7674897967916414703_n.jpg?_nc_cat=103&ccb=1-5&_nc_sid=09cbfe&_nc_ohc=reWiQX3MrJYAX8D9BI_&tn=8cvU-xCDYc34zfNx&_nc_ht=scontent.fktm8-1.fna&oh=00_AT-jym4u-RDEKdmSU_5s1i2tYW9IOW74V7IGTX5KxM4kxg&oe=61D7E1BC'),
                )),
          ),
          placeholderBuilder: (context, size, widget) {
            return const SizedBox(
              height: 150.0,
              width: 150.0,
              child: CircularProgressIndicator(),
            );
          },
          transitionOnUserGestures: true,
          flightShuttleBuilder:
              (flightContext, animation, direction, fromContext, toContext) {
            return const Icon(
              Icons.add,
              size: 150.0,
            );
          },
        ));
  }
}

The Hero widget flies off the place and is used in and before the widget arrives at the destination. The placeholderBuilder property defines a widget to place on the destination page while the Hero widget flies into position. By default, this is an empty SizedBox. Here We use the placeholderBuilder to construct the placeholder and return the widget. We use a circular progress indicator for the placeholderBuilder. Here we used the flightShuttleBuilder method which takes 5 parameters and gives the animation as well as the direction of the animation. The flightShuttleBuilder property defines the widget that flies across the two pages during the animation. By default, this is the child of the Hero widget on the destination page.when we run this application in the emulator or device, we should get the UI similar to the below screenshot:

Thank you for reading the guide on Hero Animation in Flutter.

Follow up Read: Animations in Flutter using Lottie.