Solved: AnimatedContainer throws error when animating its height

Question

Asked by adi99 on September 20, 2022 (source).

I am trying to have a list of card which contains a ListTile and an AnimatedContainer (containing a dynamic list) which will expand when the user clicks on the ListTile.

This is how the list looks initially: initial

When the user clicks on one, it will expand the animatedContainer like so: expanded

However, during the animation, this happens: errordemo

This is the error message from above gif: errorMessage

The error last for as long as the duration i have set for the AnimatedContainer. I can fix this issue by simply stating a static height on the AnimatedContainer, however, I need the AnimatedContainer to have dynamic height depending on how many items are in the ListView.

Below is my code snippet, any help would be greatly appreciated.

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:shop_shop/providers/orders_provider.dart';

class OrdersScreen extends StatelessWidget {
  static const routeName = 'order';

  @override
  Widget build(BuildContext context) {
    final ordersProvider = Provider.of<OrdersProvider>(context);
    final List<OrderItem> orderList = ordersProvider.orderList;

    return Scaffold(
      appBar: AppBar(
        title: Text('Order History'),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemBuilder: (ctx, i) {
                OrderItem order = orderList[i];
                return OrderCard(order: order);
              },
              itemCount: orderList.length,
            ),
          ),
        ],
      ),
    );
  }
}

class OrderCard extends StatefulWidget {
  final OrderItem order;

  const OrderCard({required this.order});

  @override
  State<OrderCard> createState() => _OrderCardState();
}

class _OrderCardState extends State<OrderCard> {
  bool isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          ListTile(
            subtitle: Text(
              '\$${widget.order.totalPrice.toStringAsFixed(2)}',
            ),
            title: Text(
              DateFormat.yMMMMEEEEd().format(widget.order.orderDate),
            ),
            onTap: () {
              setState(() {
                isExpanded = !isExpanded;
              });
            },
          ),
          AnimatedContainer(
            duration: Duration(milliseconds: 500),
            curve: Curves.easeOutBack,
            padding: EdgeInsets.only(top: 10, right: 15, left: 15, bottom: 12),
            height: isExpanded ? null : 0,
            width: double.infinity,
            color: Colors.grey[300],
            child: ListView.builder(
              shrinkWrap: true,
              itemBuilder: (ctx, i) => Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(widget.order.orderList[i].title),
                  Text('x${widget.order.orderList[i].quantity}'),
                  Text(widget.order.orderList[i].price.toStringAsFixed(2)),
                ],
              ),
              itemCount: widget.order.orderList.length,
            ),
          ),
        ],
      ),
    );
  }
}


Answer

Question answered by adi99 (source).

I've found a work-around solution by manually calculating height based on the length of the list, this way i don't need shrinkWrap, like so (see property height in AnimatedContainer):

         AnimatedContainer(
            duration: Duration(milliseconds: 300),
            curve: Curves.linearToEaseOut,
            padding: EdgeInsets.only(top: 10, right: 15, left: 15, bottom: 10),
            height: isExpanded
                ? widget.order.orderList.length * 17.5 + 20 // <==== dynamic height
                : 0,
            width: double.infinity,
            color: Colors.grey[300],
            child: ListView.builder(
              itemBuilder: (ctx, i) => Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(widget.order.orderList[i].title),
                  Text('x${widget.order.orderList[i].quantity}'),
                  Text(widget.order.orderList[i].price.toStringAsFixed(2)),
                ],
              ),
              itemCount: widget.order.orderList.length,
            ),
          ),

Please feel free to post another answer if you found a more proper way, thanks!

DART FLUTTER FRONTEND USER-INTERFACE
SHARE: