IIW
IIW

Reputation: 11

How to use Flutter AnimatedContainer with height matching child

Currently this code works to resize a container to toggle between different content, but it's not animating. I think that I need to provide a height property to make the animation work, and when I do provide a height to toggle between it does match, like this:

height: selected ? 400 : 100,

The container animates smoothly between the two states. However the height is no longer adaptive. So I try to supply a height using:

GlobalKey _key = GlobalKey();

_size = _key.currentContext.size;

height: _size.height,

But it gives me errors and I'm not sure how to fix it.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  bool selected = false;
//  GlobalKey _key = GlobalKey();
//  Size _size = _key.currentContext.size;




  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: ListView(
              children: [
                GestureDetector(
                  onTap: () {
                    setState(() {
                      selected = !selected;
//                      _size = _key.currentContext.size;
                    });
                  },
                  child: Padding(
                    padding: EdgeInsets.all(5),
                    child: AnimatedContainer(
//                      height: _size.height,
//                      key: _key,
                      duration: Duration(seconds: 1),
                      curve: Curves.fastOutSlowIn,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(15),
                        color: Colors.white,
                        boxShadow: [
                          BoxShadow(
                            blurRadius: 2.5,
                            spreadRadius: 0.4,
                            color: Colors.grey,
                            offset: Offset(0, 0.5),
                          ),
                        ],
                      ),
                      child: Padding(
                        padding: EdgeInsets.all(17),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              'Move fridge up stairs',
                              style: TextStyle(fontSize: 16),
                            ),
                            SizedBox(height: 5),
                            Text(
                              'Sarah - 2 days ago - 2.3km',
                              style: TextStyle(color: Colors.black54),
                            ),
                            if (selected)
                              Container(
                                child: Column(
                                  children: [
                                    Padding(
                                      padding: EdgeInsets.fromLTRB(0, 20, 0, 20),
                                      child: Text(
                                        'Fridge is a single door. Sitting in kitchen. Need one strong person as I will help move it.',
                                        style: TextStyle(
                                          color: Colors.black54,
                                        ),
                                      ),
                                    ),
                                    Padding(
                                      padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
                                      child: Row(
                                        children: [
                                          Icon(
                                            Icons.calendar_today,
                                            color: Colors.black54,
                                            size: 20,
                                          ),
                                          Text(
                                            ' In three days',
                                            style: TextStyle(
                                              color: Colors.black54,
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                    Padding(
                                      padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
                                      child: Row(
                                        children: [
                                          Icon(
                                            Icons.attach_money,
                                            color: Colors.black54,
                                          ),
                                          Text(
                                            ' 30',
                                            style: TextStyle(
                                              color: Colors.black54,
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                    Row(
                                      children: [
                                        Text('Price : '),
                                        Container(
                                          width: 140,
                                          margin: EdgeInsets.fromLTRB(0, 0, 0, 0),
                                          decoration: BoxDecoration(
                                            borderRadius: BorderRadius.circular(20.0),
                                            color: Colors.white,
                                            border: Border.all(color: Colors.white),
                                          ),
                                          child: Container(
                                            child: TextField(
                                              decoration: new InputDecoration(
                                                  hintText: "Your price",
                                                  contentPadding: const EdgeInsets.all(10.0)),
                                              keyboardType: TextInputType.number,
                                              maxLines: null,
                                            ),
                                          ),
                                        ),
                                      ],
                                    ),
                                    Row(
                                      children: [
                                        Text('Reply : '),
                                        Expanded(
                                          child: TextField(
                                            decoration: new InputDecoration(
                                                hintText: "Enter your reply",
                                                contentPadding: const EdgeInsets.all(10.0)),
                                            keyboardType: TextInputType.multiline,
                                            maxLines: null,
                                          ),
                                        ),
                                      ],
                                    ),
                                    SizedBox(height:20),
                                    Row(
                                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                      children: [
                                        SizedBox(
                                          width: 10,
                                        ),
                                        FlatButton(
                                          shape: RoundedRectangleBorder(
                                            side: BorderSide(
                                                color: Colors.blue, width: 1, style: BorderStyle.solid),
                                            borderRadius: BorderRadius.circular(50),
                                          ),
                                          color: Colors.white,
                                          textColor: Colors.black,
                                          onPressed: () {
                                            /*...*/
                                          },
                                          child: Padding(
                                            padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
                                            child: Text(
                                              "Reply",
                                            ),
                                          ),
                                        ),
                                      ],
                                    )
                                  ],
                                ),
                              ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      )

    );
  }
}

Upvotes: 1

Views: 2090

Answers (2)

Martin
Martin

Reputation: 121

You can use custom animation to do this, like this:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  bool selected = false;
  GlobalKey _key1 = GlobalKey();
  GlobalKey _key2 = GlobalKey();
  AnimationController _controller;
  Animation _animation;
  bool foward = false;
  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        _animation = Tween<double>(
                begin: _key1.currentContext.size.height,
                end: _key2.currentContext.size.height +
                    _key1.currentContext.size.height)
            .chain(CurveTween(curve: Curves.fastOutSlowIn))
            .animate(_controller);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SafeArea(
            child: ListView(
      children: [
        Padding(
          padding: EdgeInsets.all(5),
          child: GestureDetector(
            onTap: () {
              if (foward) {
                _controller.reverse();
              } else {
                _controller.forward();
              }
              foward = !foward;
            },
            child: (_animation == null)
                ? _buildWidget(_key1, _key2)
                : AnimatedBuilder(
                    // curve: Curves.fastOutSlowIn,
                    animation: _controller,
                    builder: (context, child) {
                      return Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(15),
                            color: Colors.white,
                            boxShadow: [
                              BoxShadow(
                                blurRadius: 2.5,
                                spreadRadius: 0.4,
                                color: Colors.grey,
                                offset: Offset(0, 0.5),
                              ),
                            ],
                          ),
                          child: Padding(
                            padding: EdgeInsets.all(17.0),
                            child: Container(
                                height: _animation.value, child: child),
                          ));
                    },
                    child: _buildWidget(_key1, _key2)),
          ),
        ),
      ],
    )));
  }

  Widget _buildWidget(Key key1, Key key2) {
    return SingleChildScrollView(
      physics: const NeverScrollableScrollPhysics(),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Widget1(
            key: _key1,
          ),
          Widget2(
            key: _key2,
          ),
        ],
      ),
    );
  }
}

class Widget1 extends StatefulWidget {
  Widget1({Key key}) : super(key: key);

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

class _Widget1State extends State<Widget1> {
  @override
  Widget build(BuildContext context) {
    return Column(mainAxisSize: MainAxisSize.min, children: [
      Text(
        'Move fridge up stairs',
        style: TextStyle(fontSize: 16),
      ),
      SizedBox(height: 5),
      Text(
        'Sarah - 2 days ago - 2.3km',
        style: TextStyle(color: Colors.black54),
      ),
    ]);
  }
}

class Widget2 extends StatefulWidget {
  Widget2({Key key}) : super(key: key);

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

class Widget2State extends State<Widget2> {
  @override
  Widget build(BuildContext context) {
    return Column(mainAxisSize: MainAxisSize.min, children: [
      Container(
          child: Column(children: [
        Padding(
          padding: EdgeInsets.fromLTRB(0, 20, 0, 20),
          child: Text(
            'Fridge is a single door. Sitting in kitchen. Need one strong person as I will help move it.',
            style: TextStyle(
              color: Colors.black54,
            ),
          ),
        ),
        Padding(
          padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
          child: Row(
            children: [
              Icon(
                Icons.calendar_today,
                color: Colors.black54,
                size: 20,
              ),
              Text(
                ' In three days',
                style: TextStyle(
                  color: Colors.black54,
                ),
              ),
            ],
          ),
        ),
        Padding(
          padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
          child: Row(
            children: [
              Icon(
                Icons.attach_money,
                color: Colors.black54,
              ),
              Text(
                ' 30',
                style: TextStyle(
                  color: Colors.black54,
                ),
              ),
            ],
          ),
        ),
        Row(
          children: [
            Text('Price : '),
            Container(
              width: 140,
              margin: EdgeInsets.fromLTRB(0, 0, 0, 0),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(20.0),
                color: Colors.white,
                border: Border.all(color: Colors.white),
              ),
              child: Container(
                child: TextField(
                  decoration: new InputDecoration(
                      hintText: "Your price",
                      contentPadding: const EdgeInsets.all(10.0)),
                  keyboardType: TextInputType.number,
                  maxLines: null,
                ),
              ),
            ),
          ],
        ),
        Row(
          children: [
            Text('Reply : '),
            Expanded(
              child: TextField(
                decoration: new InputDecoration(
                    hintText: "Enter your reply",
                    contentPadding: const EdgeInsets.all(10.0)),
                keyboardType: TextInputType.multiline,
                maxLines: null,
              ),
            ),
          ],
        ),
        SizedBox(height: 20),
        Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
          SizedBox(
            width: 10,
          ),
          FlatButton(
              shape: RoundedRectangleBorder(
                side: BorderSide(
                    color: Colors.blue, width: 1, style: BorderStyle.solid),
                borderRadius: BorderRadius.circular(50),
              ),
              color: Colors.white,
              textColor: Colors.black,
              onPressed: () {
                /*...*/
              },
              child: Padding(
                padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
                child: Text(
                  "Reply",
                ),
              ))
        ])
      ]))
    ]);
  }
}

Upvotes: 0

Payam Zahedi
Payam Zahedi

Reputation: 855

I thing AnimatedContainer is not Appropriate for this situation. I think its better to use AnimatedCrossFade. The AnimatedContainer will automatically animate between the old and new values of properties when they change using the provided curve and duration.

following code is refactored for using AnimatedCrossFade:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool selected = false;
//  GlobalKey _key = GlobalKey();
//  Size _size = _key.currentContext.size;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
      children: [
        Expanded(
          child: ListView(
            children: [
              GestureDetector(
                onTap: () {
                  setState(() {
                    selected = !selected;
//                      _size = _key.currentContext.size;
                  });
                },
                child: Padding(
                  padding: EdgeInsets.all(5),
                  child: AnimatedCrossFade(
                    duration: Duration(seconds: 1),
                    crossFadeState: selected
                        ? CrossFadeState.showFirst
                        : CrossFadeState.showSecond,
                    firstChild: Container(
//                        height: !selected ? 100 : 400,
//                      key: _key,
                      width: double.infinity,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(15),
                        color: Colors.white,
                        boxShadow: [
                          BoxShadow(
                            blurRadius: 2.5,
                            spreadRadius: 0.4,
                            color: Colors.grey,
                            offset: Offset(0, 0.5),
                          ),
                        ],
                      ),
                      child: Padding(
                        padding: EdgeInsets.all(17),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              'Move fridge up stairs',
                              style: TextStyle(fontSize: 16),
                            ),
                            SizedBox(height: 5),
                            Text(
                              'Sarah - 2 days ago - 2.3km',
                              style: TextStyle(color: Colors.black54),
                            ),
                            if (selected)
                              Container(
                                child: Column(
                                  children: [
                                    Padding(
                                      padding:
                                          EdgeInsets.fromLTRB(0, 20, 0, 20),
                                      child: Text(
                                        'Fridge is a single door. Sitting in kitchen. Need one strong person as I will help move it.',
                                        style: TextStyle(
                                          color: Colors.black54,
                                        ),
                                      ),
                                    ),
                                    Padding(
                                      padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
                                      child: Row(
                                        children: [
                                          Icon(
                                            Icons.calendar_today,
                                            color: Colors.black54,
                                            size: 20,
                                          ),
                                          Text(
                                            ' In three days',
                                            style: TextStyle(
                                              color: Colors.black54,
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                    Padding(
                                      padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
                                      child: Row(
                                        children: [
                                          Icon(
                                            Icons.attach_money,
                                            color: Colors.black54,
                                          ),
                                          Text(
                                            ' 30',
                                            style: TextStyle(
                                              color: Colors.black54,
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                    Row(
                                      children: [
                                        Text('Price : '),
                                        Container(
                                          width: 140,
                                          margin:
                                              EdgeInsets.fromLTRB(0, 0, 0, 0),
                                          decoration: BoxDecoration(
                                            borderRadius:
                                                BorderRadius.circular(20.0),
                                            color: Colors.white,
                                            border:
                                                Border.all(color: Colors.white),
                                          ),
                                          child: Container(
                                            child: TextField(
                                              decoration: new InputDecoration(
                                                  hintText: "Your price",
                                                  contentPadding:
                                                      const EdgeInsets.all(
                                                          10.0)),
                                              keyboardType:
                                                  TextInputType.number,
                                              maxLines: null,
                                            ),
                                          ),
                                        ),
                                      ],
                                    ),
                                    Row(
                                      children: [
                                        Text('Reply : '),
                                        Expanded(
                                          child: TextField(
                                            decoration: new InputDecoration(
                                                hintText: "Enter your reply",
                                                contentPadding:
                                                    const EdgeInsets.all(10.0)),
                                            keyboardType:
                                                TextInputType.multiline,
                                            maxLines: null,
                                          ),
                                        ),
                                      ],
                                    ),
                                    SizedBox(height: 20),
                                    Row(
                                      mainAxisAlignment:
                                          MainAxisAlignment.spaceBetween,
                                      children: [
                                        SizedBox(
                                          width: 10,
                                        ),
                                        FlatButton(
                                          shape: RoundedRectangleBorder(
                                            side: BorderSide(
                                                color: Colors.blue,
                                                width: 1,
                                                style: BorderStyle.solid),
                                            borderRadius:
                                                BorderRadius.circular(50),
                                          ),
                                          color: Colors.white,
                                          textColor: Colors.black,
                                          onPressed: () {
                                            /*...*/
                                          },
                                          child: Padding(
                                            padding: EdgeInsets.fromLTRB(
                                                0, 10, 0, 10),
                                            child: Text(
                                              "Reply",
                                            ),
                                          ),
                                        ),
                                      ],
                                    )
                                  ],
                                ),
                              ),
                          ],
                        ),
                      ),
                    ),
                    secondChild: Container(
//                        height: !selected ? 100 : 400,
//                      key: _key,
                                            width: double.infinity,

                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(15),
                        color: Colors.white,
                        boxShadow: [
                          BoxShadow(
                            blurRadius: 2.5,
                            spreadRadius: 0.4,
                            color: Colors.grey,
                            offset: Offset(0, 0.5),
                          ),
                        ],
                      ),
                      child: Padding(
                        padding: EdgeInsets.all(17),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              'Move fridge up stairs',
                              style: TextStyle(fontSize: 16),
                            ),
                            SizedBox(height: 5),
                            Text(
                              'Sarah - 2 days ago - 2.3km',
                              style: TextStyle(color: Colors.black54),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ],
    ));
  }
}

Upvotes: 2

Related Questions