Search code examples
fluttercontainersflutter-layoutflutter-animation

Flutter AnimatedContainer resizing overflow issue


as clearly seen on the gif, when the switch button is tapped the AnimatedContainer's height changes from X to Y depending on the enum type but whenever it has to get larger first it overflows with the newly added widgets. Is there a way around of this? Or maybe another widget that can help me achieve the intended animation? Thanks!

enter image description here

enum Company { FOUR, TWO }

class CompanyDemo extends StatefulWidget {
  @override
  _CompanyDemoState createState() => _CompanyDemoState();
}

class _CompanyDemoState extends State<CompanyDemo> {
  Company _comp = Company.FOUR;
  double containerHeight = 170;

  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Padding(
        padding: EdgeInsets.all(15.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              AnimatedContainer(
                height: containerHeight,
                duration: Duration(seconds: 1),
                padding: EdgeInsets.all(15),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey),
                  borderRadius: BorderRadius.circular(10),
                ),
                child: Column(
                  children: [
                    _comp == Company.TWO
                        ? TextFormField(
                            decoration: InputDecoration(
                              labelText: 'CEO',
                              border: InputBorder.none,
                              prefixIcon: Icon(Icons.person),
                            ),
                          )
                        : SizedBox(),
                    _comp == Company.TWO
                        ? Divider(color: Colors.grey)
                        : SizedBox(),
                    TextFormField(
                      decoration: InputDecoration(
                        labelText: 'Head Directive',
                        border: InputBorder.none,
                        prefixIcon: Icon(Icons.people),
                      ),
                    ),
                    Divider(color: Colors.grey),
                    TextFormField(
                      decoration: InputDecoration(
                        labelText: 'Human Resources',
                        border: InputBorder.none,
                        prefixIcon: Icon(Icons.nature_people),
                      ),
                    ),
                    _comp == Company.TWO
                        ? Divider(color: Colors.grey)
                        : SizedBox(),
                    _comp == Company.TWO
                        ? TextFormField(
                            decoration: InputDecoration(
                              labelText: 'Slave',
                              border: InputBorder.none,
                              prefixIcon: Icon(Icons.person_pin),
                            ),
                          )
                        : SizedBox(),
                  ],
                ),
              ),
              RaisedButton(
                child: Text('Switch'),
                onPressed: () {
                  setState(() {
                    _comp = _comp == Company.TWO ? Company.FOUR : Company.TWO;

                    containerHeight = _comp == Company.TWO ? 320 : 170;
                  });
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Solution

  • To animate widget size when its content changes, you can use AnimatedSize

    Container(
      padding: ...,
      decoration: ...,
      child: AnimatedSize(
        vsync: this,
        alignment: Alignment.topCenter,
        duration: Duration(seconds: 1),
        child: Column(
          children: [
            TextField(),
            TextField(),
            _comp == Company.TWO ? TextField() : SizedBox(),
            _comp == Company.TWO ? TextField() : SizedBox(),
            _comp == Company.TWO ? TextField() : SizedBox(),
            _comp == Company.TWO ? TextField() : SizedBox(),
          ],
        ),
      ),
    )
    

    It will infer size from child contents automatically, so you don't need to specify height anywhere.

    P.S. to make vsync: this work, add mixin to your state class:

    class _CompanyDemoState extends State<CompanyDemo> with SingleTickerProviderStateMixin {