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.