'Flutter AnimatedSwitcher jumps between children

I am trying to implement some custom design in an expasion panel list. Therefore, I wanted to create some kind of animation that animates smoothly from one view (e.g. header) to another view (e.g. full info of the tile) that has other dimensions (obviously, full info will be higher than just the header). This is quite easy to implement with an AnimatedContainer. However, I would need the height of the header widget and the full info widget in order to animate between these two heigths. As these values differ between tiles (other info -> maybe other height) and tracking height via global keys is not my preferred solution, I decided to use the much simpler AnimatedSwitcher instead. However, the behavior of my AnimatedSwitcher is quite strange. At first, the other tiles in the ListView (in my example the button) move down instantly and subsequently the tile expands. Has anyone an idea of how I could implement some code in order to achieve the same animation that I would get from AnimatedContainer(button/other tiles moving down simultaniously with the tile expanding)? Thanks in advance for any advice. Here is my code:

class MyPage extends State {
List _items;
int pos;

@override
void initState() {
pos = 0;
_items = [
  Container(
    color: Colors.white,
    width: 30,
    key: UniqueKey(),
    child: Column(
      children: <Widget>[Text('1'), Text('2')], //example that should visualise different heights
    ),
  ),
  Container(
    width: 30,
    color: Colors.white,
    key: UniqueKey(),
    child: Column(
      children: <Widget>[Text('1'), Text('2'), Text('44534'), Text('534534')],
    ),
  )
];
super.initState();
}

@override
Widget build(BuildContext context) {

return Scaffold(
  body: ListView(
    padding: EdgeInsets.only(top: 100),
    children: <Widget>[
      AnimatedSwitcher(
        duration: Duration(seconds: 1),
        transitionBuilder: (child, animation) => ScaleTransition(
          child: child,
          scale: animation,
        ),
        child: _items[pos],
      ),
      RaisedButton(
          child: Text('change'),
          onPressed: pos == 0
              ? () {
                  setState(() => pos = 1);
                }
              : () {
                  setState(() => pos = 0);
                })
    ],
  ),
);
}
}

The solution was quite simple. Just found out that there exists an AnimatedSize Widget that finds out the size of its children automatically.



Solution 1:[1]

I stumbled on this post and since I had a similar problem I decided to create a tutorial here on how to mix AnimatedSwitcher and AnimatedSize to solve this issue. Animations do not happen at the same time but the advantage is that you have full control on the animation provided to the switcher.

I ended up doing this in the end (please note that I'm using BlocBuilder and that AnimatedSizeWidget is a basic implementation of AnimatedSize:

    AnimatedSizeWidget(
              duration: const Duration(milliseconds: 250),
              child: BlocBuilder<SwapCubit, bool>(
                builder: (context, state) {
                  return AnimatedSwitcher(
                    duration: const Duration(milliseconds: 1000),
                    child: state
                        ? Icon(Icons.face, size: 80, key: Key("80"))
                        : Icon(Icons.face, size: 160, key: Key("160")),
                  );
                },
              ),
            ),

Solution 2:[2]


  var isWidgetA = true;
  final Widget widgetA = Container(
    key: const ValueKey(1),
    color: Colors.red,
    width: 100,
    height: 100,
  );
  final Widget widgetB = Container(
    key: const ValueKey(2),
    color: Colors.green,
    width: 50,
    height: 50,
  );     
...
  AnimatedSwitcher(
    duration: const Duration(milliseconds: 500),
    transitionBuilder: (Widget child, Animation<double> animation) {
      return SizeTransition(
        sizeFactor: animation,
        child: ScaleTransition(
          child: child,
          scale: animation,
          alignment: Alignment.center,
        ),
      );
    },
    child: isWidgetA
        ? widgetA
        : widgetB,
  ),

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 gbaccetta
Solution 2 Mate