Flutter开发·Hero与HeroMode的使用

1,477 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

我们经常看到一些APP在从列表点击进入到详情时会有一个图片放大的渐变效果,不像是页面的跳转。给用户一种无缝切换的感觉。这种效果通常称为共享元素转换动画,那么它在Flutter中是如何实现呢?

效果图

其实Flutter中官方给我们提供了非常好用的控件:Hero。

Hero

中文网给出的定义是:Hero指的是可以在路由(页面)之间“飞行”的widget。将 hero从一个路由飞到另一个路由。

hero 动画代码应该具有以下结构:

1.定义一个起始 hero widget,称为"源 hero"。 hero 指定其图形表示(通常是图片)和识别标记,并且位于源路由定义的当前显示的 widget树中。

2.定义一个结束的 hero widget,称为"目标 hero"。这位 hero 也指定了它的图形表示,以及与源 hero 相同的标记。重要的是两个 hero widget都使用相同的标签创建,通常是代表底层数据的对象。为了获得最佳效果, hero 应该有几乎相同的 widget树。

3.创建一个包含目标 hero 的路由。目标路由定义了动画结束时的 widget树。

4.通过导航器将目标路由入栈来触发动画。Navigator推送和弹出操作会为每对 hero 配对,并在源路由和目标路由中使用匹配的标签触发 hero 动画。

5.Flutter计算从起点到终点对 hero 界限进行动画处理的补间(生成每一帧大小和位置),并在叠加层中执行动画。

根据要求及定义就可以直接撸代码了,很简单:

首先是第一个界面:

Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            InkWell(
              onTap: (){
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return SecondHeroPage(tag: "tag",imageUrl: imageUrl,);
                }));
              },
              child: Hero(
                  tag: "tag",
                  child: Image.network(
                    imageUrl,
                    width: 200,
                    height: 200,
                  )),
            ),
            Text("点击图片跳转")
          ],
        ),
      ),
    );
  }

第二个界面

Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          InkWell(
            onTap: (){
              Navigator.of(context).pop();//这里pop后也是有动画的
            },
            child: Hero(
                tag: widget.tag,
                child: Image.network(
                  widget.imageUrl,
                  width: double.infinity,
                  height: 500,
                  fit: BoxFit.fill,
                )),
          ),
          Text("点击图片返回")
        ],
      ),
    );
  }

HeroMode

有一些场景,比如单数的图片需要过渡动画,双数的不需要,那么如何处理呢,Flutter中也提供了解决办法就是HeroMode,给Hero嵌套HeroMode组件后可以通过设置enabled属性,当其值为false时,hero动画就会失效。

HeroMode(
  enabled: isNeedHero,
  child: Hero(
      tag: "tag",
      child: Image.network(
        imageUrl,
        width: 200,
        height: 200,
      )),
)

注意这里的tag一定要和第一个界面Hero中的tag一致

总的来说,这个效果实现起来非常简单的,但是用到实际项目中呈现出的用户体验会非常好!

代码地址