Animation In Flutter: AnimatedCrossFade

Animation In Flutter: AnimatedCrossFade

Fade animation between two different widgets and their sizes.

ยท

4 min read

AnimatedCrossFade

  • Consider a scenario, where you want to replace a current widget with a new widget.
  • For example, you have an app where there are different types of posts. And you want to blur/just place a widget on top of that post saying "This post is sensitive". Just Like Instagram
  • Let's make something like this in our Flutter App
  • noCrossFade.gif
  • As you can see It's working fine. But the problem is the visibility of the original post when we click on the Show Me button is very quick. It's not smooth.
  • So to do a fade animation when we replace our Spoiler Alert widget with the original post widget, we can use the AnimatedCrossFade widget.

Using AnimatedCrossFade

  • First Lets define the AnimatedCrossFade widget in the body
  • Scaffold(
        backgroundColor: Colors.blueGrey,
        appBar: AppBar(
          title: const Text("AnimatedCrossFade"),
        ),
        body: Center(
          child: AnimatedCrossFade(
               // ....
          )
      )
    
  • AnimatedCrossFade takes two children widgets that as you might have guessed, firstChild and secondChild.
  • So let's give the Spoiler Alert widget to the firstChild property because it's the first child we're showing to the user.
  • AnimatedCrossFade(
           firstChild: GestureDetector(
           onTap: () {
             setState(() {
                 isVisible = true;
               });
            },
           child: Container(
              width: 300,
              height: 400,
              color: Colors.black,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Text(
                    "Spoiler Alert",
                    style: TextStyle(fontSize: 18, color: Colors.white),
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  OutlinedButton(
                      onPressed: () {
                        setState(() {
                          isVisible = true;
                        });
                      },
                      child: const Text("Show me"))
                ],
              ),
            ),
          ),
         )
      )
    
  • And now, also give the original post widget to the secondChild property.
  • secondChild: GestureDetector(
            onTap: () {
              setState(() {
                isVisible = false;
              });
            },
            child: Container(
                width: 300,
                height: 400,
                color: Colors.blueGrey,
                child: Image.network(
                    "https://memegenerator.net/img/instances/60138700.jpg")
            ),
    ),
    
  • Still, it will not do anything. We have two more required properties i.e -

    duration

  • You also have to provide the duration to the AnimatedCrossFade widget. Which is used to define for how long the animation will fade from first to a second child.
  • Let's put 1 second
  • AnimatedCrossFade(
     duration: const Duration(seconds: 1),
     firstChild : //...
     secondChild: //....
    )
    

    crossFadeState

  • The last required property is crossFadeState. It is as you might have guessed, the state, which determines whether to show firstChild or secondChild

  • If it isCrossFadeState.showFirst then, when the animation completes the firstChild will be shown and vice versa

  • The child that is shown will fade in, while the other will fade out.
  • Now let's conditionally render our two children. For that let's first define a boolean.
  • bool showPost = false;
    
  • And in the crossFadeState property add the below code :
  • crossFadeState:
              !showPost ? CrossFadeState.showFirst : CrossFadeState.showSecond,
    
  • And THAT'S IT !!

Final Output


layoutBuilder :

  • To understand why this property is needed, Let's change the width and height of the Post Image.
  •       secondChild: GestureDetector(
            onTap: () {
              setState(() {
                showPost = false;
              });
            },
            child: Container(
                width: 200,   // <- here
                height: 300,  // <- here
                color: Colors.blueGrey,
                child: Image.network(
                    "https://memegenerator.net/img/instances/60138700.jpg"
                      ),
                ),
          ),
    
  • Now if you run the app, you'll see a weird jump when we go from the firstChild state to secondChild state.
  • crossFadeWithLayoutBuilder.gif
  • To fix these types of issues where both the widget have different height and width. We have to use layoutbuilder of the AnimatedCrossFade widget.
  • This has 4 parameters i.e :
  • topChild, topChildKey, bottomChild, bottomChildKey.
  • This layoutbuilder will return a Stack widget with two Positioned widgets. The first Positioned widget will take the bottomChild widget and the second Positioned widget takes the topChild widget.
  • Also don't forget to add alignment center and clipBehaviour to Clip.none. Otherwise, you'll not get the expected result.
  • AnimatedCrossFade(
              layoutBuilder:
                      (topChild, topChildKey, bottomChild, bottomChildKey) =>
                          Stack(
                    clipBehavior: Clip.none, 
                    alignment: Alignment.center,
                    children: [
                      Positioned(
                        key: bottomChildKey,
                        child: bottomChild,
                        top: 0.0,
                      ),
                      Positioned(
                        key: topChildKey,
                        child: topChild,
                      ),
                    ],
                  ),
       //....
    )
    
  • OUTPUT :
  • crossFadeWithLayoutBuilder2.gif
  • SEE!! SmoothCoreyVidalGIF.gif

  • Thank you for reading. If you found this article useful then, don't forget to share it with others ๐Ÿ™‚.
  • Feedback and comments are welcomed โœ๏ธ
  • See you in the next article. Till then...
  • PeaceOutImOutGIF.gif

Did you find this article valuable?

Support Dhruv Nakum by becoming a sponsor. Any amount is appreciated!

ย