Флаттер: анимированный переключатель в стеке не анимируется в нужном положении

#flutter #animation

Вопрос:

Я пытаюсь использовать AnimatedSwitcher внутреннее Stack . Это приводит к очень странному поведению анимации. Он анимирует соответствующий дочерний виджет (красное поле в моем случае) в центре моего 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())
              ],
            ),
          ),
    );
  }
}
 

Что приводит к следующему поведению при нажатии-событии где-то на экране:

введите описание изображения здесь

Есть идеи, почему красное поле не анимируется в левом верхнем углу, а появляется там только после завершения анимации?

Комментарии:

1. Ваш ключ должен быть уникальным окончательным, а не повторять один и тот же снова и снова.

2. хорошо, я думал, что использую уникальный ключ, добавляя UniqueKey его в свою анимацию Container . Если я удалю этот ключ, он полностью прекратит анимацию (даже если он действительно перестанет неловко менять положение, как и раньше)… не могли бы вы предложить, как это решить?

3. вам нужен уникальный ключ… но должен быть заключительный глобальный по этому классу… потому что каждый раз, когда метод сборки вызывается снова, тот, который у вас есть, отбрасывается и создается снова

4. если я добавлю final key = UniqueKey(); уровень класса, он все равно поменяется местами… что я упускаю?

Ответ №1:

Проблема заключается, как указал @Marino Zorilla, в уникальном ключе, который я указал для своего анимирующего виджета. Как только я удалил этот ключ, а также изменил «пустой» Container (для ложного условия моей троичной операции) на a SizedBox , он работает по желанию.

По-видимому, это связано с тем, как flutter работает внутри (когда дерево элементов и дерево виджетов сравниваются, чтобы определить, какие виджеты необходимо перестроить). Если виджет изменится на другой тип (как в моем случае с 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(),
                  
                )
              ],
            )),
      
    );
  }
}