0

我正在尝试在 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,
    );
  }
}

更新

嗨解决了我认为的问题,我仍然需要做一些测试,但似乎调用 Navigator.popAndPushNamed(context, "/main");AnimationClass 不是一个好主意我只是更改了我的登录类,当动画完成时我调用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;在我的动画结束通话

4

1 回答 1

1

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

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

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 上设置的触发侦听器是导致问题的原因。

演示

于 2021-01-22T19:39:29.953 回答