Skip to content

Image in Hero animation is displayed outside the borders #100903

@diegoveloper

Description

@diegoveloper

The problem is that it's not clipping the image correctly on Lists and when the Image is displayed using a custom size.
I have an image like this:

person

And I want to display it using BoxFit.cover, but I just want to display a small part of the top of the image, so I used this code:

      Image.asset(
                'assets/person.png',
                fit: BoxFit.cover,
                width: width,
                alignment: Alignment.topCenter,
              ),

Alignment.topCenter allow us to focus on the top of the image instead of the center.

Steps to Reproduce

  1. Execute flutter run on the code sample
  2. Using the code attached, if you use _ItemCard(id: 1), it will work without problems, but I don't want to cover all the card with the top of the image, I just want to reduce the size of the image, then use this : _ItemCard(id: 1, clipped: true),.
  3. Tap on the Card, see the hero animation.

Expected results:

I don't want to see the image outside borders for elements on the List.

Actual results:

  • This is working fine, but I want to reduce the content displayed.
hero1.mov
  • This is not working fine, this is how I want to display the image on the card, but the hero is not working correctly, and it's working different on each item from the List.
hero2.mov

See the problem.

hero_bug

Code sample
class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    timeDilation = 3.0;
    const separator = SizedBox(height: 40);
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              _ItemCard(id: 1, clipped: true),
              separator,
              _ItemCard(id: 2, clipped: true),
              separator,
              _ItemCard(id: 3, clipped: true),
              separator,
            ],
          ),
        ),
      ),
    );
  }
}

class _ItemCard extends StatelessWidget {
  const _ItemCard({
    required this.id,
    this.clipped = false,
    Key? key,
  }) : super(key: key);

  final int id;
  final bool clipped;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 120,
      child: LayoutBuilder(builder: (context, constraints) {
        return GestureDetector(
          onTap: () {
            Navigator.of(context).push(
              PageRouteBuilder(
                pageBuilder: (_, __, ___) => _ItemDetails(
                  id: id,
                ),
                transitionsBuilder: (_, animation, __, child) =>
                    FadeTransition(opacity: animation, child: child),
              ),
            );
          },
          child: Builder(builder: (context) {
            final width = constraints.maxWidth * 0.5;
            final hero = Hero(
              tag: 'hero_details_$id',
              child: Image.asset(
                'assets/person.png',
                fit: BoxFit.cover,
                width: width,
                alignment: Alignment.topCenter,
              ),
            );
            return Stack(
              clipBehavior: Clip.none,
              children: [
                Positioned.fill(
                  child: Hero(
                    tag: 'background_$id',
                    child: Container(
                      color: Colors.black,
                    ),
                  ),
                ),
                if (clipped)
                  Positioned(
                      left: constraints.maxWidth / 2 - width / 2,
                      top: 0,
                      width: width,
                      bottom: 0,
                      child: hero)
                else
                  Positioned.fill(
                    child: hero,
                  ),
              ],
            );
          }),
        );
      }),
    );
  }
}

class _ItemDetails extends StatelessWidget {
  const _ItemDetails({
    required this.id,
    Key? key,
  }) : super(key: key);

  final int id;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: [
            Positioned(
              left: 0,
              right: 0,
              top: 0,
              height: 400,
              child: Stack(
                children: [
                  Hero(
                    tag: 'background_$id',
                    child: Container(
                      color: Colors.black,
                    ),
                  ),
                  Positioned.fill(
                    top: 100,
                    child: Hero(
                      tag: 'hero_details_$id',
                      child: Image.asset(
                        'assets/person.png',
                        fit: BoxFit.cover,
                        alignment: Alignment.topCenter,
                      ),
                    ),
                  ),
                ],
              ),
            ),
            Positioned(
              left: 0,
              top: 0,
              right: 0,
              height: kToolbarHeight,
              child: AppBar(
                backgroundColor: Colors.transparent,
                elevation: 0,
                leading: const BackButton(
                  color: Colors.white,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
[✓] Flutter (Channel stable, 2.10.3, on macOS 12.3 21E230 darwin-arm, locale en-PE)
    • Flutter version 2.10.3 at /Users/diegoveloper/fvm/versions/2.10.3
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 7e9793dee1 (4 weeks ago), 2022-03-02 11:23:12 -0600
    • Engine revision bd539267b4
    • Dart version 2.16.1
    • DevTools version 2.9.2

[✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
    • Android SDK at /Users/diegoveloper/Library/Android/sdk
    • Platform android-32, build-tools 32.0.0
    • ANDROID_HOME = /Users/diegoveloper/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2020.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)

Metadata

Metadata

Assignees

Labels

a: animationAnimation APIsa: imagesLoading, displaying, rendering imagesd: api docsIssues with https://api.flutter.dev/d: examplesSample code and demosf: material designflutter/packages/flutter/material repository.found in release: 2.10Found to occur in 2.10found in release: 2.13Found to occur in 2.13frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer version

Type

No type

Projects

Status

Done (PR merged)

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions