You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AnimatedContainer高度动画时抛出异常问题求助

问题解决:AnimatedContainer展开动态列表时的动画异常

问题原因

你遇到的异常是因为AnimatedContainer无法在动画过程中使用null作为高度值。当设置height: isExpanded ? null : 0时,动画过程中容器高度从0向自适应高度过渡,但此时内部的ListView.builder无法在动态变化的高度中正确布局——ListView需要明确的约束,而动画中的不确定高度会触发布局溢出或约束异常。

解决方案

核心思路是预先计算展开后的容器总高度,让AnimatedContainer在0和这个固定计算值之间做动画,既保留自适应效果,又避免动态高度带来的布局冲突。

修改后的代码

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;
  late double _expandedHeight;

  // 每个订单项的高度(根据你的UI调整)
  final double _itemHeight = 40;
  // AnimatedContainer的上下padding总和
  final double _paddingVertical = 22;

  @override
  void initState() {
    super.initState();
    // 计算展开后的总高度:订单项数量*单项高度 + 上下padding
    _expandedHeight = widget.order.orderList.length * _itemHeight + _paddingVertical;
  }

  @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: const Duration(milliseconds: 500),
            curve: Curves.easeOutBack,
            padding: const EdgeInsets.only(top: 10, right: 15, left: 15, bottom: 12),
            // 用预计算的高度代替null
            height: isExpanded ? _expandedHeight : 0,
            width: double.infinity,
            color: Colors.grey[300],
            // 添加clipBehavior避免动画过程中子widget溢出
            clipBehavior: Clip.hardEdge,
            child: ListView.builder(
              shrinkWrap: true,
              // 禁用滚动,因为高度已经固定,不需要滚动
              physics: const NeverScrollableScrollPhysics(),
              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,
            ),
          ),
        ],
      ),
    );
  }
}

关键修改点

  1. 预计算高度:在initState中根据订单条目数量、单项高度和容器padding,算出展开后的总高度_expandedHeight
  2. 固定动画高度:AnimatedContainer的height使用_expandedHeight代替null,让动画在0和固定值之间过渡
  3. 优化ListView:添加physics: const NeverScrollableScrollPhysics(),因为高度已经适配所有条目,不需要滚动;添加clipBehavior: Clip.hardEdge避免动画过程中子元素溢出显示

进阶优化(可选)

如果想要更精确的高度计算(比如考虑文本换行、不同字体大小的情况),可以用TextPainter计算每个列表项的实际高度,或者在LayoutBuilder中动态获取约束,但对于订单列表这种布局固定的场景,固定单项高度已经足够稳定。

内容的提问来源于stack exchange,提问作者adi99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:15:40