1. Home
  2. Categories
  3. Flutter

Tutorial: Super Simple Hero Animation in Flutter

Creating animations when you natively develop an app for Android or iOS is rarely done. Most of the time it is too complicated, and the budget restricts adding anything fancy to an app.

Therefore, I was positively surprised when I discovered how simple it is to add basic animation to my apps when developing with Flutter.

In this tutorial, I want to show you how to create a very simple app where you can select an item from a list to open in detail view. To make the transitions between list view and detail view more appealing, we will add a so-called hero animation.

A hero animation basically consists of a piece of content that is brought over from one screen to another. This supports the user’s understanding of the context of the app.

Let’s have a look how our app should look like:

Super Simple Hero Animation in Flutter
After finishing this tutorial our app includes a hero animation like this.


We start by creating a new Flutter project. This time I name it super_simple_hero_animation. Like in my other tutorials , we start by cleaning up our project.

We go with the most basic configuration of our pubspec.yaml and main.dart file.


name: super_simple_hero_animation description: Super Simple Hero Animation publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: sdk: ">=2.7.0 <3.0.0" dependencies: flutter: sdk: flutter flutter: uses-material-design: true
Code language: YAML (yaml)


import 'package:flutter/material.dart'; import 'package:super_simple_hero_animation/home_page.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } }
Code language: Dart (dart)

Hero Animation Part 1: The Master Page

In a new file home_page.dart we create our master page – the page containing a list of elements. Every element consists of an image and a name. Perfect application for the ListTile widget.


import 'package:flutter/material.dart'; import 'package:super_simple_hero_animation/detail_page.dart'; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Super Simple Hero Animation")), body: ListView.builder(itemBuilder: (context, index) { return ListTile( leading: CircleAvatar( child: ClipOval( child: Hero( tag: "avatar-$index", child: Image.network("https://picsum.photos/seed/$index/800"), ), ), ), title: Text("Item #$index"), onTap: () => _openDetail(context, index), ); }), ); } _openDetail(context, index) { final route = MaterialPageRoute( builder: (context) => DetailPage(index: index), ); Navigator.push(context, route); } }
Code language: Dart (dart)
  • Lines 7 – 8: We wrap everything in a Scaffold widget and add an AppBar with a title.
  • Line 9: To create a ListView, we use the builder factory method and simply create an infinite list.
  • Lines 10 – 12: As the leading element of our ListTile, we use the CircleAvatar widget. It takes care of the size and the padding of our image. The ClipOval widgets makes sure our image is circular.
  • Lines 13 – 16: To create a hero animation, we simply need to wrap our image into a Hero widget. It must have a unique tag. The exact tag also needs to be used again in our next step when creating the detail view.
    Within the Hero widget, we load a network image from picsum.photos . Using /seed/$index makes sure we always get the same image depending on our list index.
  • Lines 19 – 20: We add a title to each element and install a listener to react on tab events.
  • Lines 26 – 31: When a tile is tapped, we navigate to the DetailPage and pass the index of the tapped tile as an argument.
Simple Hero Animation Flutter
The master page of the app shows a list with images and text.

Hero Animation Part 2: The Detail Page

The detail_page.dart contains our DetailPage widget. It will only consist of a large SliverAppBar showing an image.

import 'package:flutter/material.dart'; class DetailPage extends StatelessWidget { final int index; const DetailPage({this.index}); @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( slivers: <Widget>[ SliverAppBar( expandedHeight: 300, flexibleSpace: FlexibleSpaceBar( title: Text("Item #$index"), background: Hero( tag: "avatar-$index", child: Image.network( "https://picsum.photos/seed/$index/800", fit: BoxFit.cover, ), ), ), ), ], ), ); } }
Code language: Dart (dart)
  • Line 4: To be able to construct the correct hero tag and to load the same image as in the ListTile, we need to know the unique index of the tapped tile.
  • Lines 10 – 14: Again, we wrap everything in a Scaffold and set a CustomScrollView as the body widget. The CustomScrollView allows us to add a SliverAppBar which – in contrast to a normal AppBar – allows us to specify a certain height.
  • Lines 15 – 16: We use a FlexibleSpaceBar to set a title and a background image to our SliverAppBar.
  • Lines 17 – 23: The most important part is that our background image is again wrapped in a Hero widget with the same tag as the tapped list element. If this is the case, Flutter takes care of the animation. Pretty cool, ha?
    For the Image we use the same URL that we have used before. In this case, the Image is already cached and will load quickly. We use BoxFit.cover to tell the Image to cover all the available space.
Simple Hero Animation Detail Flutter
The SliverAppBar contains the image that is animated from the ListTile.

When we now run the app, we have a cool little animation when navigating between our master and detail page. And that without having to write any complicated animation code.


Creating simple animations in Flutter is simple. Including them in your app can make it look smoother and more appealing in no time. Now that you know how to use the Hero widget, there is no excuse for boring looking apps anymore 😉

Let me know how you use hero animations in your app in the comments below.


Leave a Reply

Your email address will not be published. Required fields are marked *