Flutter:Stack 中的 AnimatedSwitcher 未在所需位置设置动画

Flutter: AnimatedSwitcher in Stack not animating in desired position

我正在尝试在 Stack 中使用 AnimatedSwitcher。这会导致动画出现非常奇怪的行为。它在我的 Stack 中央为相应的子部件(在我的例子中是一个红色框)设置动画,完成后它会捕捉到我屏幕的左上角(这是我也希望动画发生的地方)。当我切换回来时,同样的奇怪行为发生了。

我的代码如下所示:

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(home: Home()));

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  bool _showMenu = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
            child: Stack(
              children: [
                GestureDetector(
                  onTap: () => setState(() => _showMenu = !_showMenu),
                  child: SizedBox.expand(
                    child: Container(
                      color: Colors.yellow,
                    ),
                  ),
                ),
                AnimatedSwitcher(
                    duration: Duration(milliseconds: 500),
                    child: _showMenu
                        ? Container(
                            key: UniqueKey(),
                            height: 200,
                            width: 200,
                            color: Colors.red,
                          )
                        : Container())
              ],
            ),
          ),
    );
  }
}

在屏幕某处的点击事件中会产生以下行为:

知道为什么左上角的红框没有动画,但只有在动画结束后才会出现吗?

正如@Marino Zorilla 指出的那样,问题在于我为动画小部件指定的唯一键。一旦我删除了这个键并将“空”Container(对于我的三元操作的 false-condition)更改为 SizedBox,它就会按预期工作。

显然,这与 flutter 的内部工作方式有关(当 element treewidget tree 进行比较以确定哪个小部件需要重建)。如果小部件更改为不同的类型(例如在我的情况下从 Container 变为 SizedBox),则 flutter 不需要密钥就可以知道需要重建此小部件。

正确的代码如下:

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(home: Home()));

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  bool _showBox = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
              children: [
                GestureDetector(
                  onTap: () => setState(() => _showBox = !_showBox),
                  child: SizedBox.expand(
                    child: Container(
                      color: Colors.yellow,
                    ),
                  ),
                ),
                AnimatedSwitcher(
                  duration: Duration(milliseconds: 500),
                  child: _showBox
                      ? Container(
                          height: 200.0,
                          width: 200.0,
                          color: Colors.red,
                        )
                      : SizedBox(),
                  
                )
              ],
            )),
      
    );
  }
}