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

Flutter showModalBottomSheet结合ListView.builder动态高度不生效问题

Flutter 自适应高度BottomSheet实现方案

问题背景

  • 实现目标:开发可根据内容尺寸自适应高度的BottomSheet,组件嵌套结构为Container -> Column -> ListView.builder
  • 预期交互:
    • 列表数据少于10条时,BottomSheet完全适配内容高度
    • 列表数据多于10条时,BottomSheet展示固定默认高度,超出部分内容支持滚动查看
  • 已尝试但未生效的方案:
    • 为showModalBottomSheet配置isScrollControlled: true属性
    • 为ListView配置shrinkWrap: true属性
    • 嵌套Wrap组件适配布局
      注:不使用ListView仅使用Wrap组件时,自适应高度功能可正常运行

问题根因

原有代码存在4个核心布局错误:

  1. 在SingleChildScrollView内部嵌套Expanded:Expanded会强制子组件撑满剩余可用空间,与滚动视图自适应内容的布局逻辑冲突,会触发布局溢出异常
  2. Column未设置mainAxisSize属性:Column默认mainAxisSize: MainAxisSize.max,会强制撑满父组件最大可用高度,无法跟随内容收缩
  3. 未给长列表场景设置高度约束:列表项过多时无高度上限,要么撑满全屏要么出现布局溢出,也无法触发ListView自身滚动
  4. 外层多余的SingleChildScrollView会和内部ListView产生滚动冲突,干扰高度计算

修复步骤

  1. 开启showModalBottomSheet的isScrollControlled: true配置,授予弹窗完整高度访问权限
  2. 移除外层多余的SingleChildScrollView和错误的Expanded嵌套,Column直接作为Padding的子组件
  3. 给Column设置mainAxisSize: MainAxisSize.min,让列高度跟随内容自动收缩
  4. 增加列表长度判断逻辑:
    • 列表项≤10条时:给ListView.builder配置shrinkWrap: true + physics: const NeverScrollableScrollPhysics(),让列表高度完全适配内容,禁止列表自身滚动,整体高度由内容决定
    • 列表项>10条时:用ConstrainedBox包裹列表,设置最大高度阈值(可按需调整,示例为屏幕高度的60%),此时ListView自动接管滚动,展示超出部分内容
  5. 给ListView配置padding: EdgeInsets.zero,移除ListView默认内边距,避免高度计算偏差

修正后完整代码

class BottomStringlistController {
  var selectedValue = "";
  void showSheet(BuildContext context, List<String> values, String currentTitle, {required Function(String value) onCompletion}){
    var bottomSheet = showModalBottomSheet<void>(
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      context: context,
      builder: (BuildContext context) {
        return Container(
          decoration: const BoxDecoration(
            color: Colors.white,
            borderRadius:  BorderRadius.all(
              Radius.circular(15.0),
            ),
          ),
          child: Padding(
            padding: const EdgeInsets.all(15.0),
            child: Column(
              // 核心配置:让Column高度跟随内容收缩
              mainAxisSize: MainAxisSize.min,
              children: [
                Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Text(
                      currentTitle.toUpperCase(),
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        fontSize: getProportionateScreenWidth(14),
                        fontWeight: FontWeight.bold,
                        color: kPrimaryColor,
                      ),
                    ),
                    const Spacer(),
                    IconButton(
                      onPressed:(){
                        Navigator.pop(context);
                      },
                      icon: const Icon(Icons.close,color: kPrimaryColor,),
                    ),
                  ],
                ),
                const Divider(color: kPrimaryColor,),
                SizedBox(height: getProportionateScreenWidth(15),),
                // 根据列表长度切换布局模式
                values.length <= 10 
                ? ListView.builder(
                    shrinkWrap: true,
                    // 短列表禁止自身滚动,高度完全适配内容
                    physics: const NeverScrollableScrollPhysics(),
                    padding: EdgeInsets.zero,
                    itemCount: values.length,
                    itemBuilder: (context, index){
                      return GestureDetector(
                        onTap: (){
                          selectedValue = values[index];
                          Navigator.pop(context);
                        },
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(values[index].toUpperCase(),
                              textAlign: TextAlign.start,
                              style: TextStyle(
                                  fontSize: getProportionateScreenWidth(12),
                                  fontWeight: FontWeight.normal,
                                  color: Colors.black
                              ),
                            ),
                            const SizedBox(height: 10),
                          ],
                        ),
                      );
                    },
                  )
                : ConstrainedBox(
                    // 长列表设置最大高度,超出部分滚动
                    constraints: BoxConstraints(
                      maxHeight: MediaQuery.of(context).size.height * 0.6,
                    ),
                    child: ListView.builder(
                      padding: EdgeInsets.zero,
                      itemCount: values.length,
                      itemBuilder: (context, index){
                        return GestureDetector(
                          onTap: (){
                            selectedValue = values[index];
                            Navigator.pop(context);
                          },
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(values[index].toUpperCase(),
                                textAlign: TextAlign.start,
                                style: TextStyle(
                                    fontSize: getProportionateScreenWidth(12),
                                    fontWeight: FontWeight.normal,
                                    color: Colors.black
                                ),
                              ),
                              const SizedBox(height: 10),
                            ],
                          ),
                        );
                      },
                    ),
                  ),
              ],
            ),
          ),
        );
      },
    ).whenComplete(() {
      onCompletion(selectedValue);
    });
  }
}

提示:长列表的最大高度可根据业务需求自行调整maxHeight的数值,不需要固定为屏幕高度的60%

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 09:54:29