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

如何在SingleChildScrollView中使用非滚动的GridView

解决方案

要让外层SingleChildScrollView整体滚动,同时让GridView完整显示所有子项且自身不可滚动,你需要调整以下核心设置:

  • 移除包裹GridView的固定高度SizedBox,固定高度会强制限制GridView的显示空间,导致子项被截断
  • 保留shrinkWrap: true,让GridView根据子项总高度自动适配自身高度
  • 保留physics: NeverScrollableScrollPhysics(),禁用GridView自身的滚动行为

修改后的关键代码片段

Align(
  alignment: const AlignmentDirectional(-1, 0),
  child: Padding(
    padding: const EdgeInsetsDirectional.fromSTEB(16, 16, 0, 0),
    child: Text(
      'POPULAR SERVICES',
      style: Theme.of(context).textTheme.titleSmall?.copyWith(
        fontFamily: 'Readex Pro',
        letterSpacing: 0.0,
      ),
    ),
  ),
),
// 移除固定高度的SizedBox,让GridView自适应高度
GridView(
  physics: const NeverScrollableScrollPhysics(),
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3,
      crossAxisSpacing: 10,
      mainAxisSpacing: 10,
      childAspectRatio: 1
  ),
  padding: EdgeInsets.zero,
  shrinkWrap: true,
  scrollDirection: Axis.vertical,
  children: [
    GridCard(iconData: Icons.fire_truck_outlined, text: "Fire Stations"),
    GridCard(iconData: Icons.restore_sharp, text: "Logo"),
    GridCard(iconData: Icons.content_paste_outlined, text: "FWI"),
    GridCard(iconData: Icons.location_on_outlined, text: "Location Tracker"),
    GridCard(iconData: Icons.keyboard_control_sharp, text: "More",),
  ],
),

完整修改后的页面代码

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:forest_fire/gridcard.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  TextEditingController textEditingController = TextEditingController();
  FocusNode focusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          top: true,
          child: Padding(
            padding: EdgeInsetsDirectional.fromSTEB(8, 0, 8, 0),
            child: SingleChildScrollView(
              child: Column(
                mainAxisSize: MainAxisSize.max,
                children: [
                  Padding(
                    padding: EdgeInsetsDirectional.fromSTEB(8.0, 0.0, 8.0, 0.0),
                    child: TextFormField(
                      controller: textEditingController,
                      focusNode: focusNode,
                      autofocus: true,
                      obscureText: false,
                      decoration: InputDecoration(
                          labelText: "Services",
                          labelStyle: Theme.of(context).textTheme.labelMedium?.copyWith(
                            fontFamily: 'Plus Jakarta Sans',
                            color: const Color(0xFFA5A5A5),
                            letterSpacing: 0.0,
                            fontWeight: FontWeight.bold,
                          ),
                          hintStyle: Theme.of(context).textTheme.labelMedium?.copyWith(
                            fontFamily: 'Readex Pro',
                            letterSpacing: 0.0,
                          ),
                          enabledBorder: OutlineInputBorder(
                              borderSide: const BorderSide(
                                color: Color(0xFF696969),
                                width: 2.0,
                              ),
                              borderRadius: BorderRadius.circular(24)
                          ),
                          errorBorder: OutlineInputBorder(
                              borderSide: BorderSide(
                                  color: Theme.of(context).colorScheme.error,
                                  width: 2.0
                              ),
                              borderRadius: BorderRadius.circular(24)
                          ),
                          focusedErrorBorder: OutlineInputBorder(
                              borderSide: BorderSide(
                                  color: Theme.of(context).colorScheme.error,
                                  width: 2.0
                              ),
                              borderRadius: BorderRadius.circular(24)
                          ),
                          prefixIcon: const Icon(
                              Icons.search
                          ),
                          suffixIcon: const Icon(
                              Icons.mic_none
                          )
                      ),
                      style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                        fontFamily: 'Readex Pro',
                        letterSpacing: 0.0,
                      ),
                    ),
                  ),
                  Flexible(
                      child: Padding(
                        padding: const EdgeInsetsDirectional.fromSTEB(0.0, 16.0, 0.0, 0.0),
                        child: ClipRRect(
                          borderRadius: BorderRadius.circular(8),
                          child: Image.asset(
                            'assets/images/download_1.png',
                            width: 360,
                            height: 160,
                            fit: BoxFit.cover,
                          ),
                        ),
                      )
                  ),
                  Align(
                    alignment: const AlignmentDirectional(-1, 0),
                    child: Padding(
                      padding: const EdgeInsetsDirectional.fromSTEB(16, 16, 0, 0),
                      child: Text(
                        'POPULAR SERVICES',
                        style: Theme.of(context).textTheme.titleSmall?.copyWith(
                          fontFamily: 'Readex Pro',
                          letterSpacing: 0.0,
                        ),
                      ),
                    ),
                  ),
                  GridView(
                    physics: const NeverScrollableScrollPhysics(),
                    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: 3,
                        crossAxisSpacing: 10,
                        mainAxisSpacing: 10,
                        childAspectRatio: 1
                    ),
                    padding: EdgeInsets.zero,
                    shrinkWrap: true,
                    scrollDirection: Axis.vertical,
                    children: [
                      GridCard(iconData: Icons.fire_truck_outlined, text: "Fire Stations"),
                      GridCard(iconData: Icons.restore_sharp, text: "Logo"),
                      GridCard(iconData: Icons.content_paste_outlined, text: "FWI"),
                      GridCard(iconData: Icons.location_on_outlined, text: "Location Tracker"),
                      GridCard(iconData: Icons.keyboard_control_sharp, text: "More",),
                    ],
                  ),
                  Align(
                    alignment: const AlignmentDirectional(-1.0, 0.0),
                    child: Padding(
                      padding:
                      const EdgeInsetsDirectional.fromSTEB(16.0, 16.0, 0.0, 0.0),
                      child: Text(
                        'SERVICE CATEGORIES',
                        style: Theme.of(context).textTheme.titleSmall?.copyWith(
                          fontFamily: 'Readex Pro',
                          color: Theme.of(context).colorScheme.onPrimary,
                          letterSpacing: 0.0,
                        ),
                      ),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsetsDirectional.fromSTEB(0.0, 8.0, 0.0, 8.0),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(8.0),
                      child: Image.asset(
                        'assets/images/video_1.png',
                        width: 347.0,
                        height: 68.0,
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsetsDirectional.fromSTEB(0.0, 8.0, 0.0, 8.0),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(8.0),
                      child: Image.asset(
                        'assets/images/video_1.png',
                        width: 347.0,
                        height: 68.0,
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsetsDirectional.fromSTEB(0.0, 8.0, 0.0, 8.0),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(8.0),
                      child: Image.asset(
                        'assets/images/video_1.png',
                        width: 347.0,
                        height: 68.0,
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          )
      ),
    );
  }
}

原理说明

  • shrinkWrap: true:让GridView仅占用子项所需的最小高度,而非试图填充父容器的剩余空间
  • NeverScrollableScrollPhysics():完全禁用GridView自身的滚动交互,把滚动控制权交给外层的SingleChildScrollView
  • 移除固定高度约束:让GridView能根据子项数量和布局参数自动计算高度,确保所有GridCard都能完整显示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 00:27:34