Как создать анимацию в стиле героя на одном экране с Flutter? - PullRequest
0 голосов
/ 25 сентября 2018

Mockup

Я пытаюсь создать динамическую анимацию, описанную в макете выше.Мне нужно:

  • Круглая форма, которая будет представлять аватар (например, рисунок пользователя);
  • Текст под ним.
  • Ниже этих(используется половина экрана) с возможностью прокрутки PageView.

Анимация должна выглядеть следующим образом:

Начало: В Stack оба по центру в начале.
Анимация: Уменьшение и сдвиг текста (с переменная длина ), который должен быть справа от аватара.
Конец:
Как второе изображение в макете.Из стороны в сторону, пока содержимое ниже прокручивается.

Мысль в SliverPersistentHeader в сочетании с CustomMultiChildLayout, но проблема в том, что текст начинается по центру и заканчивается по левому краю, и я могу динамически анимировать это.Я пытался удалить смещение по центру текста в конце, но это не правильно.

Буду признателен за любую помощь или образец только с этой анимацией.Спасибо.

1 Ответ

0 голосов
/ 25 сентября 2018

Вам понадобится Щепка, чтобы анимировать макет на основе смещения прокрутки.В частности, SliverPersistentHeader в вашей ситуации.

CustomMultiChildLayout не требуется, хотя, вы можете достичь того же результата, используя анимацию и выравнивание / заполнение / материал.Но вы можете попробовать, если ваш макет начинает становиться слишком сложным.

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

Вот пример:

enter image description here

class TransitionAppBar extends StatelessWidget {
  final Widget avatar;
  final Widget title;

  const TransitionAppBar({this.avatar, this.title, Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SliverPersistentHeader(
      pinned: true,
      delegate: _TransitionAppBarDelegate(
        avatar: avatar,
        title: title,
      ),
    );
  }
}

class _TransitionAppBarDelegate extends SliverPersistentHeaderDelegate {
  final _avatarTween =
      SizeTween(begin: Size(150.0, 150.0), end: Size(50.0, 50.0));
  final _avatarMarginTween =
      EdgeInsetsTween(begin: EdgeInsets.zero, end: EdgeInsets.only(left: 10.0));
  final _avatarAlignTween =
      AlignmentTween(begin: Alignment.topCenter, end: Alignment.centerLeft);

  final _titleMarginTween = EdgeInsetsTween(
      begin: EdgeInsets.only(top: 150.0 + 5.0),
      end: EdgeInsets.only(left: 10.0 + 50.0 + 5.0));
  final _titleAlignTween =
      AlignmentTween(begin: Alignment.center, end: Alignment.centerLeft);

  final Widget avatar;
  final Widget title;

  _TransitionAppBarDelegate({this.avatar, this.title})
      : assert(avatar != null),
        assert(title != null);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    final progress = shrinkOffset / 200.0;

    final avatarSize = _avatarTween.lerp(progress);
    final avatarMargin = _avatarMarginTween.lerp(progress);
    final avatarAlign = _avatarAlignTween.lerp(progress);

    final titleMargin = _titleMarginTween.lerp(progress);
    final titleAlign = _titleAlignTween.lerp(progress);
    return Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Padding(
          padding: avatarMargin,
          child: Align(
            alignment: avatarAlign,
            child: SizedBox.fromSize(size: avatarSize, child: avatar),
          ),
        ),
        Padding(
          padding: titleMargin,
          child: Align(
            alignment: titleAlign,
            child: DefaultTextStyle(
                style: Theme.of(context).textTheme.title, child: title),
          ),
        )
      ],
    );
  }

  @override
  double get maxExtent => 200.0;

  @override
  double get minExtent => 100.0;

  @override
  bool shouldRebuild(_TransitionAppBarDelegate oldDelegate) {
    return avatar != oldDelegate.avatar || title != oldDelegate.title;
  }
}

, которыйВы можете использовать с CustomScrollView:

Scaffold(
  body: CustomScrollView(
    slivers: <Widget>[
      TransitionAppBar(
        avatar: Material(
          color: Colors.blue,
          elevation: 3.0,
        ),
        title: Text("Hello World"),
      ),
      SliverList(
        delegate: SliverChildBuilderDelegate((context, index) {
          return ListTile(
            title: Text('$index'),
          );
        }),
      )
    ],
  ),
);
...