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

Flutter ListView自定义卡片Text overflow布局溢出问题求解

Flutter ExpansionPanelList头部文本溢出问题修复

问题复现

开发Flutter应用时,在ExpansionPanelList(可折叠列表,和ListView布局逻辑一致)中实现自定义卡片头部,渲染时出现Text横向溢出报错,直接使用Expanded包裹溢出的Text节点会触发布局约束相关异常。
附异常运行效果:
文本溢出报错运行截图
附原始问题代码:

import 'package:flutter/material.dart';
import '../../shared/AppTheme.dart';

class ComandaScreen extends StatefulWidget {
  const ComandaScreen({Key? key}) : super(key: key);

  @override
  State<ComandaScreen> createState() => _ComandaScreenState();
}

class _ComandaScreenState extends State<ComandaScreen> {
  bool expanded = false;
  int unidades = 0;

  final List<Map<String, dynamic>> _items = List.generate(
      10, (index) => {'id': index, 'Nombre': 'Nuggets $index',
      'isExpanded': false, "unidades": 8});

  @override
  Widget build(BuildContext context) {
    final ButtonStyle flatButtonStyle = TextButton.styleFrom(
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
    );

    return Scaffold(
      appBar: AppBar(
        title: const Text('Comanda'),
        backgroundColor: AppTheme.backgroundColor,
        foregroundColor: AppTheme.primaryTextColor,
        elevation: 0,
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          elevation: 3,
          expansionCallback: (index, isExpanded) {
            setState(() {
              _items[index]['isExpanded'] = !isExpanded;
            });
          },
          animationDuration: const Duration(milliseconds: 200),
          children: _items
              .map(
                (item) => ExpansionPanel(
                  canTapOnHeader: true,
                  headerBuilder: (context, isExpanded) {
                    return Container(
                      margin: const EdgeInsets.all(10),
                      child: Row(children: [
                        const CircleAvatar(
                            child: Text(
                              '1',
                              textAlign: TextAlign.center,
                            )),
                        const SizedBox(
                          width: 10,
                        ),
                        Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              const Text(
                                'Nuggets',
                                style: TextStyle(color: Colors.black, fontWeight: FontWeight.w600),
                              ),
                              Row(
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                children: [
                                  Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    mainAxisAlignment: MainAxisAlignment.start,
                                    children: const [
                                      Text(
                                        'Unidades: ${7}',
                                        style: TextStyle(color: Colors.black),
                                      ),
                                      Text(
                                        'Pendientes: 400',
                                        style: TextStyle(color: Colors.black),
                                      ),
                                    ],
                                  ),
                                  const SizedBox(
                                    width: 20,
                                  ),
                                  Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    children: const [
                                      Text(
                                        'Precio: 10 €',
                                        maxLines: 1,
                                        overflow: TextOverflow.ellipsis,
                                        style: TextStyle(color: Colors.black),
                                      ),
                                      Text(
                                        'Total: 70 €',
                                        maxLines: 1,
                                        overflow: TextOverflow.ellipsis,
                                        style: TextStyle(color: Colors.black),
                                      ),
                                    ],
                                  ),
                                ],
                              ),
                            ],
                          ),
                        ),
                      ]),
                    );
                  },
                  body: ButtonBar(
                    alignment: MainAxisAlignment.spaceAround,
                    buttonHeight: 52.0,
                    buttonMinWidth: 90.0,
                    children: <Widget>[
                      TextButton(
                        style: flatButtonStyle,
                        onPressed: () {
                          setState(() {
                            item['unidades'] += 1;
                          });
                        },
                        child: Column(
                          children: const <Widget>[
                            Icon(
                              Icons.add,
                              color: AppTheme.grismedio,
                            ),
                          ],
                        ),
                      ),
                      TextButton(
                        style: flatButtonStyle,
                        onPressed: () {
                          setState(() {
                            item['unidades'] -= 1;
                          });
                        },
                        child: Column(
                          children: const <Widget>[
                            Icon(
                              Icons.remove,
                              color: AppTheme.grismedio,
                            ),
                          ],
                        ),
                      ),
                      TextButton(
                        style: flatButtonStyle,
                        onPressed: () {},
                        child: Column(
                          children: const <Widget>[
                            Icon(
                              Icons.edit_outlined,
                              color: AppTheme.grismedio,
                            ),
                          ],
                        ),
                      ),
                      TextButton(
                        style: flatButtonStyle,
                        onPressed: () {},
                        child: Column(
                          children: const <Widget>[
                            Icon(
                              Icons.delete_outline_outlined,
                              color: AppTheme.grismedio,
                            ),
                          ],
                        ),
                      ),
                      TextButton(
                        style: flatButtonStyle,
                        onPressed: () {},
                        child: Column(
                          children: const <Widget>[
                            Icon(
                              Icons.card_giftcard_outlined,
                              color: AppTheme.grismedio,
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                  isExpanded: item['isExpanded'],
                ),
              )
              .toList(),
        ),
      ),
    );
  }
}

问题根因

  • 嵌套Row未做宽度约束:头部外层Row已经用Expanded包裹右侧内容区,但内容区内部嵌套的第二层Row的两个子Column没有拿到明确的横向宽度上限,内部Text渲染时按无界宽度计算,触发横向溢出。
  • Expanded使用层级错误:Expanded必须作为Row/Column/Flex等Flex组件的直接子节点使用,直接包裹深层嵌套的Text节点会触发布局约束异常。

修复方案

  • 给内层Row的两个并列Column分别添加Expanded包裹,按1:1比例分配剩余横向宽度,让内部Text拿到明确的可用宽度约束。
  • 移除内层Row中固定宽度的SizedBox占位,通过调整右侧Column的对齐方式为CrossAxisAlignment.end实现两端对齐的视觉效果,适配不同屏幕宽度。
  • 所有Text组件统一配置maxLines: 1和overflow: TextOverflow.ellipsis,文本过长时自动显示省略号,避免撑破布局。

修复后核心代码(替换原headerBuilder中内层Row部分即可)

Row(
  children: [
    Expanded(
      flex: 1,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: const [
          Text(
            'Unidades: 7',
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(color: Colors.black),
          ),
          Text(
            'Pendientes: 400',
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(color: Colors.black),
          ),
        ],
      ),
    ),
    Expanded(
      flex: 1,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        children: const [
          Text(
            'Precio: 10 €',
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(color: Colors.black),
          ),
          Text(
            'Total: 70 €',
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(color: Colors.black),
          ),
        ],
      ),
    ),
  ],
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:36:22