'Error "This widget has been unmounted, so the State no longer has a context (and should be considered defunct)" upon SDK and packages upgrade

I got the error "This widget has been unmounted, so the State no longer has a context (and should be considered defunct. Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active" upon upgrading the flutter SDK from >2.5.1 and their pub packages. Here is the code snippet of the error-causing widget:

   class SplashScreen extends StatefulWidget {
  SplashScreen({this.auth});
  final BaseAuth auth;
  @override
  _SplashScreenState createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen>
    with TickerProviderStateMixin {
  Animation logoanimation, textanimation;
  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController =
        AnimationController(vsync: this, duration: Duration(seconds: 2));
    logoanimation = Tween(begin: 25.0, end: 100.0).animate(
        CurvedAnimation(parent: animationController, curve: Curves.bounceOut));
    textanimation = Tween(begin: 0.0, end: 27.0).animate(CurvedAnimation(
        parent: animationController, curve: Curves.bounceInOut));
    logoanimation.addListener(() => this.setState(() {}));
    textanimation.addListener(() => this.setState(() {}));
    animationController.forward();
    Timer(
        Duration(seconds: 4),
        () => Navigator.of(context).pushReplacement(MaterialPageRoute(
            builder: (BuildContext context) => RootPage(       // <----- Error pointing to 
                  auth: widget.auth,
                ))));
  }

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

This error is pointing to the line builder: (BuildContext context) => RootPage(..

Here is what i have tried based on this solution but, I coud not be able to resolve the issue.

 Timer(
        Duration(seconds: 4),
        () => Navigator.of(context, rootNavigator: true)
            .pushReplacement(MaterialPageRoute(
                builder: (BuildContext context) => RootPage(
                      auth: widget.auth,
                    ))));

Update: I tried to use future.delayed and if(mounted) scenarios instead of Timer but this has not resolved the issue.

But, one observation which would be interesting is:

Instead of naviagating to RootPage that needs authentication, If we navigate to some other dart page which do not need login authentication the navigation works fine without any errors.

Here is the code snippet for the RootPage:

class RootPage extends StatefulWidget {
  const RootPage({this.auth});
  final BaseAuth auth;
  @override
  _RootPageState createState() => _RootPageState();
}

enum AuthStatus {
  //NotDetermined,
  SignedIn,
  NotSignedIn
}
//...
//...


@override
  Widget build(BuildContext context) {
    switch (authStatus) {
      case AuthStatus.NotSignedIn:
        return LoginPage(
          auth: widget.auth,
          onSignedIn: _signedIn,
        );
        break;
      case AuthStatus.SignedIn:
        return TabbedHomeScreen(
          auth: widget.auth,
          onSignedOut: _signedOut,
        );
        break;
      /*case AuthStatus.NotDetermined:
        return Scaffold(body: Center(child: Text("Hello World"),),);
        break;*/
    }
    
  }
}

Could someone please share your thoughts and assist me in resolving this issue.



Solution 1:[1]

Not sure if it helps, but it's a good idea to also cancel the Timer in the dispose, so something like:

class _SplashScreenState extends State<SplashScreen>
    with TickerProviderStateMixin {
  Animation logoanimation, textanimation;
  AnimationController animationController;
  Timer? timer;

  @override
  void initState() {
    super.initState();
    animationController =
        AnimationController(vsync: this, duration: Duration(seconds: 2));
    logoanimation = Tween(begin: 25.0, end: 100.0).animate(
        CurvedAnimation(parent: animationController, curve: Curves.bounceOut));
    textanimation = Tween(begin: 0.0, end: 27.0).animate(CurvedAnimation(
        parent: animationController, curve: Curves.bounceInOut));
    logoanimation.addListener(() => this.setState(() {}));
    textanimation.addListener(() => this.setState(() {}));
    animationController.forward();
    timer = Timer(
        Duration(seconds: 4),
            () => Navigator.of(context).pushReplacement(MaterialPageRoute(
            builder: (BuildContext context) => RootPage(       // <----- Error pointing to 
              auth: widget.auth,
            ))));
  }

  @override
  void dispose() {
    animationController.dispose();
    timer?.cancel();
    super.dispose();
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ivo Beckers