在向侦听器通知AnimationController错误

时间:2018-12-10 12:57:33

标签: animation dart flutter flutter-animation

我正在尝试在Flutter中创建一个登录页面,并希望在进入下一页之前进行动画处理。一切正常,除非这样做(登录时出现错误信息,所以它调用“ failedLoginAnimation”函数,当我在表单中输入正确的信息后)开始动画,我的应用崩溃并显示给我,但是当我进入正确的信息,而在一切正常之前您没有犯错。 PS:我是一个新手,大约要动一个月的动画,甚至更多。

    ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (11398): The following assertion was thrown while notifying listeners for AnimationController:
I/flutter (11398): Looking up a deactivated widget's ancestor is unsafe.
I/flutter (11398): At this point the state of the widget's element tree is no longer stable. To safely refer to a
I/flutter (11398): widget's ancestor in its dispose() method, save a reference to the ancestor by calling
I/flutter (11398): inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
I/flutter (11398): 
I/flutter (11398): When the exception was thrown, this was the stack:
I/flutter (11398): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3232:9)
I/flutter (11398): #1      Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3241:6)
I/flutter (11398): #2      Element.ancestorStateOfType (package:flutter/src/widgets/framework.dart:3289:12)
I/flutter (11398): #3      Navigator.of (package:flutter/src/widgets/navigator.dart:1271:19)
I/flutter (11398): #4      Navigator.popAndPushNamed (package:flutter/src/widgets/navigator.dart:825:22)
I/flutter (11398): #5      StaggerAnimationSignIn.build.<anonymous closure> (package:test/premium/pages/login/loginAnimation.dart:146:25)
I/flutter (11398): #6      _AnimationController&Animation&AnimationEagerListenerMixin&AnimationLocalListenersMixin.notifyListeners (package:flutter/src/animation/listener_helpers.dart:124:19)
I/flutter (11398): #7      AnimationController._tick (package:flutter/src/animation/animation_controller.dart:693:5)
I/flutter (11398): #8      Ticker._tick (package:flutter/src/scheduler/ticker.dart:228:5)
I/flutter (11398): #9      _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter (11398): #10     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleBeginFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:906:11)
I/flutter (11398): #11     __InternalLinkedHashMap&_HashVMBase&MapMixin&_LinkedHashMapMixin.forEach (dart:collection/runtime/libcompact_hash.dart:370:8)
I/flutter (11398): #12     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleBeginFrame (package:flutter/src/scheduler/binding.dart:904:17)
I/flutter (11398): #13     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleBeginFrame (package:flutter/src/scheduler/binding.dart:834:5)
I/flutter (11398): #14     _invoke1 (dart:ui/hooks.dart:159:13)
I/flutter (11398): #15     _beginFrame (dart:ui/hooks.dart:129:3)
I/flutter (11398): 
I/flutter (11398): The AnimationController notifying listeners was:
I/flutter (11398):   AnimationController#97d39(⏭ 1.000; paused)
I/flutter (11398): ════════════════════════════════════════════════════════════════════════════════════════════════════

我的登录页面

import 'package:flutter/material.dart';
import 'loginAnimation.dart';
import 'freeAccessAnimation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/animation.dart';
import 'package:test/premium/tools/jsonTools.dart';
import 'dart:async';
import 'package:test/premium/tools/design/color/designColors.dart';
import './Components/PremiumLink.dart';
import './Components/ForgotPasswordLink.dart';
import './Components/Logo.dart';
import './Components/SignInButton.dart';
import './Components/FreeAccessButton.dart';
import 'package:flutter/services.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
import 'package:test/premium/tools/internet/checkInternet.dart';
import 'package:test/premium/pages/tools/alerts.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:test/premium/variables/variablesApp.dart';
import 'package:test/premium/tools/internet/sendHTTPRequest.dart';


class LoginScreen extends StatefulWidget {
  const LoginScreen({Key key}) : super(key: key);
  @override
  LoginScreenState createState() => new LoginScreenState();
}


class LoginScreenState extends State<LoginScreen>
    with TickerProviderStateMixin {
  static bool hasFinishedLogin;
  static bool hasFailedLogin;

  static String userLoginToken ;
  static String tokenFileExist ;
  static String backToLogin;
  static bool isFirstCallAction;
  static bool hasFinishLogAnim;

  AnimationController _loginButtonController;
  AnimationController _freeButtonController;

  static final usernameController = TextEditingController();
  static final passwordController = TextEditingController();

  static var animationStatusLogin = 0;
  static var animationStatusFree = 0;

  Future pause(Duration d) => new Future.delayed(d);

  _launchURL(String url) async {
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }

  void tokensManagement(){
    if(userLoginToken == null && tokenFileExist == null && backToLogin == "true") {
      JTools.deleteFileToken().then((e){
        if(e == "Token has been deleted"){
          setState(() {
            tokenFileExist = 'false';
            userLoginToken = null;
            backToLogin = 'false';
          });

        }
      });

    }else{
      JTools.readDataToken().then((e){
        String _token = e;
        pause(new Duration(milliseconds: 500)).then((e){
          JTools.tokenFileExist().then((value){
            tokenFileExist = value.toString();
            userLoginToken = _token;
          }).then((b){
            if(_token != null){
              if(userLoginToken.isNotEmpty && tokenFileExist == 'true'){
                //TODO: on loading
                APIToken.token = _token;
                CheckInternet().checkInternet().then((e){
                  if(e == true){
                        PostHTTP().sendRequestAPI(APIToken.token, 'membership').then((value){
                          if(value['status'] == "200"){
                            String membership = value['membership'];
                            print(membership);
                            if(membership == "138" || membership == "136"){
                              print('User is premium');
                              Navigator.of(context).pushNamed('/main');
                            }else {
                              print("User isn't premium ");
                              JTools.deleteFileToken().then((e) {
                                if (e == "Token has been deleted") {
                                  setState(() {
                                    tokenFileExist = 'false';
                                    userLoginToken = null;
                                  });
                                  pause(Duration(milliseconds: 1000)).then((e){
                                    Alerts(context: context).alertNotPremium();
                                  });
                                }
                              });
                            }
                          }else{
                            if(value['status'] == "404"){
                                setState(() {
                                  tokenFileExist = 'false';
                                  userLoginToken = null;
                                  backToLogin = 'false';
                                });
                                pause(Duration(milliseconds: 1000)).then((e){
                                  Alerts(context: context).alertErrorConnectionServer();
                                });

                            }
                            if(value['status'] == "401" || value['status'] == "403")
                            {print("User isn't premium ");
                            JTools.deleteFileToken().then((e) {
                              if (e == "Token has been deleted") {
                                setState(() {
                                  tokenFileExist = 'false';
                                  userLoginToken = null;
                                  backToLogin = 'false';
                                });
                                pause(Duration(milliseconds: 1000)).then((e){
                                  Alerts(context: context).alertNotPremium();
                                });
                              }
                            });}
                          }
                        });
                  }else{
                    Alerts(context: context).alertInternet();
                    failedLoginAnimation();
                  }
                });
              }
              else{
                setState(() {

                });
              }
            }else{
              setState(() {

              });
            }
          });
        });
      });
    }
  }

  @override
  void initState() {
    super.initState();
    _loginButtonController = new AnimationController(
        duration: new Duration(milliseconds: 3000), vsync: this);
    _freeButtonController = new AnimationController(
        duration: new Duration(milliseconds: 3000), vsync: this);
    tokensManagement();
    animationStatusLogin = 0;
    animationStatusFree = 0;
    print('init LOGIN');
  }

  @override
  void dispose() {
    super.dispose();
    _loginButtonController.dispose();
    _freeButtonController.dispose();
  }

  Future<Null> _playAnimationLogin() async {
    try {
      await _loginButtonController.forward();
    } on TickerCanceled {}
  }

  Future<Null> _playAnimationFree() async {
    try {
      await _freeButtonController.forward();
      await _freeButtonController.reverse();
    } on TickerCanceled {}
  }

  Widget formLogin(){
    return new Container(
      margin: new EdgeInsets.symmetric(horizontal: 20.0),
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          new Form(
              child: new Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: <Widget>[
                  new Container(
                    decoration: new BoxDecoration(
                      border: new Border(
                        bottom: new BorderSide(
                          width: 0.5,
                          color: DesignColors.animatedLoginColor,
                        ),
                      ),
                    ),
                    child: new TextFormField(
                      controller: usernameController,
                      keyboardType: TextInputType.emailAddress,
                      style: const TextStyle(
                        color: Colors.white,
                      ),
                      decoration: new InputDecoration(
                        icon: new Icon(
                          Icons.person_outline,
                          color: Colors.white,
                        ),
                        border: InputBorder.none,
                        hintText: "Email/Username",
                        hintStyle: const TextStyle(color: Colors.white, fontSize: 15.0),
                        contentPadding: const EdgeInsets.only(
                            top: 30.0, right: 30.0, bottom: 30.0, left: 5.0),
                      ),
                    ),
                  ),
                  new Container(
                    decoration: new BoxDecoration(
                      border: new Border(
                        bottom: new BorderSide(
                          width: 0.5,
                          color: DesignColors.animatedLoginColor,
                        ),
                      ),
                    ),
                    child: new TextFormField(
                      controller: passwordController,
                      obscureText: true,
                      style: const TextStyle(
                        color: Colors.white,
                      ),
                      decoration: new InputDecoration(
                        icon: new Icon(
                          Icons.lock_outline,
                          color: Colors.white,
                        ),
                        border: InputBorder.none,
                        hintText: "Password",
                        hintStyle: const TextStyle(color: Colors.white, fontSize: 15.0),
                        contentPadding: const EdgeInsets.only(
                            top: 30.0, right: 30.0, bottom: 30.0, left: 5.0),
                      ),
                    ),
                  ),
                ],
              )),
        ],
      ));
  }

  void failedLoginAnimation(){
    setState(() {
      hasFailedLogin = true;
      _playAnimationLogin();
      animationStatusLogin = 0;
    });
  }

  Widget logPage(){
    return new WillPopScope(
        onWillPop: () async => false,
        child: new Scaffold(
            backgroundColor: DesignColors.backgroundColor,
            body: Center(
              child: Theme(
                data: ThemeData(splashColor: Colors.transparent, textSelectionHandleColor: Colors.blue, highlightColor: Colors.transparent),
                child:new ListView(
                  shrinkWrap: true,
                  children: <Widget>[
                    new Stack(
                      alignment: AlignmentDirectional.bottomCenter,
                      children: <Widget>[
                        new Column(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: <Widget>[
                            new Logo(image: DecorationImage(image: ExactAssetImage('assets/images/AppLogo.png'), fit: BoxFit.cover),),
                            formLogin(),
                            new PremiumLink(action: (){_launchURL("someurl");}),
                            new ForgotPasswordLink(action: (){_launchURL("someurl");}),
                          ],
                        ),
                        animationStatusLogin == 0
                            ? new Padding(
                          padding: const EdgeInsets.only(bottom: 160.0),
                          child: new InkWell(
                              onTap: () {
                                setState(() {
                                  animationStatusLogin = 1;
                                  hasFinishedLogin = false;
                                  hasFinishLogAnim = false;
                                  hasFailedLogin = false;
                                  isFirstCallAction = true;
                                });
                                _playAnimationLogin();
                              },
                              child: new SignIn()),
                        )
                            : new StaggerAnimationSignIn( action: (){
                          CheckInternet().checkInternet().then((e){
                            if(e == true){
                              PostHTTP().sendLogin({'username': usernameController.text, 'password': passwordController.text}).then((e){
                                String responseStatus = e['status'];
                                if(responseStatus != "200"){
                                  Alerts(context: context).alertFalseInfosLogin();
                                  failedLoginAnimation();
                                }else{
                                  String token = e['token'];
                                  PostHTTP().sendRequestAPI(token, 'membership').then((value){
                                    if(value['status'] == "200"){
                                      String membership = value['membership'];
                                      print(membership);
                                      if(membership == "138" || membership == "136"){
                                        APIToken.token = token;
                                        JTools.saveLogin(token);
                                        print('User is premium ');
                                        FocusScope.of(context).requestFocus(new FocusNode());
                                        hasFinishedLogin = true;
                                        hasFinishLogAnim = false;
                                        hasFailedLogin = false;
                                        _playAnimationLogin();
                                      }else{
                                        print("User isn't premium ");
                                        Alerts(context: context).alertNotPremium();
                                        failedLoginAnimation();
                                      }
                                    }else{
                                      if(value['status'] == "404"){Alerts(context: context).alertErrorConnectionServer(); failedLoginAnimation();}
                                      if(value['status'] == "401" || value['status'] == "403"){Alerts(context: context).alertNotPremium(); failedLoginAnimation();}
                                    }
                                  });
                                }
                              });
                            }else{
                              Alerts(context: context).alertInternet();
                              failedLoginAnimation();
                            }
                          });


                          //Navigator.of(context).popAndPushNamed('/main');
                        },
                            buttonController:
                            _loginButtonController.view
                        ),

                        animationStatusFree == 0
                            ? new Padding(
                          padding: const EdgeInsets.only(bottom: 85.0),
                          child: new InkWell(
                              onTap: () {
                                setState(() {
                                  animationStatusFree = 1;
                                });
                                _playAnimationFree();
                              },
                              child: new FreeAccess()),
                        )
                            : new StaggerAnimationFree(
                            buttonController:
                            _freeButtonController.view),
                      ],
                    ),
                  ],
                ),
              ),
            )
        )
    );
  }

  Widget waitingPage(){
    return WillPopScope(child: Scaffold(backgroundColor: DesignColors.backgroundColor.withOpacity(1), body: Center(child: new CircularProgressIndicator(
      value: null,
      strokeWidth: 1.0,
      valueColor: new AlwaysStoppedAnimation<Color>(
          DesignColors.robot1Theme),
    )),), onWillPop:() async => false);
  }

  @override
  Widget build(BuildContext context) {
    timeDilation = 0.4;
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);


    if(userLoginToken == null && tokenFileExist == 'false'){
      return logPage();
    }

    if(userLoginToken == null && tokenFileExist == null && backToLogin == "true"){
      //TODO: on loading
      backToLogin ="false";
      tokensManagement();
      return logPage();
    }else if(userLoginToken == null && tokenFileExist == null){
      //TODO: on loading
      return waitingPage();
    }

    if(userLoginToken.isNotEmpty && tokenFileExist == null){
      //TODO: on loading
      return waitingPage();
    }


    if(userLoginToken.isEmpty && tokenFileExist == 'true'){
      return logPage();
    }

    return logPage();
  }
}

我的动画课

import 'package:flutter/material.dart';
import 'package:test/premium/tools/design/color/designColors.dart';
import 'login.dart';

class StaggerAnimationSignIn extends StatelessWidget {
  final action;
  StaggerAnimationSignIn({Key key, this.buttonController, @required this.action})
      : buttonSqueezeanimation = new Tween(
          begin: 320.0,
          end: 70.0,
        )
            .animate(
          new CurvedAnimation(
            parent: buttonController,
            curve: new Interval(
              0.0,
              0.175,
            ),
          ),
        ),
        buttomZoomOut = new Tween(
          begin: 70.0,
          end: 5000.0,
        )
            .animate(
          new CurvedAnimation(
            parent: buttonController,
            curve: new Interval(
              0.55,
              0.999,
              curve: Curves.bounceOut,
            ),
          ),
        ),
        containerCircleAnimation = new EdgeInsetsTween(
          begin: const EdgeInsets.only(bottom: 160.0),
          end: const EdgeInsets.only(bottom: 0.0),
        )
            .animate(
          new CurvedAnimation(
            parent: buttonController,
            curve: new Interval(
              0.500,
              0.800,
              curve: Curves.ease,
            ),
          ),
        ),
        super(key: key);

  final AnimationController buttonController;
  final Animation<EdgeInsets> containerCircleAnimation;
  final Animation buttonSqueezeanimation;
  final Animation buttomZoomOut;

  Widget _buildAnimation(BuildContext context, Widget child) {
    return new Padding(
      padding: buttomZoomOut.value == 80
          ? const EdgeInsets.only(bottom: 160.0)
          : containerCircleAnimation.value,
      child: new InkWell(
          onTap: () {

          },
          child: new Hero(
            tag: "fadeLogin",
            child: buttomZoomOut.value <= 300
                ? new Container(
                    width: buttomZoomOut.value == 70
                        ? buttonSqueezeanimation.value
                        : buttomZoomOut.value,
                    height:
                        buttomZoomOut.value == 70 ? 60.0 : buttomZoomOut.value,
                    alignment: FractionalOffset.center,
                    decoration: new BoxDecoration(
                      color: DesignColors.animatedLoginColor,
                      borderRadius: buttomZoomOut.value < 400
                          ? new BorderRadius.all(const Radius.circular(30.0))
                          : new BorderRadius.all(const Radius.circular(0.0)),
                    ),
                    child: buttonSqueezeanimation.value > 75.0
                        ? new Text(
                            "Sign In",
                            style: new TextStyle(
                              color: Colors.white,
                              fontSize: 20.0,
                              fontWeight: FontWeight.w300,
                              letterSpacing: 0.3,
                            ),
                          )
                        : buttomZoomOut.value < 300.0
                            ? new CircularProgressIndicator(
                                value: null,
                                strokeWidth: 1.0,
                                valueColor: new AlwaysStoppedAnimation<Color>(
                                    Colors.white),
                              )
                            : null)
                : new Container(
                    width: buttomZoomOut.value,
                    height: buttomZoomOut.value,
                    decoration: new BoxDecoration(
                      shape: buttomZoomOut.value < 500
                          ? BoxShape.circle
                          : BoxShape.rectangle,
                      color: DesignColors.animatedLoginColor,
                    ),
                  ),
          )),
    );
  }


  @override
  Widget build(BuildContext context) {
      if(LoginScreenState.isFirstCallAction){
        buttonController.addListener(() {
            if (LoginScreenState.isFirstCallAction) {
              print("1");
              LoginScreenState.isFirstCallAction = false;
              action();
            }

            if (LoginScreenState.hasFailedLogin) {
              print("3");
              LoginScreenState.hasFailedLogin = false;
              buttonController.reverse();
              LoginScreenState.animationStatusLogin = 0;
            }

            if (buttonController.value > 0.2 &&
                LoginScreenState.hasFinishedLogin == false &&
                LoginScreenState.hasFailedLogin == false &&
                LoginScreenState.hasFinishLogAnim == false) {
              print("2");
              buttonController.stop();
            }

            if (LoginScreenState.hasFinishedLogin && !LoginScreenState.hasFailedLogin && !LoginScreenState.hasFinishLogAnim && buttonController.value == 1.0) {
              print("4");
              LoginScreenState.hasFinishedLogin = false;
              LoginScreenState.hasFailedLogin = false;
              LoginScreenState.hasFinishLogAnim = true;
              LoginScreenState.animationStatusLogin = 0;
              Navigator.popAndPushNamed(context, "/main");
              return;
          }
        });

    }
    return new AnimatedBuilder(
      builder: _buildAnimation,
      animation: buttonController,
    );
  }
}

更新

HI解决了我认为的问题,我仍然需要做一些测试,但是似乎在AnimationClass中调用Navigator.popAndPushNamed(context, "/main");并不是一个好主意,我只是以一种动画结束的方式更改了Login类我在登录课程中致电Navigator.popAndPushNamed(context, "/main");。我将在未来几天内继续更新此帖子。

我的构建登录类的新起点

      @override
  Widget build(BuildContext context) {
    _loginButtonController.addListener((){
      if(_loginButtonController.isCompleted){
        FocusScope.of(context).requestFocus(new FocusNode());
        Navigator.popAndPushNamed(context, "/main");
      }
    });

并在“我的动画”结束通话中删除了Navigator.popAndPushNamed(context, "/main");return;

1 个答案:

答案 0 :(得分:0)

虽然我无法运行提供的最小重现,但通过日志,您似乎正在尝试从已弹出或处理的内容中访问上下文。

如果您能够复制您在 minimal repro 上报告的行为,则更容易确定原因。这是我正在使用的完整示例代码。

import 'package:flutter/material.dart';

class StaggerAnimation extends StatelessWidget {
  StaggerAnimation({Key key, this.controller})
      :

        // Each animation defined here transforms its value during the subset
        // of the controller's duration defined by the animation's interval.
        // For example the opacity animation transforms its value during
        // the first 10% of the controller's duration.

        opacity = Tween<double>(
          begin: 0.0,
          end: 1.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.0,
              0.100,
              curve: Curves.ease,
            ),
          ),
        ),
        width = Tween<double>(
          begin: 50.0,
          end: 150.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.125,
              0.250,
              curve: Curves.ease,
            ),
          ),
        ),
        height = Tween<double>(begin: 50.0, end: 150.0).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.250,
              0.375,
              curve: Curves.ease,
            ),
          ),
        ),
        padding = EdgeInsetsTween(
          begin: const EdgeInsets.only(bottom: 16.0),
          end: const EdgeInsets.only(bottom: 75.0),
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.250,
              0.375,
              curve: Curves.ease,
            ),
          ),
        ),
        borderRadius = BorderRadiusTween(
          begin: BorderRadius.circular(4.0),
          end: BorderRadius.circular(75.0),
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.375,
              0.500,
              curve: Curves.ease,
            ),
          ),
        ),
        color = ColorTween(
          begin: Colors.indigo[100],
          end: Colors.orange[400],
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.500,
              0.750,
              curve: Curves.ease,
            ),
          ),
        ),
        super(key: key);

  final Animation<double> controller;
  final Animation<double> opacity;
  final Animation<double> width;
  final Animation<double> height;
  final Animation<EdgeInsets> padding;
  final Animation<BorderRadius> borderRadius;
  final Animation<Color> color;

  // This function is called each time the controller "ticks" a new frame.
  // When it runs, all of the animation's values will have been
  // updated to reflect the controller's current value.
  Widget _buildAnimation(BuildContext context, Widget child) {
    return Container(
      padding: padding.value,
      alignment: Alignment.bottomCenter,
      child: Opacity(
        opacity: opacity.value,
        child: Container(
          width: width.value,
          height: height.value,
          decoration: BoxDecoration(
            color: color.value,
            border: Border.all(
              color: Colors.indigo[300],
              width: 3.0,
            ),
            borderRadius: borderRadius.value,
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      builder: _buildAnimation,
      animation: controller,
    );
  }
}

class StaggerDemo extends StatefulWidget {
  @override
  _StaggerDemoState createState() => _StaggerDemoState();
}

class _StaggerDemoState extends State<StaggerDemo>
    with TickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Future<void> _playAnimation() async {
    try {
      await _controller.forward().orCancel;
      await _controller.reverse().orCancel;
    } on TickerCanceled {
      // the animation got canceled, probably because we were disposed
    }
  }

  @override
  Widget build(BuildContext context) {
    var timeDilation = 10.0; // 1.0 is normal animation speed.
    return Scaffold(
      appBar: AppBar(
        title: const Text('Staggered Animation'),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          _playAnimation();
        },
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Container(
                width: 300.0,
                height: 300.0,
                decoration: BoxDecoration(
                  color: Colors.black.withOpacity(0.1),
                  border: Border.all(
                    color: Colors.black.withOpacity(0.5),
                  ),
                ),
                child: StaggerAnimation(controller: _controller.view),
              ),
              ElevatedButton(
                child: Text('Open route'),
                onPressed: () {
                  // Navigate to second route when tapped.
                  Navigator.popAndPushNamed(context, '/second');
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

void main() {
  runApp(StaggerDemoApp());
}

class StaggerDemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Start the app with the "/" named route. In this case, the app starts
      // on the FirstScreen widget.
      initialRoute: '/',
      routes: {
        // When navigating to the "/" route, build the FirstScreen widget.
        '/': (context) => StaggerDemo(),
        // When navigating to the "/second" route, build the SecondScreen widget.
        '/second': (context) => MyHomePage(title: 'Second Page',),
      },
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

据我观察,在动画中弹出屏幕并推送新屏幕似乎不会导致任何错误。正如日志所提到的,在 AnimationControllers 上设置的触发侦听器是导致问题的原因。

Demo