Bird
Raised Fist0
Fluttermobile~20 mins

Hero animations in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Hero Animation Demo
This screen demonstrates a hero animation between a list of images and a detail view. When the user taps an image, it smoothly transitions to a larger version on a new screen.
Target UI
List Screen:
+-----------------------+
| [Image 1]             |
| [Image 2]             |
| [Image 3]             |
|                       |
| Tap an image to view  |
+-----------------------+

Detail Screen:
+-----------------------+
| [Large Image]         |
|                       |
| Back button           |
+-----------------------+
Create a list screen with at least 3 tappable images.
Each image uses a Hero widget with a unique tag.
On tap, navigate to a detail screen showing the tapped image larger.
The detail screen also uses a Hero widget with the same tag for smooth animation.
Include a back button to return to the list screen.
Starter Code
Flutter
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hero Animation Demo',
      home: const ImageListScreen(),
    );
  }
}

class ImageListScreen extends StatelessWidget {
  const ImageListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Images')),
      body: ListView(
        children: [
          // TODO: Add tappable images with Hero widgets here
        ],
      ),
    );
  }
}

class ImageDetailScreen extends StatelessWidget {
  final String imageTag;
  final String imageUrl;

  const ImageDetailScreen({super.key, required this.imageTag, required this.imageUrl});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Image Detail')),
      body: Center(
        // TODO: Add Hero widget with the image here
      ),
    );
  }
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hero Animation Demo',
      home: const ImageListScreen(),
    );
  }
}

class ImageListScreen extends StatelessWidget {
  const ImageListScreen({super.key});

  final List<Map<String, String>> images = const [
    {'tag': 'image1', 'url': 'https://picsum.photos/id/1011/200/200'},
    {'tag': 'image2', 'url': 'https://picsum.photos/id/1012/200/200'},
    {'tag': 'image3', 'url': 'https://picsum.photos/id/1013/200/200'},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Images')),
      body: ListView(
        padding: const EdgeInsets.all(8),
        children: images.map((image) {
          return GestureDetector(
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => ImageDetailScreen(
                    imageTag: image['tag']!,
                    imageUrl: image['url']!,
                  ),
                ),
              );
            },
            child: Hero(
              tag: image['tag']!,
              child: Padding(
                padding: const EdgeInsets.symmetric(vertical: 8),
                child: Image.network(image['url']!, width: 200, height: 200, fit: BoxFit.cover),
              ),
            ),
          );
        }).toList(),
      ),
    );
  }
}

class ImageDetailScreen extends StatelessWidget {
  final String imageTag;
  final String imageUrl;

  const ImageDetailScreen({super.key, required this.imageTag, required this.imageUrl});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Image Detail')),
      body: Center(
        child: Hero(
          tag: imageTag,
          child: Image.network(imageUrl, width: 300, height: 300, fit: BoxFit.cover),
        ),
      ),
    );
  }
}

We created a list of images each wrapped in a Hero widget with a unique tag. When the user taps an image, the app navigates to a detail screen passing the tag and URL. The detail screen also uses a Hero widget with the same tag to create a smooth transition animation between the small image and the large image. This demonstrates the hero animation concept in Flutter simply and clearly.

Final Result
Completed Screen
Images Screen:
+-----------------------+
| [Image 1]             |
|                       |
| [Image 2]             |
|                       |
| [Image 3]             |
+-----------------------+

Tap an image to see it enlarge with a smooth animation.

Detail Screen:
+-----------------------+
|                       |
|       [Large Image]   |
|                       |
|       < Back          |
+-----------------------+
Tap an image on the list screen to navigate to the detail screen with a smooth hero animation enlarging the image.
Tap the back button in the app bar to return to the list screen with a reverse hero animation.
Stretch Goal
Add a fade transition along with the hero animation when navigating between screens.
💡 Hint
Use PageRouteBuilder with a FadeTransition wrapping the Hero animation.

Practice

(1/5)
1. What is the main purpose of a Hero widget in Flutter?
easy
A. To create smooth animations between two screens by linking widgets with the same tag
B. To manage state across multiple widgets
C. To handle user input events
D. To fetch data from the internet asynchronously

Solution

  1. Step 1: Understand Hero widget role

    The Hero widget is designed to animate a widget from one screen to another smoothly.
  2. Step 2: Identify correct purpose

    It links widgets on different screens using the same tag to create a seamless transition.
  3. Final Answer:

    To create smooth animations between two screens by linking widgets with the same tag -> Option A
  4. Quick Check:

    Hero widget = smooth screen transition [OK]
Hint: Hero widgets link by tag for smooth screen animations [OK]
Common Mistakes:
  • Confusing Hero with state management widgets
  • Thinking Hero handles user input
  • Assuming Hero fetches data
2. Which of the following is the correct way to define a Hero widget with a tag of 'profilePic'?
easy
A. Hero(tag: profilePic, child: Image.asset('pic.png'))
B. Hero(child: Image.asset('pic.png'), tag: profilePic)
C. Hero(tag: 'profilePic', child: Image.asset('pic.png'))
D. Hero('profilePic', child: Image.asset('pic.png'))

Solution

  1. Step 1: Check Hero constructor syntax

    The Hero widget requires named parameters: tag and child.
  2. Step 2: Validate correct parameter order and types

    Hero(tag: 'profilePic', child: Image.asset('pic.png')) uses named parameters correctly with a string tag and a child widget.
  3. Final Answer:

    Hero(tag: 'profilePic', child: Image.asset('pic.png')) -> Option C
  4. Quick Check:

    Hero uses named parameters tag and child [OK]
Hint: Use named parameters: tag first, then child [OK]
Common Mistakes:
  • Passing tag as positional argument
  • Using unquoted tag string
  • Swapping tag and child order without names
3. Given this code snippet, what will happen when navigating from Screen A to Screen B?
Screen A:
Hero(tag: 'logo', child: Image.asset('logo.png'))

Screen B:
Hero(tag: 'logo', child: Image.asset('logo_large.png'))
medium
A. The logo image will smoothly animate from Screen A to Screen B, changing size
B. No animation will occur because the images are different
C. The app will crash due to tag conflict
D. The Hero animation will flicker and not complete

Solution

  1. Step 1: Understand Hero tag matching

    Hero animations link widgets with the same tag, regardless of child widget differences.
  2. Step 2: Predict animation behavior

    The image will animate smoothly from the smaller to the larger version because the tag matches.
  3. Final Answer:

    The logo image will smoothly animate from Screen A to Screen B, changing size -> Option A
  4. Quick Check:

    Matching tags animate widgets smoothly [OK]
Hint: Same tag triggers animation even if child widgets differ [OK]
Common Mistakes:
  • Assuming images must be identical for animation
  • Thinking different children cause crashes
  • Expecting no animation if child changes
4. What is wrong with this Hero animation code?
Hero(tag: 'avatar', child: Container(color: Colors.blue))

// On next screen
Hero(tag: 'avatar', child: Container(color: Colors.red))
medium
A. The child widgets are not wrapped in Material widgets
B. The Hero animation will fail because Container has no size
C. The Hero widgets have different tags
D. Nothing is wrong; this code works correctly

Solution

  1. Step 1: Check Hero child widget requirements

    Hero animations require child widgets to have a defined size to animate properly.
  2. Step 2: Analyze Container usage

    Container with only color but no size defaults to zero size, causing animation failure.
  3. Final Answer:

    The Hero animation will fail because Container has no size -> Option B
  4. Quick Check:

    Hero child needs size to animate [OK]
Hint: Ensure Hero child widgets have size for animation [OK]
Common Mistakes:
  • Thinking tag mismatch causes error here
  • Assuming Material widget is always required
  • Ignoring size requirement of Hero child
5. You want to create a Hero animation for a profile picture that appears in a list and on a detail page. The list shows a small circular avatar, and the detail page shows a larger square image. How should you implement the Hero widgets to ensure a smooth animation?
hard
A. Use different tags for each Hero widget to avoid conflicts
B. Use the same tag but keep both images circular to match shapes exactly
C. Wrap both Hero widgets in Material widgets with different elevation values
D. Use the same tag on both Hero widgets; wrap the small avatar in a ClipOval and the large image in a ClipRRect with border radius zero

Solution

  1. Step 1: Use matching tags for Hero animation

    Both Hero widgets must share the same tag to link the animation.
  2. Step 2: Handle shape differences smoothly

    Wrap the small avatar in ClipOval for circle shape and the large image in ClipRRect with zero border radius for square shape to animate shape change smoothly.
  3. Final Answer:

    Use the same tag on both Hero widgets; wrap the small avatar in a ClipOval and the large image in a ClipRRect with border radius zero -> Option D
  4. Quick Check:

    Same tag + shape wrappers = smooth Hero animation [OK]
Hint: Match tags and wrap widgets to animate shape changes [OK]
Common Mistakes:
  • Using different tags breaks animation
  • Ignoring shape differences causes jumpy animation
  • Assuming Material widget fixes shape issues
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard