Skip to main content

hero_here 2.3.1 copy "hero_here: ^2.3.1" to clipboard
hero_here: ^2.3.1 copied to clipboard

Hero animation within the same page: tag a widget's old and new places alike, change state, and it flies from one to the other. No Navigator or routes.

example/example.md

Flutter hero animation example

This Flutter hero animation example is an add to cart animation on the same page. Tap a product's button, and its picture, a hero widget, flies into the cart without navigation, where it lands on a soft spring.1 Tap it in the cart, and it flies back.

See the add to cart code › Install hero_here ›

Flutter add to cart animation: a product's picture flies from its card into the cart, and back

Get the highlights. #

  • No route. The picture flies within the page, as the page changes its state. Nothing is pushed or popped.
  • Two places. One tag. The picture is a HeroHere on its card and another in the cart, both tagged with the product's name.
  • Soft landing. It lands on a soft spring, with a little bounce.
  • Room to move. Take one out, and the pictures after it fly along to close the gap.
  • Back again. Tap a picture in the cart, and it flies back to its card.

Take a closer look. #

Three steps, all in one build method:

  1. The switcher. A HeroHereSwitcher wraps the page.
  2. The heroes. Each product's picture is a HeroHere: on its card while the product is out of the cart, and in the cart while it's in. Both have the product's name as their tag, and each has a key of its own. A picture's position in the cart is part of its key, so when one leaves, the ones after it fly along.
  3. The change. A tap adds the product to the cart, or takes it out, and the switcher flies each picture whose place changed.
import 'package:flutter/material.dart';
import 'package:hero_here/hero_here.dart';

import 'add_to_cart/shop.dart';
import 'style/style.dart';

void main() => runApp(
  MaterialApp(
    title: 'HeroHere Example',
    debugShowCheckedModeBanner: false,
    theme: exampleTheme,
    home: const AddToCartExample(),
  ),
);

/// A shop whose products' pictures fly into the cart, and back, within the
/// page:
///
/// 1. A [HeroHereSwitcher] wraps the page.
/// 2. Each product's picture is a [HeroHere], on its card or in the cart:
///    with the product's name as its tag, and a key of its own in each place.
/// 3. Adding a product to the cart, or taking it out, moves its hero, and
///    the switcher flies the picture there.
class AddToCartExample extends StatefulWidget {
  const AddToCartExample({super.key});

  @override
  State<AddToCartExample> createState() => _AddToCartExampleState();
}

class _AddToCartExampleState extends State<AddToCartExample> {
  final _cart = <Product>[];

  @override
  Widget build(BuildContext context) => HeroHereSwitcher(
    // 1. The switcher.
    child: Scaffold(
      appBar: AppBar(title: const Text('Shop')),
      body: ProductGrid(
        children: [
          for (final product in kProducts)
            ProductCard(
              product,
              inCart: _cart.contains(product),
              // 3. Adds the product to the cart, or takes it out.
              onToggle: () => _toggle(product),
              // 2. The product's picture on its card, while it's out of the
              // cart. In the cart, the card keeps its place, empty.
              picture: _cart.contains(product)
                  ? null
                  : HeroHere(
                      key: ValueKey('card-${product.name}'),
                      tag: product.name,
                      // Lands on a soft spring.
                      flightAnimationControllerFactory: softSpring.controller,
                      flightAnimationFactory: softSpring.animation,
                      forwardFlightAnimation: softSpring.forward,
                      reverseFlightAnimation: softSpring.reverse,
                      child: ProductPicture(product),
                    ),
            ),
        ],
      ),
      bottomNavigationBar: CartBar(
        total: _total,
        children: [
          for (final (position, product) in _cart.indexed)
            CartItem(
              // 3. A tap takes the product out of the cart.
              onTap: () => _toggle(product),
              // 2. The product's picture in the cart, while it's in: the same
              // tag as on its card, with another key. With its position in
              // the key, it flies along when one before it leaves the cart,
              // as a hero with a new key flies.
              picture: HeroHere(
                key: ValueKey('cart-${product.name}-$position'),
                tag: product.name,
                // Lands on a soft spring.
                flightAnimationControllerFactory: softSpring.controller,
                flightAnimationFactory: softSpring.animation,
                forwardFlightAnimation: softSpring.forward,
                reverseFlightAnimation: softSpring.reverse,
                child: ProductPicture(product),
              ),
            ),
        ],
      ),
    ),
  );

  int get _total => _cart.fold(0, (total, product) => total + product.price);

  void _toggle(Product product) => setState(
    () => _cart.contains(product) ? _cart.remove(product) : _cart.add(product),
  );
}

Find the hero animation that suits you best. #

Six more Flutter hero animation examples, each a widget that apps need, and each recorded as it runs.2 Run one from the repository's example folder, such as with flutter run -t lib/navigation_bar_hero/main.dart. In each, main.dart places the heroes, and a file beside it draws the rest.

Animated bottom navigation bar #

An animated bottom navigation bar with a sliding indicator, in a bottom navigation bar animation with no offsets to compute. Tap a tab, and the indicator flies to it, under the icons, with HeroHereSwitcher.skyUnderLayoutBuilder. Tap another before it lands, and it starts over toward that one from where it is, with HeroHereFlightRedirect.restart. See the navigation bar code ›

Animated bottom navigation bar: the sliding indicator flies to the tapped tab, and starts over toward Home mid-flight

Sliding segmented control #

A sliding segmented control that works as an animated toggle switch with three options: Day, Week and Month. Tap one, and the thumb flies to it, under the labels, with HeroHereSwitcher.skyUnderLayoutBuilder, as the chart's bars grow and shrink and the steps count to the period's total. Tap another mid-flight, and it turns toward that one at the speed it had, with HeroHereFlightRedirect.divert. See the segmented control code ›

Sliding segmented control: the thumb flies to the tapped period as the chart's bars grow and shrink

Splash screen animation #

A splash screen animation on the same page, with no route of its own. Once the app has loaded, the logo and the name fly from the middle of the animated splash screen into the app bar, landing on a soft spring as the content fades in. In this logo animation, the name scales with HeroHereShuttles.scale rather than laying out anew, so its text doesn't reflow in flight. See the splash screen code ›

Splash screen animation: the logo and the name fly into the app bar once the app has loaded

Chat message animation #

A chat message animation: send a message, and it flies from the text field up into the chat, where it lands on a soft spring. The spring is set on the message's hero in the chat only, since a flight takes its options from the hero it flies to. See the chat message code ›

Chat message animation: sent messages fly from the text field up into the chat

Shared element transition #

A shared element transition from a grid to details, as in a container transform or an open container animation, with no route. Tap a mountain, and its photo, name and description fly from the grid into its details, in a card expand animation. Pull the photo down, and they fly back, the photo as fast as the pull let go. See the grid to details code ›

Shared element transition: a mountain's photo flies from the grid into its details, and back on a pull

Flip card animation #

A flip card animation: tap a photo, and its card flies to the middle on a bouncy spring, in a card flip animation with HeroHereShuttles.flip that shows the mountain's name and description on the back. Tap it again, or beside it, and it flies back. See the flip card code ›

Flip card animation: a tapped photo flies to the middle on a bouncy spring and flips over to show the mountain's name and description

Compare all examples. #

Each example shows a few of hero_here's options. For what they do, see Redirected flights, Shuttles and Layout.

Example What flies Lands on A change mid-flight Shuttle Flies
Add to cart The product's picture A soft spring chain Default Over the page
Navigation bar The indicator easeInOut, 300 ms restart Default Under the icons
Segmented control The thumb easeInOut, 300 ms divert Default Under the labels
Splash screen The logo and the name A soft spring chain scale, for the name Over the page
Chat message The sent message A soft spring chain Default Over the page
Grid to details The photo, name and description easeInOut, or the pull's fling chain Custom Over the page
Flip card The card A bouncy spring chain flip Over the page

Questions? Answers. #

How do I make an add to cart animation in Flutter?

Wrap the page in a HeroHereSwitcher, and show each product's picture as a HeroHere where it is: on its card, or in the cart. Give it the same tag in both places, and a key of its own in each. Then add the product to the cart in setState, and the picture flies from its card to the cart, as in the code above.

Why doesn't my picture fly?

When a hero animation is not working, check two things. Its old and its new place both need a HeroHere with the same tag, in the same HeroHereSwitcher. And each needs a key of its own: a hero with the same key is the same hero, which shows where it is now rather than flying there.

What if the user taps again mid-flight?

The flight changes course, as its heroes' flightRedirect says. With chain, the default, it lands first, then flies on. With divert, it turns toward the new place at the speed it had. With restart, it starts over toward it from where it is, as the navigation bar's indicator does.

Can a hero fly under other widgets?

Yes. Set the switcher's layoutBuilder to HeroHereSwitcher.skyUnderLayoutBuilder, and its heroes fly under its child, as the segmented control's thumb flies under the labels.

1. The soft spring is SpringDescription.withDurationAndBounce, at 500 ms with a bounce of 0.25, in lib/style/motion.dart.
2. Recorded from the examples frame by frame, at 60 fps, in an iPhone 15 frame.

20
likes
0
points
1.08k
downloads

Publisher

verified publisherigorkurilenko.dev

Weekly Downloads

Hero animation within the same page: tag a widget's old and new places alike, change state, and it flies from one to the other. No Navigator or routes.

Repository (GitHub)
View/report issues

Topics

#animation #hero #transition #widget #ui

License

unknown (license)

Dependencies

flutter

More

Packages that depend on hero_here