Hero Animations in Flutter: Creating Seamless Transitions Between Screens
Animations in mobile apps aren’t just about making things move — they’re about guiding the user’s attention and creating a smooth, engaging experience. In Flutter, one of the most intuitive ways to achieve this is with Hero animations, where a visual element appears to “fly” between screens. It’s not just eye candy; it’s functional design that helps users stay oriented while navigating your app.
In this article, we’ll dive into Hero animations, understand how they work, and explore their real-world applications. Whether you’re building an educational app or a portfolio showcase, this concept can bring a professional edge to your projects. Let’s get started.
What Are Hero Animations, and Why Do They Matter?
A Hero animation connects an element on one screen with its counterpart on another. For example, imagine a photo preview that expands into full view when tapped. Instead of instantly switching screens, the photo transitions smoothly, helping the user maintain their sense of context.
This effect is powered by Flutter’s Hero
widget. The Hero widget identifies linked elements using a tag, which is a unique identifier shared between the source and destination widgets. The tag tells Flutter, “These elements represent the same thing, so animate them as if they’re one.”
Can You Use Hero Animations Without Tags?
No, Hero animations require tags. The tag
is the key to linking the source and destination widgets. Without it, Flutter cannot associate the two widgets and won’t know what to animate.
However, this does not limit you to static tags. You can dynamically assign tags based on data, which is especially useful when building animations for lists or dynamic content. For example, you might use the ID of an item in a list as its tag, ensuring unique animations for each element.
A Step-by-Step Hero Animation Example
Let’s start with a basic Hero animation where an image transitions between two screens:
Example 1: Animating an Image
import 'package:flutter/material.dart';
void main() => runApp(HeroAnimationApp());
class HeroAnimationApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Screen')),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Hero(
tag: 'hero-image',
child: ClipOval(
child: Image.network(
'https://picsum.photos/200',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Screen')),
body: Center(
child: Hero(
tag: 'hero-image',
child: Image.network(
'https://picsum.photos/200',
width: 300,
height: 300,
fit: BoxFit.cover,
),
),
),
);
}
}
What Happens Here?
- The
Hero
widgets on both screens share the sametag
('hero-image'
), creating a link. - When navigating to the second screen, Flutter animates the transition of the shared element.
- The
ClipOval
in the source screen adds a circular shape, while the destination enlarges the same image without additional clipping.
Advanced Example: Animating Text and Buttons
Let’s take it a step further. In this example, a button on the first screen transforms into a larger button with text on the second screen:
Example 2: Transforming a Button
import 'package:flutter/material.dart';
void main() => runApp(AdvancedHeroApp());
class AdvancedHeroApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Screen')),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Hero(
tag: 'hero-button',
child: Container(
width: 100,
height: 50,
color: Colors.blue,
child: Center(child: Text('Tap Me', style: TextStyle(color: Colors.white))),
),
),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Screen')),
body: Center(
child: Hero(
tag: 'hero-button',
child: Container(
width: 200,
height: 100,
color: Colors.blue,
child: Center(
child: Text('Welcome!', style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
),
),
);
}
}
What’s New Here?
- The
Hero
widget animates a transformation of size and text content. - The button starts as a small clickable area and becomes an interactive greeting on the second screen.
- This example demonstrates that Hero animations are not limited to images — they work with any widget.
Where Can You Use Hero Animations?
Hero animations are versatile and can be used in various scenarios:
- Onboarding flows: Create smooth transitions between steps to keep users engaged.
- Detail views: Highlight key information by animating elements like charts, images, or cards.
- Interactive stories: Use animations to guide users through narratives or tutorials.
By creatively applying Hero animations, you can make your apps feel polished and intuitive.
Conclusion
Hero animations are more than just a visual effect — they’re a way to guide users and create a cohesive experience. In this article, we explored the importance of tags, walked through two distinct examples, and discussed practical use cases. With these tools, you’re well-equipped to enhance your Flutter apps with seamless transitions.
Feel free to share your thoughts, questions, or even your own creations in the comments below. And if you enjoyed this article, I’d be grateful for your claps and a follow. Every bit of support motivates me to bring you more content like this. Keep coding, and I’ll see you in the next lesson!