Flutter:英雄过渡+小部件动画同时进行?

2023-11-30

因此,我对 Flutter 的特定动画案例有一些疑问。

基本上,我想做的是同时运行用于路线更改的英雄过渡和相邻小部件上的自定义动画。

具体来说,我的根目录中有一个自定义的 InheritedWidget,它从 StatefulWidget 父级提供应用程序状态。我有一个 WidgetsApp 和一个用于自定义选项卡导航的相邻同级,嵌套在我的 InheritedWidget 中。这棵树看起来像这样:

Root Widget (Stateful)
        |
        |__InheritedWidget
                   |
                   |__WidgetsApp (Handles routing)
                   |
                   |__Navigation Bar (Overlay)

当我在 WidgetsApp 上执行使用 Hero 转换的路线更改时,出现了我的问题。当发生这种情况时,我还尝试根据用户所在的视图对导航栏进行动画显示或隐藏。但是,由于我在应用程序状态上使用 bool 变量来通过动画显示或隐藏导航栏,因此 SetState 调用会“覆盖”英雄过渡,因为树是在此过程中重建的(这就是我所要做的)思维)。

我最初的想法是 InheritedWidget 会捕获应用程序状态更改,并且仅通过 updateShouldNotify 重建导航栏,但可惜这不是我所看到的所需效果:(

那么 - 有没有人尝试过类似的事情,或者知道如何优雅地处理这个问题? :)


我已经做了类似的事情,但不幸的是我的代码还包含一堆其他的东西&这是相对复杂的事情,所以我必须把事情分开来制作一个例子,这比我现在能做的要多一些。我将解释我所做的一般概念。可能还有更好的方法来做到这一点。

您想要编写一个带有 State 的 StatefulWidget,该 State 也扩展了 NavigatorObserver(您也许可以使用无状态小部件,但我不这么认为)。我个人将其放在树中导航器的上方(即,它在其构建函数中构建导航器),但您很可能也将其放在导航器“旁边”。

重写 NavigatorObserver 中的 didPush、didRemove、didPop 等方法。在每一个中,调用 setState 并保存动画和其他参数,如下所示:

class NavigationFaderState extends State<NavigationFader> with NavigatorObserver {

  Animation _animation;
  // whatever else you need, maybe starting/finishing opacity or position etc.

  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    setState(() {
      _animation = route.animation;
    }
    route.animation.addStatusListener((status) {
      if (status = AnimationStatus.completed) {
        setState(() {
          _animation = null;
        });
      }
    });
  }

  ....
}

在您的构建函数中,您需要根据 _animation 是否存在以及您可能想要设置的任何其他参数来检查 _animation 和动画(即是否设置动画的标志,以及是否向前或向后可能会有所帮助 - 我相信“弹出”动画已经从 0 开始,到 1 为止,与推送动画相同,但我可能是错的)。 然后,您可以将此动画连接到您想要为导航栏设置动画的方式,可能使用 AnimatedBuilder 或直接连接动画等。如果对这一切如何运作有任何具体问题,请发表评论,我将添加一些评论等。

希望有帮助 =)

编辑:带有完整的代码示例。郑重声明,我并不认为这段代码就那么好,或者说这是您应该做的事情。但这是解决问题的一种方法。在真正的应用程序中使用它之前,值得对其进行测试,并可能添加一些断言来检查状态等。

导入“包:flutter/material.dart”;

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  PushListener listener = new PushListener();

  @override
  Widget build(BuildContext context) {
    return new WidgetsApp(
      locale: new Locale("en"),
      navigatorObservers: [listener],
      builder: (context, child) {
        // this is here rather than outside the WidgetsApp so that it
        // gets access to directionality, text styles, etc
        return new Scaffold(
          body: child,
          bottomNavigationBar:
              new ColorChangingNavigationBar(key: listener.navBarKey),
        );
      },
      onGenerateRoute: (settings) {
        switch (settings.name) {
          case '/':
            return new MaterialPageRoute(
              settings: settings,
              builder: (context) => Column(
                    children: <Widget>[
                      new Text(
                          "I have a green nav bar when you open me and blue when you come back"),
                      new RaisedButton(
                        onPressed: () {
                          Navigator.pushNamed(context, "/red");
                        },
                        child: new Text("Next"),
                      ),
                    ],
                  ),
            );
          case '/red':
            return new MaterialPageRoute(
              settings: settings,
              builder: (context) => Column(
                    children: <Widget>[
                      new Text("I have a red nav bar"),
                      new RaisedButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                      )
                    ],
                  ),
            );
        }
      },
      color: Colors.blue,
    );
  }
}

class PushListener extends NavigatorObserver {
  GlobalKey<ColorChangingNavigationBarState> navBarKey = new GlobalKey();

  @override
  void didPop(Route route, Route previousRoute) {
    if (route is ModalRoute && navBarKey.currentState != null) {
      var name = route.settings.name;
      var color = name == "/" ? Colors.red.shade500 : Colors.blue.shade500;
      var animation = new ReverseAnimation(route.animation);
      print("Popping & changing color to: ${name == "/" ? "red" : "blue"}");

      navBarKey.currentState.setAnimating(animation, color);
    }
  }

  @override
  void didPush(Route route, Route previousRoute) {
    if (route is ModalRoute && navBarKey.currentState != null) {
      var name = route.settings.name;
      var color = name == "/" ? Colors.blue.shade500 : Colors.red.shade500;
      print("Pushing & changing color to: ${name == "/" ? "red" : "blue"}");
      var animation = route.animation;
      navBarKey.currentState.setAnimating(animation, color);
    }
  }

  @override
  void didRemove(Route route, Route previousRoute) {
    // probably don't need
  }

  @override
  void didStartUserGesture() {
    // might want to do if gestures are supported with whichever type of
    // route you're using.
  }

  @override
  void didStopUserGesture() {
    // if you implement didStartUserGesture
  }
}

class ColorChangingNavigationBar extends StatefulWidget {
  final Color startColor;

  ColorChangingNavigationBar(
      {Key key, this.startColor = const Color.fromRGBO(0, 255, 0, 1.0)})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => new ColorChangingNavigationBarState();
}

class _ColorAnimationInfo {
  final Animation animation;
  final Tween<Color> colorTween;
  final AnimationStatusListener statusListener;

  _ColorAnimationInfo(this.animation, this.colorTween, this.statusListener);
}

class ColorChangingNavigationBarState
    extends State<ColorChangingNavigationBar> {
  @override
  void initState() {
    _toColor = widget.startColor;
    super.initState();
  }

  Color _toColor;
  _ColorAnimationInfo _colorAnimationInfo;

  void setAnimating(Animation animation, Color to) {
    var fromColor;
    if (_colorAnimationInfo != null) {
      fromColor = _colorAnimationInfo.colorTween
          .lerp(_colorAnimationInfo.animation.value);
      _colorAnimationInfo.animation
          .removeStatusListener(_colorAnimationInfo.statusListener);
    } else {
      fromColor = _toColor;
    }

    var statusListener = (state) {
      if (state == AnimationStatus.completed ||
          state == AnimationStatus.dismissed) {
        setState(() {
          _colorAnimationInfo = null;
        });
      }
    };

    animation.addStatusListener(statusListener);

    setState(() {
      _toColor = to;
      Tween<Color> colorTween = new ColorTween(begin: fromColor, end: to);

      _colorAnimationInfo =
          new _ColorAnimationInfo(animation, colorTween, statusListener);
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_colorAnimationInfo != null) {
      return new AnimatedBuilder(
          animation: _colorAnimationInfo.animation,
          builder: (context, child) {
            return new Container(
              color: _colorAnimationInfo.colorTween
                  .lerp(_colorAnimationInfo.animation.value),
              height: 30.0,
            );
          });
    } else {
      return new Container(
        color: _toColor,
        height: 30.0,
      );
    }
  }

  @override
  void dispose() {
    if (_colorAnimationInfo != null) {
      _colorAnimationInfo.animation.removeStatusListener(_colorAnimationInfo.statusListener);
    }
    _colorAnimationInfo = null;
    super.dispose();
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter:英雄过渡+小部件动画同时进行? 的相关文章

随机推荐

  • MySQL 左连接错误

    我有一些 SQL 曾经适用于较旧的 MySQL 版本 但升级到较新的 MySQL 5 版本后 出现错误 这是 SQL SELECT portfolio projects types FROM projects types LEFT JOIN
  • 获取预览camerax android中框内的所有文本

    i want to analyse all text that is just inside by box that i have in a preview camera But im getting wrong coordinates f
  • 将文件包含在数组中

    我有这个代码 routes array Home gt index php Contact gt contact php Register gt register php 我有这样的 example php 文件 Support gt su
  • Selenium Chromedriver 导致 Chrome 在没有配置插件、书签和其他设置的情况下启动

    我是 Selenium 的新用户 我想用它来启动 Chrome 浏览器 但遇到问题 public static void processor String url String name System setProperty webdriv
  • 安全的视频流

    我正在使用 jwplayer 我希望用户观看视频但不下载它们 我发现生成视频的哈希 URL 是可以做到的 我在创建哈希的服务器上使用lighttpd mod 并使用它创建视频的url timeout 从创建哈希后每 30 分钟过期 当我第二
  • 使用 jquery 删除不带

    我有一个定义列表 我需要删除所有 dt 没有标签的人 dd 在这种特殊情况下 Herramientas Suplementos Repuestos Herramientas 和 Antipincaduras 该列表可能会有很大差异 因为它取
  • 更改 PHP 中的时区

    好吧 快速提问 服务器正在东部时间运行 PHP程序需要使用中央时间进行日期计算 目前 我将这一行放在脚本的最顶部 putenv TZ US Central 这是最好的方法吗 还是有一些我不知道的 PHP 技巧 Cheers 您可以使用dat
  • Xcode 4异常断点过滤

    中断 Objective C 异常确实非常有用 并且是调试问题的最佳方法NSArray等等 然而 在实际编程时 异常也是一个很好用的东西 Xcode 提供了两种中断 Obj C 异常的选择 每当抛出异常时就中断 每当捕获异常时就中断 打破捕
  • 用于类延续的 Xcode 代码片段?

    我有一个用于创建属性的 Xcode 4 6 代码片段 一个用于弱属性 一个用于强属性 Typing propstrong在接口声明 h 文件 中工作得很好 但是当我在课堂延续中执行此操作时 没有向我提供代码片段 interface MyCl
  • 搜索和链接库目录的顺序

    我很难理解搜索目录以链接到库的顺序 我有一个CentOS6系统和3个版本的gcc 4 4 7 4 7 2 4 9 2 系统版本为4 4 7 版本4 7 2和4 9 2为模块 在 etc ld so conf d 有两个文件 gcc 4 7
  • 如何安装适用于 Python 2.7 的 PyQT4?

    我正在尝试在 Python 2 7 9 上安装 PyQT4 我在 Mac OS X 上 所以我尝试通过 Homebrew 和 Macports 安装它 不幸的是它们似乎都不起作用 这是我尝试过的 brew install python qt
  • 如何检查一个数组元素是否完全存在于php中的另一个数组中[重复]

    这个问题在这里已经有答案了 我有两个数组 例如 array1 1 2 3 4 5 6 7 8 9 array2 4 6 9 有没有什么函数可以让我确定array2完全存在于array1 我知道我可以使用in array 循环中的函数 但在我
  • 包含文件中的包含路径失败

    我在 PHP 包含路径方面遇到了一些麻烦 并且不明白 那里出了什么问题 首先 我想向您展示我的文件 目录结构 文件 目录结构 index php foo baz php bar inc php asdf qwerty inc php ind
  • 批处理脚本帮助 - 将 DelayedExpansion Var 的子字符串替换为另一个 DelayedExpansion Var

    基本上我正在尝试做 var1 SomeText var2 但这段代码不起作用 我缺少什么 在执行使用变量进行搜索和 或替换的搜索和替换操作时 扩展顺序至关重要 内部变量必须在外部搜索和替换扩展发生之前扩展 尝试对两者都使用延迟扩展显然是行不
  • 如何在我的本地 Geth 账户中获取一些以太币?

    我已经设置了 Geth 并创建了一些没有余额的帐户 所以我无法进行任何交易 因为它需要花费 Gas 费 如何创建具有一些初始余额的帐户 以便我可以测试我的合同 我使用以下命令创建了帐户 gt personal newAccount 假设您正
  • 确保 for 循环中的可观察对象在执行其他代码之前全部完成

    我有一段代码 如下所示 getPersons subscribe persons gt for const person of persons getAddress person id subscribe address gt person
  • Android 11 - 访问Android/data目录

    除了 root 之外 还有什么方法可以访问 Android 11 上的 SD 卡的 android data 目录吗 我的 非 Play 商店 应用程序需要访问另一个应用程序的公共 Android data com appname 文件夹
  • PHP 8.1.1 的 __toString() 方法问题 - 如果在声明类本身之前创建实例,则导致找不到类

    从 PHP 7 3 升级到 8 1 1 后遇到问题 当然还有很多事情要做 但这有点奇怪 这个例子对我不起作用 出现错误致命错误 未捕获错误 找不到类 TestC C xampp81 htdocs helpdesk811 test81 ind
  • 修复了滚动项目时导航抽屉中的导航标题

    当前状态 具有 NavigationHeader 和 NavigationMenu 项的 NavigationDrawer 这些项目数量很大 因此需要滚动才能访问底部的项目 要求 向下滚动到底部时 导航标题应保持固定 这是我的 Activi
  • Flutter:英雄过渡+小部件动画同时进行?

    因此 我对 Flutter 的特定动画案例有一些疑问 基本上 我想做的是同时运行用于路线更改的英雄过渡和相邻小部件上的自定义动画 具体来说 我的根目录中有一个自定义的 InheritedWidget 它从 StatefulWidget 父级