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

构建横向滚动ListView失败,求助布局报错解决方案

横向滚动ListView布局报错问题解决

我在MainPage组件中尝试展示一个可填充后端返回数据的横向滚动ListView,但始终遇到布局报错。试过用Expanded包裹ListView.builder并添加shrinkWrap: true属性,但仍出现以下错误:

════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by scheduler library
═════════════════════════════════ Updated layout information required
for RenderFlex#c30cf relayoutBoundary=up11 NEEDS-LAYOUT NEEDS-PAINT to
calculate semantics. 'package:flutter/src/rendering/object.dart':
Failed assertion: line 3620 pos 12: '!_needsLayout'
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by gestures library
══════════════════════════════════ Cannot hit test a render box that
has never been laid out.
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by gestures library
══════════════════════════════════ Cannot hit test a render box that
has never been laid out.
════════════════════════════════════════════════════════════════════════════════


问题代码

ListView代码

Expanded(
  child: ListView.builder(
    shrinkWrap: true,
    scrollDirection: Axis.horizontal,
    itemCount: 15,
    itemBuilder: (BuildContext context, int index) => Card(
      child: Center(child: Text('Dummy Card Text')),
    ),
  ),
),

完整MainPage组件代码

class MainPage extends ConsumerWidget {
  const MainPage({super.key});

  @override
  Widget build(BuildContext context, ref) {
    // provider
    // final _data = ref.watch(roomDataProvider);

    final roomIDController = TextEditingController();
    // UI screen size
    Size size = MediaQuery.of(context).size;

    double deviceWidth = size.width;
    double deviceHeight = size.height;

    return Scaffold(
      backgroundColor: bluePrimary,
      body: SafeArea(
          child: SingleChildScrollView(
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 16),
          child: Column(
              //crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    IconButton(
                      iconSize: deviceWidth * 0.09,
                      icon: const Icon(Icons.person_outline,
                          color: orangePrimary),
                      onPressed: () {},
                    ),
                    IconButton(
                      icon: const Icon(
                        Icons.add,
                        color: orangePrimary,
                      ),
                      iconSize: deviceWidth * 0.09,
                      onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                              builder: (context) => const CreateRoomScreen()),
                        );
                      },
                    )
                  ],
                ),
                SizedBox(height: deviceHeight * 0.04),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Text("W",
                      style: TextStyle(
                          fontFamily: 'Chalet',
                          fontSize: deviceWidth * 0.12,
                          color: orangePrimary,
                          fontWeight: FontWeight.w300,
                          height: deviceHeight * 0.001)),
                ),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Text("W",
                      style: TextStyle(
                          fontFamily: 'Chalet',
                          fontSize: deviceWidth * 0.12,
                          color: whitePrimary,
                          fontWeight: FontWeight.w300,
                          height: deviceHeight * 0.001)),
                ),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Text("M",
                      style: TextStyle(
                          fontFamily: 'Chalet',
                          fontSize: deviceWidth * 0.12,
                          color: orangePrimary,
                          fontWeight: FontWeight.w300,
                          height: deviceHeight * 0.001)),
                ),
                SizedBox(height: deviceHeight * 0.04),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Text("Join room",
                      style: TextStyle(
                          fontFamily: 'Chalet',
                          fontSize: deviceWidth * 0.07,
                          color: whitePrimary,
                          fontWeight: FontWeight.w100,
                          height: deviceHeight * 0.001)),
                ),
                SizedBox(height: deviceHeight * 0.008),

                // email textField
                SizedBox(
                  width: MediaQuery.of(context).size.width * 0.85,
                  child: TextField(
                    controller: roomIDController,
                    decoration: InputDecoration(
                        filled: true,
                        fillColor: whitePrimary,
                        border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide.none),
                        hintText: 'Enter room ID to join it',
                        hintStyle: const TextStyle(
                            color: Color.fromARGB(255, 174, 173, 173))),
                  ),
                ),

                SizedBox(height: deviceHeight * 0.016),

                Align(
                  alignment: Alignment.bottomRight,
                  child: FloatingActionButton(
                    backgroundColor: orangePrimary,
                    child: const Icon(Icons.arrow_forward_ios_rounded,
                        color: whitePrimary),
                    onPressed: () {},
                  ),
                ),

                SizedBox(height: deviceHeight * 0.020),

                Align(
                  alignment: Alignment.centerLeft,
                  child: Text("My rooms",
                      style: TextStyle(
                          fontFamily: 'Chalet',
                          fontSize: deviceWidth * 0.07,
                          color: whitePrimary,
                          fontWeight: FontWeight.w100,
                          height: deviceHeight * 0.001)),
                ),

                SizedBox(height: deviceHeight * 0.014),

                Expanded(
                  child: ListView.builder(
                    shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    itemCount: 15,
                    itemBuilder: (BuildContext context, int index) => Card(
                      child: Center(child: Text('Dummy Card Text')),
                    ),
                  ),
                ),
              ]),
        ),
      )),
    );
  }
}

解决方法

问题根源在于SingleChildScrollView嵌套Column,同时Column内用Expanded包裹横向ListView的布局冲突:

  1. 横向ListView不需要Expanded:横向滚动的ListView需要明确的高度约束,但外层SingleChildScrollView会让Column的垂直空间变为无限,Expanded无法计算出合理的剩余空间,导致布局崩溃。
  2. 移除Expanded,给ListView设置固定高度(推荐):

修改后的ListView代码:

SizedBox(
  height: 120, // 根据UI需求设置合适高度
  child: ListView.builder(
    scrollDirection: Axis.horizontal,
    itemCount: 15,
    itemBuilder: (BuildContext context, int index) => Card(
      child: Center(child: Text('Dummy Card Text')),
    ),
  ),
),

如果希望高度自适应内容(不推荐,可能影响性能),可以用IntrinsicHeight包裹:

IntrinsicHeight(
  child: ListView.builder(
    scrollDirection: Axis.horizontal,
    shrinkWrap: true,
    itemCount: 15,
    itemBuilder: (BuildContext context, int index) => Card(
      child: Center(child: Text('Dummy Card Text')),
    ),
  ),
),

额外提示:不要在build方法内创建TextEditingController,建议用late final声明或在StatefulWidget的initState中初始化,避免组件重建时重复创建实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 02:35:57