Search code examples
androidflutterdartflutter-animationandroid-lifecycle

Flutter Widget Life Cycle Warning/Exception from Animation Widget


I am trying to build a simple Loader animation inside Flutter using custom animations making use of an Animation Controller and a Tween method. The animation works fine on the mobile, but I am getting an endless list of warning messages that state:-

Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4586 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true.

I have no idea as to what this is. It would be great if I could get an explanation regarding this warning.

Here is my Loader Animation Class Code for reference :

class Loader extends StatefulWidget {
  const Loader({super.key});

  @override
  State<Loader> createState() => _LoaderState();
}

class _LoaderState extends State<Loader> with TickerProviderStateMixin {
  late AnimationController controller;

  late Animation<double> animation_rotation;
  late Animation<double> animation_Radius_in;
  late Animation<double> animation_Radius_out;

  final double idistance = 60;

  double distance = 0;

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

    controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    );

    animation_rotation = Tween<double>(begin: 1.0, end: 0.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.0, 1.0, curve: Curves.linear),
      ),
    );

    animation_Radius_in = Tween<double>(begin: 1.0, end: 0.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.75, 1.0, curve: Curves.easeInOut),
      ),
    );
    animation_Radius_out = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.0, 0.25, curve: Curves.easeInOut),
      ),
    );

    controller.addListener(() {
      if (mounted) {
        setState(() {
          if (controller.value >= 0.75 && controller.value <= 1.0) {
            distance = animation_Radius_in.value * idistance;
          } else if (controller.value >= 0.0 && controller.value <= 0.25) {
            distance = animation_Radius_out.value * idistance;
          }
        });
      }
    });

    controller.repeat();
  }

Thank You.


Solution

  • I am not totally sure that this is the reason, please try. Even if it does not solve your problem, it is highly recommended to remove listeners and dispose controllers according to the widget's lifecycle.

    To do so, create a separate function for the listener (to be able to remove it), and add an override to the dispose method:

    class _LoaderState extends State<Loader> with TickerProviderStateMixin {
      late AnimationController controller;
    
      late Animation<double> animation_rotation;
      late Animation<double> animation_Radius_in;
      late Animation<double> animation_Radius_out;
    
      final double idistance = 60;
    
      double distance = 0;
      
      // move the listener logic here
      void _listener() {
        if (mounted) {
          setState(() {
            if (controller.value >= 0.75 && controller.value <= 1.0) {
              distance = animation_Radius_in.value * idistance;
            } else if (controller.value >= 0.0 && controller.value <= 0.25)             distance = animation_Radius_out.value * idistance;
            }
          });
        }
      }
    
      // get rid of the listener and controller
      @override
      void dispose() {
        controller.removeListener(_listener);
        controller.dispose();
        super.dispose();
      }
    
      @override
      void initState() {
        super.initState();
    
        controller = AnimationController(
          vsync: this,
          duration: const Duration(seconds: 2),
        );
    
        animation_rotation = Tween<double>(begin: 1.0, end: 0.0).animate(
          CurvedAnimation(
            parent: controller,
            curve: const Interval(0.0, 1.0, curve: Curves.linear),
          ),
        );
    
        animation_Radius_in = Tween<double>(begin: 1.0, end: 0.0).animate(
          CurvedAnimation(
            parent: controller,
            curve: const Interval(0.75, 1.0, curve: Curves.easeInOut),
          ),
        );
        animation_Radius_out = Tween<double>(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(
            parent: controller,
            curve: const Interval(0.0, 0.25, curve: Curves.easeInOut),
          ),
        );
    
        controller.addListener(_listener);
    
        controller.repeat();
      }