animated opacity and hiding the widget so its not clickable

Viewed 5708

I'm using AnimatedOpacity to hide a the navbar based on a scroll event. I'm specifically having trouble making it animate in and out well, while also making the widget not clickable.

In order to hide my widget and prevent it from being clicked, I conditionally rendering the my CustomNavBar() component or Container() based on the visible state. However, this cuts my animation abruptly when i fade out. It works fading in, however.

AnimatedOpacity(
  opacity: _isVisible ? 1.0 : 0.0,
  duration: Duration(milliseconds: 300),
  child: _isVisible
    ? CustomNavBar()
    : Container(),
)

This is expected, since the component literally is not existing when _isVisible is false. That kills the whole animation. How do I get around this?

Thanks

3 Answers

There may be other ways to do this but you could use two variables for opacity and visibility. Make use of the AnimatedWidget's onEnd callback to change visibility. Also, you may want to use the Visibility widget for this. It lets you even maintain the child's state if you want.

/// somewhere outside of build in a StatefulWidget
bool _isVisible = true;
bool _isOpaque = true;


/// within the build method
AnimatedOpacity(
  opacity: _isOpaque ? 1.0 : 0.0,
  onEnd: (){
    if(!_isOpaque)
      setState((){
        _isVisible = false;
      });
  },
  child: Visibility(
    child: child,
    visible: _isVisible,
  ),
);


//then in some method to toggle the state...
setState((){
  if(!_isVisible){
    _isVisible = true;
    _isOpaque = true;
  }
  else{
    _isOpaque = false;
  }
})

Come to think of it, you also have AnimatedSwitcher which is a LOT easier to do this. I will leave the above code for reference to the onEnd callback. Also, if maintaining state in the child widget is important then the above code would be better suited since you have that option. If it's not important then switch-away.

AnimatedSwitcher(
   child: _isVisible ? child : SizedBox.shrink(),
)
IgnorePointer(
    child: AnimatedOpacity(
        child: child,
        opacity: visible ? 1.0 : 0.0,
        duration: const Duration(milliseconds: 200),
    ),
    ignoring: !visible,
)

This is working for me like a charm. Basically, it says "ignore hit testing while child is invisible.

I also had a case for AppBar's particular action which I wanted to show / hide with animated opacity. After realizing that Opacity widget leaves child clickable I ended up using AnimatedSwitcher for the action Widget:

AnimatedSwitcher(
  duration: Duration(milliseconds: 500),
  child: !_visible
    ? SizedBox()
    : IconButton(
        icon: ... // more of the related code 
  ),
)

I purposely negate the condition to put the empty SizedBox() to the top for the readability as the main widget can take a lot of lines in the code.

You can use your CustomNavBar instead of my IconButton:

AnimatedSwitcher(
  duration: Duration(milliseconds: 500),
  child: !_visible
    ? SizedBox()
    : CustomNavBar(
        ... // more of the related code 
  ),
)
Related