Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Delayed Animation code Error : AnimationController.forward() called after AnimationController.dispose()

Tags:

flutter

dart

I have implemented a delayed animation in my welcome screen, but I get this below error in my flutter App. Let me know if there is an error in my code that I can correct and fix this issue.

The error is :

E/flutter (11565): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)]
Unhandled Exception:
'package:flutter/src/animation/animation_controller.dart': Failed
assertion: line 455 pos 7: '_ticker != null':
AnimationController.forward() called after
AnimationController.dispose()

Here is my code:

class DelayedAnimation extends StatefulWidget {
  final Widget child;
  final int delay;
 
  DelayedAnimation({@required this.child, this.delay});

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

class _DelayedAnimationState extends State<DelayedAnimation> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation<Offset> _animOffset;

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

    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 800),
    );
    final curve = CurvedAnimation(
      curve: Curves.decelerate,
      parent: _controller,
    );
    _animOffset = Tween<Offset>(
      begin: const Offset(0.0, 0.35),
      end: Offset.zero,
    ).animate(curve);

    if (widget.delay == null) {
      _controller.forward();
    } else {
      Timer(Duration(milliseconds: widget.delay), () {
        _controller.forward();
      });
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      child: SlideTransition(
        position: _animOffset,
        child: widget.child,
      ),
      opacity: _controller,
    );
  }
}
like image 204
Jyo Avatar asked Dec 23 '22 17:12

Jyo


1 Answers

This problem will often occur when you use a Timer or Future.delayed to interact with an AnimationController. This is the problem. Lets say that delay = 1000.

The following code tells flutter that in 1000 milliseconds, call forward() on the animation controller in your DelayedAnimation widget

Timer(Duration(milliseconds: widget.delay), () {
   _controller.forward();
});

However, before this happens, your DelayedAnimation widget is disposed (this happens, for example, if the user moves to a different screen)

That means that when the Timer executes, it is calling forward() on a controller which has been disposed (because the DelayedAnimation has been disposed)

There are a few solutions.

  1. Check the mounted property before calling forward:
Timer(
  Duration(
    milliseconds: widget.delay
  ), 
  () {
    if(mounted) {
      _controller.forward();
    }
  }
);

or 2. Store the Timer when you create it:

_timer = Timer(
  Duration(
    milliseconds: widget.delay
  ), 
  () {
    _controller.forward();
  }
);

Then cancel it on dispose:

@override
  void dispose() {
    super.dispose();
    _controller.dispose();
    _timer?.cancel();
  }
like image 163
James Allen Avatar answered May 24 '23 19:05

James Allen