Есть ли способ реализовать плавную анимацию героя для виджета Image из BoxFit.cover в BoxFit.contain?

Это очень распространенный паттерн UX, с которым мы сталкиваемся каждый день:

Щелкните изображение в галерее, после плавного перехода покажите изображение в полноэкранном режиме, а затем вы можете увеличить его для просмотра деталей.

Но во Flutter мне было трудно реализовать такую ​​анимацию плавно, конечно, с виджетом Hero я могу закончить базовый код за минуту, но вот проблема: в галерее изображение помещается в небольшой квадрат с его режимом подгонки. BoxFit.cover, на странице режима масштабирования, его режим подгонки должен быть BoxFit.contain. Когда анимация героя начинается и заканчивается, режим подгонки резко меняется на целевой.

      // main.dart
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

const String url =
    'https://images.unsplash.com/photo-1635335333546-41f848cea96c';

void main() {
  runApp(
    const MaterialApp(
      home: GalleryWidget(),
    ),
  );
}

class GalleryWidget extends StatelessWidget {
  const GalleryWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    timeDilation = 10.0;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Gallery'),
      ),
      body: Material(
        child: InkWell(
          child: Hero(
            tag: url,
            child: Image.network(
              url,
              width: 100.0,
              height: 100.0,
              fit: BoxFit.cover,
            ),
          ),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const PhotoViewerWidget(url: url),
                fullscreenDialog: true,
              ),
            );
          },
        ),
      ),
    );
  }
}

class PhotoViewerWidget extends StatelessWidget {
  final String url;

  const PhotoViewerWidget({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.black87,
      child: InkWell(
        child: Hero(
          tag: url,
          child: Image.network(url),
        ),
        onTap: () => Navigator.of(context).pop(),
      ),
    );
  }
}

2 ответа

Решение

Я готов поспорить, что задержка анимации вызвана только тем, что вы запускаете приложение в режиме отладки. В режиме отладки существует много фонового трафика, из-за которого эти переходы задерживаются. Попробуйте запустить приложение на реальном устройстве, и когда вы это сделаете, запустите его из терминала с помощью этой команды flutter run --release. Он установит приложение в режиме выпуска, и вы сразу заметите разницу.

Сообщите мне, если это так.

проблема возникает из-за разного типа свойства подгонки в GallerWidget и PhotoViewerWidget. Вы можете просто достичь решения, используя одно и то же свойство подгонки в обоих случаях.

В твоем случае :

      import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class GalleryWidget extends StatelessWidget {
  GalleryWidget({Key? key}) : super(key: key);

  String url = 'https://images.unsplash.com/photo-1635335333546-41f848cea96c';

  @override
  Widget build(BuildContext context) {
    timeDilation = 10.0;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Gallery'),
      ),
      body: Material(
        child: InkWell(
          child: Hero(
            tag: url,
            child: SizedBox(
              width: 100.0,
              height: 100.0,
              child: Image.network(
                url,
                fit: BoxFit.fitWidth, 
              ),
            ),
          ),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => PhotoViewerWidget(url: url),
                fullscreenDialog: true,
              ),
            );
          },
        ),
      ),
    );
  }
}

class PhotoViewerWidget extends StatelessWidget {
  final String url;

  const PhotoViewerWidget({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.black87,
      child: InkWell(
        child: Hero(
          tag: url,
          child: Image.network(
            url,
            fit: BoxFit.fitWidth,
          ),
        ),
        onTap: () => Navigator.of(context).pop(),
      ),
    );
  }
}
Другие вопросы по тегам