Flutter Animate Visibility with opacity
Asked Answered
L

4

8

The Flutter Visibility Widget allows everything contained within it to be both hidden and disabled. However, I want to animate the opacity as well as invoke visibility. Using the Visibility widget is overriding the animated opacity. This is to be expected based on my knowledge of other languages.

Is there a simple and convenient way to achieve an animated opacity and visibility. IT would make life simpler than setting a timer.

The example below uses a bool of hasAccess using Provider as the state management.

child: Stack(
  children: [

  Visibility(
    visible: hasAccess ? false : true,
    child: AnimatedOpacity(
      duration: Duration(milliseconds: 400),
      opacity: hasAccess ? 0 : 1,
      child: Text('Not logged in'),
  )), 

  Visibility(
    visible: hasAccess ? true : false,
    child: AnimatedOpacity(
      duration: Duration(milliseconds: 400),
      opacity: hasAccess ? 1 : 0,
      child: Text('Is logged in'),
   ),
  ), 
 ],
)
Lachlan answered 27/10, 2021 at 10:54 Comment(0)
L
5

Visibility and Opacity are two entirely different things.

Opacity can be used to change the opacity of its child. AnimatedOpacity allows for an animation of this and FadeTransition allows for a transition of this. However, opacity simply deals only with opacity. The child is still painted on the screen even if you can't see it. It will still respond to pointer events and it is still taking up computational resources.

The Visibility class is an amalgamation of various other classes including IgnorePointer. Depending on the settings given, it will either replace the child entirely with a widget that takes up minimal resources and has no content (the default is SizedBox.shrink()) or move it OffScreen or simply prevent pointer events. It will always make it invisible without any animation. It can effectively replace a Widget entirely, move it offscreen or disable pointer events whilst always making the child invisible.

Using AnimatedOpacity or FadeTransition will allow you to animate the opacity. Once that animation is complete and the child has an opacity of 0, is when to use Visibility. You can find the status of your animation through listeners, timers or any other method you can think of.

You will find that Widgets like AnimatedOpacity also have callback functions. You could use one of these functions for when the animation is complete. You could create a state called _visible for example as a bool. Then set it to false once the animation of AnimatedOpacity is complete. You would then wrap the child of AnimatedOpacity in the Visibility Widget setting it's property to your _visible state bool.

Visibility(visibility: _visible, child: <yourChildWidget>);
Lachlan answered 14/6, 2022 at 12:43 Comment(0)
D
15

I hope this should solve the problem. you need to set maintainAnimation=true and maintainState=true in Visibility widget.

Visibility(
     visible: hasAccess,
     maintainAnimation: true,
     maintainState: true,
     child: AnimatedOpacity(
        duration: const Duration(milliseconds: 500),
        curve: Curves.fastOutSlowIn,
        opacity: hasAccess ? 1 : 0,
        child: Text('abc')
     )
    )
Diopside answered 1/3, 2023 at 8:28 Comment(1)
The AnimatedOpacity you have put there would only animate when the Visibility widget was set to true. It would instantly vanish when set to false so that isn't the answer. You are preserving an animation that would already be rendered invisible (an in fact replaced with a SizedBox).Lachlan
L
5

Visibility and Opacity are two entirely different things.

Opacity can be used to change the opacity of its child. AnimatedOpacity allows for an animation of this and FadeTransition allows for a transition of this. However, opacity simply deals only with opacity. The child is still painted on the screen even if you can't see it. It will still respond to pointer events and it is still taking up computational resources.

The Visibility class is an amalgamation of various other classes including IgnorePointer. Depending on the settings given, it will either replace the child entirely with a widget that takes up minimal resources and has no content (the default is SizedBox.shrink()) or move it OffScreen or simply prevent pointer events. It will always make it invisible without any animation. It can effectively replace a Widget entirely, move it offscreen or disable pointer events whilst always making the child invisible.

Using AnimatedOpacity or FadeTransition will allow you to animate the opacity. Once that animation is complete and the child has an opacity of 0, is when to use Visibility. You can find the status of your animation through listeners, timers or any other method you can think of.

You will find that Widgets like AnimatedOpacity also have callback functions. You could use one of these functions for when the animation is complete. You could create a state called _visible for example as a bool. Then set it to false once the animation of AnimatedOpacity is complete. You would then wrap the child of AnimatedOpacity in the Visibility Widget setting it's property to your _visible state bool.

Visibility(visibility: _visible, child: <yourChildWidget>);
Lachlan answered 14/6, 2022 at 12:43 Comment(0)
B
3

if you want simple solution, so use this one:

AnimatedOpacity(
  // If the widget is visible, animate to 0.0 (invisible).
  // If the widget is hidden, animate to 1.0 (fully visible).
  opacity: _visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 500),
  // The green box must be a child of the AnimatedOpacity widget.
  child: Container(
    width: 200.0,
    height: 200.0,
    color: Colors.green,
  ),
)

Original link: https://docs.flutter.dev/cookbook/animation/opacity-animation

Barbarity answered 8/6, 2022 at 5:48 Comment(1)
That's not quite an answer as Visibility concerns whether something is actually in the widget tree or not. Your example would still allow pointer events as well for the non visible item.Lachlan
T
1
import 'package:flutter/material.dart';

class AnimatedVisibility extends StatefulWidget {
  final bool visible;
  final Widget child;
  final Curve curve;
  final Duration duration;
  final alwaysIncludeSemantics;

  AnimatedVisibility({
    Key? super.key,
    this.visible = true,
    required this.child,
    this.curve = Curves.linear,
    required this.duration,
    this.alwaysIncludeSemantics = false
  });

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

class _AnimatedVisibility extends State<AnimatedVisibility> {
  bool isNotBuild = true;

  @override
  Widget build(BuildContext context) {
    return AnimatedOpacity(
      opacity: widget.visible ? 1 : 0,
      duration: widget.duration,
      curve: widget.curve,
      alwaysIncludeSemantics: widget.alwaysIncludeSemantics,
      onEnd: () {
        if (!isNotBuild) return;

        setState(() {
          isNotBuild = false;
        });
      },
      child: FutureBuilder(
        future: Future.delayed(isNotBuild ? Duration.zero: widget.duration),
        builder: (_, snapshot) => Visibility(child: widget.child, visible: snapshot.connectionState != ConnectionState.done || widget.visible),
      ),
    );
  }
}
Tiepolo answered 26/3 at 3:5 Comment(1)
It can be done a little more simply than that. Further Builder for example is not really necessary there for is the onEnd part. Your answer is very good however. This however can easily be achieved with AnimatedBuilder. Thanks for your input.Lachlan

© 2022 - 2024 — McMap. All rights reserved.