Coding Liquids recordings reveal duplicate-tag and image-fit jumps that screenshots conceal.
Understand the shared-element flight
Hero identifies visually related widgets on two routes and moves an overlay representation between their rectangles. The source disappears into the flight while the destination takes over when the transition ends.
Hero is route choreography, not a general-purpose animation for widgets on the same page.
Match tags on both routes
The source and destination Hero must have equal tags within the same Navigator. Use a stable product identifier such as product-image-42 rather than a list index that changes after sorting.
A tag mismatch produces an ordinary route transition with no shared element.
Hero(
tag: 'product-${product.id}',
flightShuttleBuilder: (context, animation, direction, from, to) {
return FadeTransition(opacity: animation, child: to.widget);
},
child: Image.network(
product.imageUrl,
width: 96,
height: 96,
fit: BoxFit.cover,
),
)
// Use the same tag on the destination route's Hero.
Instagram flight frames reveal how tag identity, aspect ratio, clipping, and placeholders shape a Hero transition.
Fly an image from list to detail
Wrap the list thumbnail and detail image in Hero widgets with the same tag and compatible clipping. Keep aspect ratio and BoxFit choices deliberate so the interpolated rectangle does not reveal a sudden crop.
A network image that has not loaded on the destination can flash during the hand-off.

Build production-ready Hero Animations features
The Complete Flutter Guide turns hero animations into maintainable app architecture, polished UI, and testable production code.
Enrol nowCustomise the in-flight widget and rectangle
flightShuttleBuilder can choose source or destination content, and createRectTween controls the path between bounds. MaterialRectArcTween gives a curved Material-style movement while a custom RectTween can enforce another geometry.
Building an unrelated shuttle makes the element visibly morph at departure or arrival.
// Grid route
Hero(
tag: 'product-image-${product.id}',
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(product.imageUrl, fit: BoxFit.cover),
),
)
// Detail route
Hero(
tag: 'product-image-${product.id}',
createRectTween: (begin, end) => MaterialRectArcTween(begin: begin, end: end),
child: Image.network(product.imageUrl, fit: BoxFit.contain),
)
Generate unique tags for list items
Compose each tag from an immutable item ID and the visual role, such as avatar-user-17. The same item may need different tags for image and title when both are heroes.
Using one constant tag in every ListView row throws a duplicate hero exception.
Hero(
tag: 'avatar-${user.id}',
flightShuttleBuilder: (_, animation, direction, from, to) {
final destination = direction == HeroFlightDirection.push ? to.widget : from.widget;
return FadeTransition(opacity: animation, child: destination);
},
placeholderBuilder: (_, size, __) => SizedBox.fromSize(size: size),
child: CircleAvatar(backgroundImage: NetworkImage(user.avatarUrl)),
)
The LinkedIn shared-element analysis looks beyond polish to navigation scope, accessibility, and missing destinations.

The Complete Flutter Guide: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowAvoid common Hero visual jumps
Match border radius, image fit, and background colour, and use placeholderBuilder when the source layout needs a stand-in. Keep nested Navigators and nested Heroes intentional because each Navigator manages its own flights.
Animating text with different fonts or line wrapping often causes a distracting snap.
Make the shared element identity unambiguous
A Hero tag must identify one source and one destination within the participating navigator. Stable record IDs are safer than list indexes, because filtering or reordering can otherwise send the wrong card into flight. Two visible heroes with the same tag on one route trigger an assertion, so grids, overlays, and nested navigators deserve particular attention. Disable or differentiate heroes that may coexist during responsive layout changes.
The source and destination should agree on the element’s visual purpose even when their final sizes differ. Matching aspect ratio, border radius, and image fit reduces the snap at take-off and landing. Use flightShuttleBuilder only when the in-flight representation genuinely needs different material, clipping, or text direction. The builder receives both route animations, so it should not start an unrelated controller or perform data loading.
Network images can turn a polished transition into a blank rectangle. Resolve and cache the destination image early, provide a stable placeholder with matching geometry, and keep the image key consistent across routes. placeholderBuilder can preserve the source layout while its render object is in the overlay. For text or interactive controls, consider whether moving the entire component aids understanding; sometimes a thumbnail alone provides a clearer spatial connection.
Test push, pop, gesture-driven back navigation where supported, a missing image, rapid repeated navigation, and a deep link that opens the destination without a source hero. The destination must remain complete when no flight occurs. Increased text size and reduced-motion preferences can change geometry or make the transition undesirable, so offer a simple fade or no shared-element movement in that mode. Widget tests can verify unique tags and final routes, while a golden or device recording is more useful for clipping and interpolation artefacts.
Nested navigation can limit which heroes participate because the flight is coordinated by a navigator’s HeroController. Confirm the actual navigator used by tab shells, dialogs, and root-level detail routes before diagnosing a missing animation. The source and destination are built in route contexts with potentially different inherited themes, so text colour or direction may jump mid-flight. An explicit shuttle can freeze the intended appearance, but it should remain lightweight. If a record disappears during the transition, let the destination show a valid not-found state after landing instead of throwing while the overlay still owns the visual element.
Hero flights temporarily move rendering into an overlay, which can expose assumptions about inherited media padding, clipping, or material ancestors. Test near screen edges, under display cut-outs, and across routes with different scaffold backgrounds. A placeholder should keep the original layout stable while the overlay travels. If the overlay representation needs Material ink or text styling, provide those ancestors explicitly instead of relying on the source route to remain underneath it.
A product-list transition should connect one stable thumbnail to one detail image while leaving price, badges, and actions in ordinary route layout. Precache the larger image after the source becomes visible, but keep a matching placeholder for offline or failed loads. The destination can open from search, a notification, or a saved link, so it must render correctly without a source flight. That fallback path also protects a user who taps just as the underlying record is removed.
Focus belongs to the destination after navigation, not to the overlay in flight. Interactive content inside a Hero can produce confusing semantics and ink effects, so keep the flying subtree visually focused and restore controls on landing. Test back gestures at partial progress, a rotation or size-class change, and two navigators in a tab shell. If the same record can appear twice on one route, scope or disable the duplicate tag. A stable, accessible page reached without animation is more important than preserving the flourish in every unusual navigation path.
Common mistakes
- Understand the shared-element flight: In Hero transitions, hero is route choreography, not a general-purpose animation for widgets on the same page; inspect this Hero transitions cause before changing another Hero transitions widget.
- Match tags on both routes: In Hero transitions, a tag mismatch produces an ordinary route transition with no shared element; inspect this Hero transitions cause before changing another Hero transitions widget.
- Fly an image from list to detail: In Hero transitions, a network image that has not loaded on the destination can flash during the hand-off; inspect this Hero transitions cause before changing another Hero transitions widget.
- Customise the in-flight widget and rectangle: In Hero transitions, building an unrelated shuttle makes the element visibly morph at departure or arrival; inspect this Hero transitions cause before changing another Hero transitions widget.
- Generate unique tags for list items: In Hero transitions, using one constant tag in every ListView row throws a duplicate hero exception; inspect this Hero transitions cause before changing another Hero transitions widget.
- Avoid common Hero visual jumps: In Hero transitions, animating text with different fonts or line wrapping often causes a distracting snap; inspect this Hero transitions cause before changing another Hero transitions widget.
Frequently asked questions
How does understand the shared-element flight work in Hero transitions?
For Hero transitions, the starting rule is that hero identifies visually related widgets on two routes and moves an overlay representation between their rectangles. Apply this Hero transitions rule first because understand the shared-element flight determines whether the Hero transitions pattern fits.
Why does fly an image from list to detail matter for Hero transitions?
In Hero transitions, keep aspect ratio and BoxFit choices deliberate so the interpolated rectangle does not reveal a sudden crop. Keeping fly an image from list to detail at the Hero transitions call site exposes the Hero transitions return value directly.
What failure should I test first in Hero transitions?
First reproduce the Hero transitions case where using one constant tag in every ListView row throws a duplicate hero exception. The fixed list derives each Hero tag from a stable record ID and completes the push without a duplicate-tag assertion.
How can I verify Hero transitions before release?
Exercise avoid common Hero visual jumps with real Hero transitions inputs on every shipped platform. Inspect the final Hero transitions callback or output; a successful Hero transitions button tap alone is not proof.
Further reads
Connect Hero transitions to the surrounding Flutter stack through these published tutorials:
- Flutter Development Guide 2026
- Flutter Navigation Basics: push, pop, and Named Routes
- Flutter Custom Page Route Transitions
- Flutter GridView Tutorial: Build Responsive Grids Step by Step
- Flutter Animations: The Complete Guide With Code Examples
Sources: Flutter framework and Dart API documentation; Hero transitions examples verified against current stable Flutter.