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

Flutter如何将屏幕垂直分为2栏实现整页滚动,可否不使用ConstrainedBox?

示例图

现有写法的性能说明

你当前使用MediaQuery.of(context).size.height - AppBar().preferredSize.height作为约束的写法没有显著性能问题,仅存在两个适配隐患:

  • 没有自动扣除状态栏、底部系统导航栏的高度,横竖屏切换、有底部导航栏的场景下容易出现布局溢出
  • 系统配置变化(如字体大小调整、屏幕旋转)时MediaQuery会触发组件重建,但该操作开销极低,不属于性能问题

无需ConstrainedBox的实现方案

推荐使用Flutter官方适配的CustomScrollView + SliverFillRemaining组合实现需求,无需手动计算页面高度,适配性更强:

核心修改思路

  1. 替换原有的SingleChildScrollView + ConstrainedBox结构为CustomScrollView
  2. 上半部分固定内容放入SliverToBoxAdapter
  3. 下半部分需要撑满剩余高度的GridView放入SliverFillRemaining

修改后核心代码示例

body: RefreshIndicator(
  onRefresh: () async {
    print('testing');
  },
  child: CustomScrollView(
    physics: const BouncingScrollPhysics(
      parent: AlwaysScrollableScrollPhysics()
    ),
    slivers: [
      SliverToBoxAdapter(
        child: Padding(
          padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 24.0),
          child: Container(
            color: Colors.amber,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Text(
                  'teacher_photo_album_flow.listing_title'.tr(),
                  style: CustomTextStyle.pageSectionTitle(context),
                ),
                SizedBox(
                  height: 2 * SizeConfig.heightMultiplier,
                ),
                GestureDetector(
                  onTap: () {},
                  child: Column(
                    children: [
                      Container(
                        height: 25 * SizeConfig.heightMultiplier,
                        width: double.infinity,
                        child: ClipRRect(
                          borderRadius: BorderRadius.circular(8.0),
                          child: Image.network(
                            this.photoAlbums1[0]['album_cover'],
                            fit: BoxFit.cover,
                            width: MediaQuery.of(context).size.width,
                          ),
                        ),
                      ),
                      SizedBox(
                        height: 1.5 * SizeConfig.heightMultiplier,
                      ),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Expanded(
                            flex: 4,
                            child: Container(
                              alignment: Alignment.centerLeft,
                              child: Text(
                                this.photoAlbums1[0]['title'],
                                style: CustomTextStyle.titleOnelineEllipses(context),
                              ),
                            ),
                          ),
                          Expanded(
                            child: Container(
                              alignment: Alignment.centerRight,
                              child: Text(
                                this.photoAlbums1[0]['newDate'],
                                style: CustomTextStyle.silentTextDate(context),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 3 * SizeConfig.heightMultiplier,
                ),
              ],
            ),
          ),
        ),
      ),
      if (photoAlbums2.length > 0)
        SliverFillRemaining(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24.0),
            child: GridView.builder(
              shrinkWrap: true,
              physics: NeverScrollableScrollPhysics(),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisSpacing: 1,
                mainAxisSpacing: 1,
                crossAxisCount: 2
              ),
              itemCount: this.photoAlbums2.length,
              itemBuilder: (BuildContext ctx, i) {
                return GestureDetector(
                  onTap: () {},
                  child: Column(
                    children: [
                      Container(
                        height: 15 * SizeConfig.heightMultiplier,
                        width: double.infinity,
                        child: ClipRRect(
                          borderRadius: BorderRadius.circular(8.0),
                          child: Image.network(
                            this.photoAlbums2[i]['album_cover'],
                            fit: BoxFit.cover,
                            width: MediaQuery.of(context).size.width,
                          ),
                        ),
                      ),
                      SizedBox(
                        height: 1.0 * SizeConfig.heightMultiplier,
                      ),
                      Container(
                        alignment: Alignment.centerLeft,
                        child: Text(
                          this.photoAlbums2[i]['title'],
                          style: CustomTextStyle.titleOnelineEllipses(context),
                        ),
                      ),
                      SizedBox(
                        height: 0.5 * SizeConfig.heightMultiplier,
                      ),
                      Container(
                        alignment: Alignment.centerLeft,
                        child: Text(
                          this.photoAlbums2[i]['newDate'],
                          style: CustomTextStyle.silentTextDate(context),
                        ),
                      ),
                    ],
                  ),
                );
              }
            ),
          ),
        )
    ],
  ),
);

其他可选优化点

  • 若photoAlbums2数据量很大,可以直接把GridView替换为SliverGrid,不用嵌套SliverFillRemaining,渲染性能会更好
  • 空判断可以直接用Dart的collection if语法,不用三元运算符写SizedBox

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 08:06:05