禁用 Flutter Hero 反向动画

Posted

技术标签:

【中文标题】禁用 Flutter Hero 反向动画【英文标题】:Disabling Flutter Hero reverse animation 【发布时间】:2019-04-04 22:44:34 【问题描述】:

给定 2 条路线,例如parent 和 child 以及具有相同标签的 Hero(..) 小部件。 当用户在“父”屏幕上并打开“子”时 - Hero 小部件是动画的。当它返回时(通过Navigator.pop),它也是动画的。

我正在寻找一种在返回时禁用该动画的方法(通过 Navigator.pop 从子级到父级)。

是否有一种处理程序可以在小部件“动画化”之前在小部件上调用?然后我可能可以更改Hero 标签并解决问题。

或者,当在父窗口小部件中为路由创建“构建器”时,我可能会记得对目标窗口小部件的引用,然后在调用 Navigator.pop 之前通知它“你将被动画化”。这还需要使该小部件有状态(我还没有找到强制重建无状态小部件的方法)。

有没有更简单的实现方法?

【问题讨论】:

【参考方案1】:

虽然目前没有内置方法可以在任何特定方向禁用英雄动画,但 CLucera 使用 FadeTransitionHeroFlightDirection 是一种创造性的方式,最直接的方法是打破 tag两个英雄之间的关联:

当你从 2 号英雄回到 1 号英雄时,只是暂时将 1 号英雄的标签更改为其他东西,然后英雄不会动画回来。一个简化的例子:

class _MyHomePageState extends State<MyHomePage> 

  String tag1, tag2;
  String sharedTag = 'test';
  String breakTag = 'notTest';

  @override
  void initState() 
    super.initState();
    tag1 = sharedTag;
    tag2 = sharedTag;
  

  @override
  Widget build(BuildContext context) 

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Hero(
            tag: tag1,
            child: RaisedButton(
              child: Text("hi"),
              onPressed: () 
                // restore the tag
                if (tag1 != sharedTag) 
                  setState(() 
                    tag1 = sharedTag;
                  );
                

                // second route
                Navigator.of(context).push(
                    MaterialPageRoute<void>(
                        builder: (BuildContext context) 
                          return Scaffold(
                            appBar: AppBar(
                              title: Text(widget.title),
                            ),
                            body: Container(
                                alignment: Alignment.topLeft,
                                child: Hero(
                                  tag: tag2,
                                  child: RaisedButton(
                                    child: Text('hello'),
                                    onPressed: () 
                                      // change the tag to disable the reverse anim
                                      setState(() 
                                        tag1 = breakTag;
                                      );

                                      Navigator.of(context).pop();
                                    ,
                                  ),
                                )
                            ),
                          );
                        
                    )
                );
              ,
            )
        ),
      ),
    );
  

但是如果你想直接修改动画,那么在flightShuttleBuilder 里面玩是像 CLucera 那样做的方法。您还可以查看medium/mastering-hero-animations-in-flutter 以进一步探索该区域。

【讨论】:

【参考方案2】:

目前我能想到的唯一方法是以一种似乎没有动画的方式“动画”弹出的英雄,让我们检查一下这段代码:

class SecondRoute extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
        body: Hero(
          flightShuttleBuilder: (context, anim, direction, fromContext, toContext) 
            final Hero toHero = toContext.widget;
            if (direction == HeroFlightDirection.pop) 
              return FadeTransition(
                opacity: AlwaysStoppedAnimation(0),
                child: toHero.child,
              );
             else 
              return toHero.child;
            
          ,
          child: FlatButton(
            child: Text("prev 1"),
            onPressed: () 
              Navigator.of(context).pop();
            ,
          ),
          tag: "test",
        ));
  

在您的 SecondRoute(应该弹出的那个)中,您必须向您的 Hero 提供一个 flightShuttleBuilder 参数,然后您可以检查方向,如果它正在弹出,只需使用 AlwaysStoppedAnimation 淡入淡出过渡隐藏小部件

结果是这样的:

我希望这和预期的结果一样,当然你可以完全改变flightShuttleBuilder里面的transition来改变效果!这取决于你:)

【讨论】:

以上是关于禁用 Flutter Hero 反向动画的主要内容,如果未能解决你的问题,请参考以下文章

Flutter动画

Flutter:在 ListView Builder 中使用 Hero 动画

列表视图项之间的 Flutter Hero 类似动画

Flutter - 我可以用 Hero 包装每个小部件来为它们设置动画吗

如何在 Flutter 中使用 Hero 转换期间未运行的动画/动画?

如何在 Flutter 中将 Hero 动画添加到我的 ListView?