AnimatedContainer Padding Issue

42 Views Asked by At

I'm designing an AnimatedContainer for my Flutter project, where a window opens when the floating action button is pressed, animatedly. However, I have a small issue: when I close the window, a object appears and disappears in the bottom right corner. I noticed that this issue goes away when I remove the padding in the AnimatedContainer, but I still need the padding. How can I solve this problem?

enter image description here

AnimatedContainer:

class _ArtiButonuState extends State<ArtiButonu> {
  bool _isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        AnimatedPositioned(
          duration: const Duration(milliseconds: 300),
          curve: Curves.bounceIn,
          right: MediaQuery.of(context).size.width / 2 - 166,
          bottom: MediaQuery.of(context).padding.bottom + 108.0,
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 300),
            padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 72.0),
            decoration: BoxDecoration(
              color: Colors.white.withOpacity(_isExpanded ? 0.9 : 0.0),
              borderRadius: BorderRadius.circular(10.0),
                ),
...

My entire floating action button code:

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

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

class _ArtiButonuState extends State<ArtiButonu> {
  bool _isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        AnimatedPositioned(
          duration: const Duration(milliseconds: 300),
          curve: Curves.bounceIn,
          right: MediaQuery.of(context).size.width / 2 - 166,
          bottom: MediaQuery.of(context).padding.bottom + 108.0,
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 300),
            padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 72.0),
            decoration: BoxDecoration(
              color: Colors.white.withOpacity(_isExpanded ? 0.9 : 0.0),
              borderRadius: BorderRadius.circular(10.0),
            ),
            child: _isExpanded ? ClipRRect(
              borderRadius: BorderRadius.circular(10.0),
              child: BackdropFilter(
                filter: ImageFilter.blur(sigmaX: _isExpanded ? 5.0 : 0.0, sigmaY: _isExpanded ? 5.0 : 0.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    _buildOption(Icons.phone, 'Şoförü Ara'),
                    _buildOption(Icons.insert_comment, 'Şoföre Mesaj At'),
                    _buildOption(Icons.phone, 'Hostesi Ara'),
                    _buildOption(Icons.insert_comment, 'Hostese Mesaj At'),
                  ],
                ),
              ),
            ) : Container(),
          ),
        ),
        Positioned(
          right: MediaQuery.of(context).size.width / 2 - 28,
          bottom: MediaQuery.of(context).padding.bottom + 64.0,
          child: Column(
            children: [
              FloatingActionButton(
                backgroundColor: _isExpanded ? MyColors.mesajlarProfilTextColor : MyColors.yellow,
                onPressed: () {
                  setState(() {
                    _isExpanded = !_isExpanded;
                  });
                },
                shape: const CircleBorder(),
                elevation: 0,
                child: AnimatedCrossFade(
                  duration: Duration(milliseconds: 300),
                  firstCurve: Curves.easeInOut,
                  secondCurve: Curves.easeInOut,
                  sizeCurve: Curves.easeInOut,
                  crossFadeState: _isExpanded ? CrossFadeState.showFirst : CrossFadeState.showSecond,
                  firstChild: const Icon(Icons.clear, color: Colors.white, size: 36.0),
                  secondChild: const Icon(Icons.add_rounded, color: Colors.white, size: 48.0),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  Widget _buildOption(IconData icon, String label) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12.0),
      child: InkWell(
        onTap: () {
          print('Butona tıklandı');
        },
        child: Row(
          children: [
            FloatingActionButton(
              shape: CircleBorder(),
              backgroundColor: MyColors.yellow,
              onPressed: () {},
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(icon, color: Colors.white),
                ],
              ),
            ),
            const SizedBox(
              width: 8.0,
            ),
            Text(
              label,
              style: const TextStyle(
                color: MyColors.mesajlarProfilTextColor,
                fontFamily: 'Poppins SemiBold',
              ),
            )
          ],
        ),
      ),
    );
  }
}
1

There are 1 best solutions below

0
Brian Thompson On

You have a couple problems with the way you're setting up your animation:

  • In your AnimatedPositioned(), your position is defined with numbers that do not change (AnimatedPositioned.right & AnimatedPositioned.bottom). If you want your pop-up menu's position to animate, the values for right & bottom need to change somehow. Using the widget's state is perfect. So, you could say this (change the "0"s to whatever numbers you want the animation to start from):
AnimatedPositioned(
    duration: const Duration(milliseconds: 300),
    curve: Curves.bounceIn,
    right: _isExpanded ? MediaQuery.of(context).size.width / 2 - 166 : 0,
    bottom: _isExpanded ? MediaQuery.of(context).padding.bottom + 108.0 : 0,
    child: AnimatedContainer(),
)
  • An AnimatedContainer widget animates its properties ONLY. It does not animate its child or any of its child's descendants Docs. That means that the only thing your AnimatedContainer() is animating is the opacity of its background.
  • One other point, although I don't really think it matters right now. You have defined the curve for AnimatedPosition (Curves.bounceIn) but not for AnimatedContainer, which means it defaults to Curves.linear). After you fix the issues above, this may make the animation look strange since the various effects will have different curves. I would recommend doing the same curve for both unless you specifically want that effect.

I would fix all these things and then see where that leaves you. I would guess that the little box you are seeing is the Container() that is the child of your AnimatedContainer() after _isExpanded = false while its opacity is being lowered. In the end, it disappears because the opacity hits zero. Since the Container() has padding from the AnimatedContainer(), it has some size. That would mean, I think, that the container is actually still there after the animation is done, it is just not visible. When you get rid of the AnimatedContainer's padding, that allows the Container to have no size, so you don't see it.

Here's the real point: I would recommend you really specify what effects you are looking for and then redesign your widgets to support that. If you simply want your menu to grow from the bottom of the window, you could use ClipRRect() with an AnimatedAlign() child (or maybe an AnimatedPosition() child). That would make your menu look like it's sliding up from below the screen. There are, of course, lots of different options that would all have different executions.

I think the big thing will be NOT switching widgets from ClipRRect() to Container() but instead animating position and/or size to get the effect you want.