【问题标题】:Hero Animation Not Working in Flutter英雄动画在 Flutter 中不起作用
【发布时间】:2018-11-01 18:31:22
【问题描述】:

所以,我正在尝试为一家餐厅制作送货上门应用程序,但我似乎无法让我的英雄动画正常工作。首先,我制作了一个启动屏幕,其中显示了徽标,然后导航到徽标应该进行英雄转换的主页。初始屏幕和主页位于两个单独的 dart 文件中。这是我的初始屏幕的代码:

import 'package:flutter/material.dart';
import 'home_page.dart';
import 'dart:async';

class Splash extends StatefulWidget {
  @override
  _SplashState createState() => new _SplashState();
}

class _SplashState extends State<Splash> with SingleTickerProviderStateMixin 
{

  Animation<double> _mainLogoAnimation;
  AnimationController _mainLogoAnimationController;

  @override
  void initState() {
    super.initState();
    goToHomePage();
    _mainLogoAnimationController = new AnimationController(duration: new Duration(milliseconds: 2500) ,vsync: this);
    _mainLogoAnimation = new CurvedAnimation(parent: 
    _mainLogoAnimationController, curve: Curves.easeIn);
    _mainLogoAnimation.addListener(() => (this.setState(() {})));
    _mainLogoAnimationController.forward();
  }

  Future goToHomePage() async {
    await new Future.delayed(const Duration(milliseconds: 4000));
    Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new HomePage()));
  }

  @override
  Widget build(BuildContext context) {
    return new Material(
      color: Colors.black,
      child: new Center(
        child: new Opacity(
          opacity: 1.0 * _mainLogoAnimation.value,
          child: new Hero(
            tag: 'tbh_logo',
            child: new Image(
              image: new AssetImage('assets/images/tbh_main_logo.png'),
              width: 300.0
            )
          )
        )
      )
    );
  }
}

这是主页的代码:

import 'package:flutter/material.dart';
import '../ui/drawer.dart';
import 'splash.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => new _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("The Barni House"),
        backgroundColor: Colors.black,
      ),
      drawer: new Drawer(child: MyDrawer()),
      body: new Center(
        child: Column(
          children: <Widget>[
            new Container(
              child: new Hero(
                tag: 'tbh_logo',
                child: new Image(
                  image: new AssetImage('assets/images/tbh_main_logo.png'),
                  width: 300.0
                )
              )
            )
          ],
        )
      ),
    );
  }
}

【问题讨论】:

    标签: animation dart flutter


    【解决方案1】:

    Hero 动画在 Flutter 中很容易实现。您只需在登录页面中导入包 package:flutter/scheduler.dart。见下面代码

    import 'package:flutter/scheduler.dart' show timeDilation;
    

    记得添加 timeDilation 变量并为其分配一个值,以秒为单位加速或减慢动画。在您的构建方法之后添加这些,如下例所示

    Widget build(BuildContext context) {
    
        timeDilation = 2;
    

    为了更好的动画,只需使用自定义动画

    【讨论】:

      【解决方案2】:

      检查了您的代码,因此英雄动画正在运行,但由于过渡持续时间仅为 300 毫秒,因此它发生得很快。

      要实现以下结果,您可以制作 Custom MaterialPageRoute

      之前

      @override
        Duration get transitionDuration => const Duration(milliseconds: 300);
      

      之后

      @override
        Duration get transitionDuration => const Duration(milliseconds: 1000);
      

      此外,您还可以使用 CurvedAnimation

      new CurvedAnimation(
                      parent: routeAnimation,
                      curve: Curves.elasticIn,
                    )
      

      CustomRoute.dart

      import 'package:flutter/cupertino.dart';
      import 'package:flutter/foundation.dart';
      import 'package:flutter/gestures.dart';
      import 'package:flutter/material.dart';
      import 'package:flutter/rendering.dart';
      import 'package:flutter/widgets.dart';
      
      final Tween<Offset> _kBottomUpTween = new Tween<Offset>(
        begin: const Offset(0.0, 1.0),
        end: Offset.zero,
      );
      
      // Offset from offscreen to the right to fully on screen.
      final Tween<Offset> _kRightMiddleTween = new Tween<Offset>(
        begin: const Offset(1.0, 0.0),
        end: Offset.zero,
      );
      
      // Offset from offscreen below to fully on screen.
      class AppPageRoute extends MaterialPageRoute<String> {
        @override
        final bool maintainState;
      
        @override
        final WidgetBuilder builder;
        CupertinoPageRoute<String> _internalCupertinoPageRoute;
      
        AppPageRoute({
          @required this.builder,
          RouteSettings settings: const RouteSettings(),
          this.maintainState: true,
          bool fullscreenDialog: false,
        })  : assert(builder != null),
              assert(settings != null),
              assert(maintainState != null),
              assert(fullscreenDialog != null),
              super(
                settings: settings,
                fullscreenDialog: fullscreenDialog,
                builder: builder,
              ) {
          assert(opaque); // PageRoute makes it return true.
        }
      
        @override
        Color get barrierColor => null;
      
        @override
        Duration get transitionDuration => const Duration(milliseconds: 1000);
      
        CupertinoPageRoute<String> get _cupertinoPageRoute {
          assert(_useCupertinoTransitions);
          _internalCupertinoPageRoute ??= new CupertinoPageRoute<String>(
            builder: builder,
            fullscreenDialog: fullscreenDialog,
            hostRoute: this,
          );
          return _internalCupertinoPageRoute;
        }
      
        bool get _useCupertinoTransitions {
          return _internalCupertinoPageRoute?.popGestureInProgress == true ||
              Theme.of(navigator.context).platform == TargetPlatform.iOS;
        }
      
        @override
        Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
          final Widget result = builder(context);
          assert(() {
            if (result == null) {
              throw new FlutterError('The builder for route "${settings.name}" returned null.\n'
                  'Route builders must never return null.');
            }
            return true;
          }());
          return result;
        }
      
        @override
        Widget buildTransitions(
            BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
          if (_useCupertinoTransitions) {
            return _cupertinoPageRoute.buildTransitions(context, animation, secondaryAnimation, child);
          }
      
          return new _CustomPageTransition(routeAnimation: animation, child: child, fullscreenDialog: fullscreenDialog);
        }
      }
      
      class _CustomPageTransition extends StatelessWidget {
        final Animation<Offset> _positionAnimation;
      
        final Widget child;
        final bool fullscreenDialog;
      
        _CustomPageTransition({
          Key key,
          @required Animation<double> routeAnimation,
          @required this.child,
          @required this.fullscreenDialog,
        })  : _positionAnimation = !fullscreenDialog
                  ? _kRightMiddleTween.animate(new CurvedAnimation(
                      parent: routeAnimation,
                      curve: Curves.elasticIn,
                    ))
                  : _kBottomUpTween.animate(new CurvedAnimation(
                      parent: routeAnimation, // The route's linear 0.0 - 1.0 animation.
                      curve: Curves.elasticIn,
                    )),
              super(key: key);
      
        @override
        Widget build(BuildContext context) {
          return new SlideTransition(
            position: _positionAnimation,
            child: child,
          );
        }
      }
      

      推新路线

       Future goToHomePage() async {
          await new Future.delayed(const Duration(milliseconds: 4000));
          Navigator.of(context).push(new AppPageRoute(builder: (BuildContext context) => new HomePage()));
        }
      

      您可以将自定义 MaterialPageRoute 用于启动屏幕和其他路由 MaterialPageRoute。

      希望对你有帮助

      【讨论】:

      • 谢谢!从未真正考虑过制作自定义页面路由。 这对我帮助很大! :)
      【解决方案3】:
      // main.dart class
      import 'package:flutter/material.dart';
      import 'package:flutter/scheduler.dart' show timeDilation;
      import 'package:flutter_test7/photo_hero.dart';
      import 'package:flutter_test7/second_page.dart';
      
      class HeroAnimation extends StatelessWidget {
        Widget build(BuildContext context) {
          timeDilation = 2.5; // 1.0 means normal animation speed.
      
          return new Scaffold(
            appBar: new AppBar(
              title: const Text('Basic Hero Animation'),
            ),
            body: new Center(
              child: new PhotoHero(
                photo: 'images/flippers-alpha.png',
                width: 300.0,
                onTap: () {
                  Navigator.of(context).pushNamed('/second_page');
                },
              ),
            ),
          );
        }
      }
      
      void main() {
        runApp(
          new MaterialApp(
            home: new HeroAnimation(),
            routes: <String, WidgetBuilder>{
              '/second_page': (context) => new SecondPage()
            },
          ),
        );
      }
      
      // photo_hero.dart class
      import 'package:flutter/material.dart';
      
      class PhotoHero extends StatelessWidget {
        const PhotoHero({Key key, this.photo, this.onTap, this.width})
            : super(key: key);
      
        final String photo;
        final VoidCallback onTap;
        final double width;
      
        Widget build(BuildContext context) {
          return new SizedBox(
            width: width,
            child: new Hero(
              tag: photo,
              child: new Material(
                color: Colors.transparent,
                child: new InkWell(
                  onTap: onTap,
                  child: new Image.asset(
                    photo,
                    fit: BoxFit.contain,
                  ),
                ),
              ),
            ),
          );
        }
      }
      
      // second_page.dart class
      import 'package:flutter/material.dart';
      import 'package:flutter_test7/photo_hero.dart';
      
      class SecondPage extends StatefulWidget {
        @override
        _SecondPageState createState() => new _SecondPageState();
      }
      
      class _SecondPageState extends State<SecondPage> {
        @override
        Widget build(BuildContext context) {
          return new Scaffold(
            appBar: new AppBar(
              title: const Text('Flippers Page'),
            ),
            body: new Container(
              color: Colors.lightBlueAccent,
              padding: const EdgeInsets.all(16.0),
              alignment: Alignment.topLeft,
              child: new PhotoHero(
                photo: 'images/flippers-alpha.png',
                width: 100.0,
                onTap: () {
                  Navigator.of(context).pop();
                },
              ),
            ),
          );
        }
      }
      

      希望这会有所帮助。

      【讨论】:

      • 时间膨胀是一个不错且简单的想法。酷:)
      • 英雄在使用Navigator.push(context, MaterialPageRoute(builder: (context) =&gt; SeondPage()));时有效,但在使用PageRouteBuilder时无效
      猜你喜欢
      • 1970-01-01
      • 2023-03-03
      • 2021-11-13
      • 1970-01-01
      • 2020-05-15
      • 1970-01-01
      • 2021-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多