Flutter基建 - Hero动画有多英雄

2,749 阅读4分钟

本篇基于Flutter 3.16.4,Dart 3.2.3版本

Flutter 3.16.4 • channel stable • github.com/flutter/flu…

Framework • revision 2e9cb0aa71 (3 days ago) • 2023-12-11 14:35:13 -0700

Engine • revision 54a7145303

Tools • Dart 3.2.3 • DevTools 2.28.4

本篇为Flutter基建的第十篇文章💪🏻💪🏻💪🏻,主要介绍的是Flutter中Hero动画,Hero动画不仅仅在Flutter中存在,原生的Android包括Compose中都是有这个概念,它字面意思是英雄动画,其实就是在路由跳转(页面切换)时提供了一种类似飞行的效果,使得页面切换过渡非常丝滑和流畅的视角效果,最常用的场景为小图查看大图,下面一起进入文章的了解下吧~

Flutter基建系列文章

Flutter基建 - Dart基础类型

Flutter基建 - Dart方法和类

Flutter基建 - 文本组件

Flutter基建 - 按钮全解析

Flutter基建 - 布局组件全面解析

Flutter基建 - 离不开的列表组件

Flutter基建 - 形影不离的State**Widget

Flutter基建 - 异步加载UI

Flutter基建 - 12种隐式动画小组件全解析

Flutter基建 - Hero动画有多英雄

Hero动画

我们就以点击小图然后跳转到大图界面为例,来看看Hero动画可以帮助我们实现什么样的效果。

列表界面

我们先来实现一个列表界面,列表中每个item展示一个Image和Text,然后对Image做Hero动画由小图飞行至大图。

class HeroAnimated extends StatelessWidget {
  const HeroAnimated({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          "Hero Animated",
        ),
      ),
      body: ListView.builder(
        itemBuilder: (context, index) {
          return HeroItem(index);
        },
        itemCount: 20,
      ),
    );
  }
}

这里用ListView.builder实现一个列表组件,列表的item为HeroItem,传入参数index,此index是为了给Hero的tag所用。

class HeroItem extends StatelessWidget {
  final int index;

  const HeroItem(this.index, {super.key});

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text('Item $index'),
      leading: Hero(
        createRectTween: (begin, end) {
          return RectTween(begin: begin, end: end);
        },
        tag: "hero-item-$index",
        child: const Image(
          image: AssetImage("images/img.png"),
        ),
      ),
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return SecHeroWidget(index);
            },
          ),
        );
      },
    );
  }
}

Item组件直接采用的是ListTile实现(主要是为了方便^-^),然后对内部Image做了一个Hero父组件包裹,注意这里Hero传入的参数:

  • createRectTween表示飞行过程中是以何种线条运动,我们传入的是RectTween它就会在小图和大图的矩形之间直接飞行过去,除了RectTween之外,还有MaterialRectArcTween和MaterialRectCenterArcTween两种,这两种在飞行的轨迹中都会有一个弧度,看起来动画效果更为精致一点,一会我们会分别展示下两种类型的效果;
  • tag参数非常重要,小图和大图的Hero tag参数必须保持一致,否则系统会找不到对应的大图无法执行动画,而且不可以一对多或者多对多,只可以一对一的形式;
  • child参数就是我们需要的小图,这里传入的是Image组件,不过多介绍。

写完列表界面之后,我们再来看看大图界面是如何实现的。

大图界面

class ImageDetailWidget extends StatelessWidget {
  final int index;

  const ImageDetailWidget(this.index, {super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("ImageDetail"),
      ),
      body: Hero(
        createRectTween: (begin, end) {
          return RectTween(begin: begin, end: end);
        },
        tag: "hero-item-$index",
        child: const Center(
          child: Image(
            image: AssetImage("images/img.png"),
          ),
        ),
      ),
    );
  }
}

大图界面的逻辑就比较简单了,将小图对应的index传入,此index参数需要传入到Hero的key种,这样就能保证小图和大图的key对应起来,大图的tween也是和小图保持一致,接下来我们来看看实际的效果。

使用Hero动画之后,在页面切换的过程中可以看到小图和大图有一个逐渐放大和缩小的动画,比起直接跳转页面的视角效果要好看的多,但是RectTween这种方式给人的感觉还是有点生硬,下面我们将RectTween替换为MaterialRectArcTween再来看看效果。

写在最后

本篇文章主要介绍了Flutter中Hero动画的基本使用,文章内容比较简单,但是Hero动画在日常开发中可以帮助我们优化页面跳转的视角效果,还是比较实用的一个知识点,希望通过文章给阅读的小伙伴们带来一点帮助,后续会循序渐进逐步接触Flutter更多的知识。

我是Taonce,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢😆😆~