Home > Back-end >  Flutter animatedOpacity onEnd not being called
Flutter animatedOpacity onEnd not being called

Time:08-13

I'm unable to get the onEnd portion of AnimatedOpacity to get called.

I've got a _visible variable set to false, I'm changing that value with setState that is called on a button press, however the print statement in onEnd is never called.

Declared at the top of my widget and set to false initially.

bool _visible = false;

setState is updating visible from a textbutton onPressed

 setState(() {
     _visible = !_visible;
 });

The visibility widget is toggled once the above state is passed however the container that is the child of the AnimatedOpacity widget is immediately shown ( there is no animated fade out from opacity to solid black ), and the only way to trigger the animation completely is to modify the _visible ? 1.0 : 0.0.

The goal is to get the container once triggered to change from an opacity of 0 to 1 then call onEnd, which currently is not happening.

Visibility(
              visible: _visible,
              child: Center(
                child: AnimatedOpacity(
                  opacity: _visibility ? 1.0 : 0.0,
                  duration: const Duration(milliseconds: 1000),
                  onEnd: () {
                     print("I am never called");
                  },
                  child: Container(
                    width: double.infinity,
                    height: double.infinity,
                    color: Colors.black,
                  ),
                ),
              ),
            ),

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {
  bool _visible = false;


  @override
  Widget build(BuildContext context) {    
    return Scaffold(
          resizeToAvoidBottomInset: false,
          body: Stack(children: [
TextButton(
                  style: ElevatedButton.styleFrom(
                    elevation: 10,
                    shape: CircleBorder(),
                    primary: Colors.transparent,
                    onSurface: Colors.transparent,
                    shadowColor: Colors.transparent,
                  ),
                  onPressed: () {
                    setState(() {
                      _visible = !_visible;
                    });
                    
                  },
                  child: Icon(Icons.home, color: Colors.white),
                ),
            Visibility(
              visible: _visible,
              child: Center(
                child: AnimatedOpacity(
                  opacity: _visible ? 1 : 0,
                  duration: const Duration(milliseconds: 1000),
                  onEnd: () { 
                   print("I am not called but should be");
                  },
   
                  child: Container(
                    width: 200,
                    height: 200,
                    color: Colors.black,
                  ),
                ),
              ),
            ),
     
           
          ]));
    }
  }

CodePudding user response:

The problem with you code is that the AnimatedOpacity widget is wrapped inside a Visibility widget, which is not animated. Therefore when you call setState to toggle _visible member, the container will become instantly visible. The animation won't run at all, because by the time the Container becomes visible, the _visible member's value is already 1.

To solve this, simply remove Visibility widget, when the AnimatedOpacity reaches opacity value of 0, the Container will be invisible and on value 1 it will be visible, animating between the two states. The print is also executed in onEnd:

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {
  bool _visible = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        body: Stack(children: [
          TextButton(
            style: ElevatedButton.styleFrom(
              elevation: 10,
              shape: const CircleBorder(),
              primary: Colors.transparent,
              onSurface: Colors.transparent,
              shadowColor: Colors.transparent,
            ),
            onPressed: () {
              setState(() {
                _visible = !_visible;
              });
            },
            child: const Icon(Icons.home, color: Colors.white),
          ),
          Center(
            child: AnimatedOpacity(
              opacity: _visible ? 1 : 0,
              duration: const Duration(milliseconds: 1000),
              onEnd: () {
                print("I am not called but should be");
              },
              child: Container(
                  width: 200,
                  height: 200,
                  color: Colors.black,                  
            ),
          ),
        ]));
  }
}
  • Related