简体   繁体   中英

Hero Animation Not Working in Flutter

So, I'm trying to make a home delivery app for a restaurant in flutter and I can't seem to get my hero animation working. First I made a splash screen where the logo shows up and then it navigates to home page where the logo is supposed to do a hero transition. The splash screen and the home page are in two separate dart files. Here's the code for my splash screen:

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
            )
          )
        )
      )
    );
  }
}

And here's the code for the home page:

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
                )
              )
            )
          ],
        )
      ),
    );
  }
}

Checked your code so the hero animation is working but its happening to fast because of the transition duration is only 300 milliseconds.

To achieve the below result you can make a Custom MaterialPageRoute .

Before

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

After

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

Also, you can play with the 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,
    );
  }
}

Push new Route

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

You can use Custom MaterialPageRoute for the splash screen and for other routes MaterialPageRoute.

Hope it helps

// 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();
          },
        ),
      ),
    );
  }
}

Hope this helps.

Hero animation is quite easy to implement in Flutter. You just need to import the package package:flutter/scheduler.dart in your landing page. See below code

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

Remember to add timeDilation variable and assign it a value to speed up or slow down the animation in seconds. Add these just after your build method as in example below

Widget build(BuildContext context) {

    timeDilation = 2;

For better animation, just use a custom animation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM