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

Flutter屏幕适配求助:GridView底部行被遮挡无法正常显示

Flutter UI适配问题求助:GridView底部行被遮挡

我是一名Android开发者,正在学习Flutter,遇到了一个关键的UI适配问题。我把屏幕尺寸分成10份,前9份用来放置GridView,最后1份放卡片组件。目前卡片显示正常,但GridView的底部部分行被遮挡(配图:GridView底部行被遮挡)。我希望UI能在所有屏幕尺寸下正常适配且无变形,尝试多种方法后仍未解决,恳请帮助。

相关代码

@override
Widget build(BuildContext context) {
  final screenWidth = MediaQuery.of(context).size.width;
  final screenHeight = MediaQuery.of(context).size.height;
  final double topPosition1 = screenHeight * 0.5;
  final double leftPosition1 = screenWidth * 0.61;
  final double topPosition2 = screenHeight * 0.56;
  final double leftPosition2 = screenWidth * 0.05;

  // Calculate the height of each part
  final double partHeight = screenHeight / 10;

  // Calculate the aspect ratio dynamically based on screen size
  final double aspectRatio = screenWidth / 10 / partHeight ;

  return Scaffold(
    appBar: AppBar(
      title: const Text('Snake and Ladder'),
    ),
    body: Center(
      child: Stack(
        children: [
          Column(
            children: [
              // First 9 parts
              Expanded(
                flex: 9,
                child: Container(
                  color: Colors.white,
                  child: GridView.builder(
                    padding: const EdgeInsets.all(5.0),
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 10,
                      mainAxisSpacing: 10.0,
                      crossAxisSpacing: 7,
                      childAspectRatio: aspectRatio,
                    ),
                    itemCount: totalCells,
                    physics: const NeverScrollableScrollPhysics(),
                    itemBuilder: (context, index) {
                      final cellNumber = totalCells - index;
                      int row = ((cellNumber - 1) ~/ 10);
                      int col = (cellNumber - 1) % 10;
                      int newCellNumber;
                      if (row % 2 == 0) {
                        newCellNumber = (row + 1) * 10 - col;
                      } else {
                        newCellNumber = (row + 1) * 10 - (9 - col);
                      }
                      return _buildCell(newCellNumber, newCellNumber == _playerLocation, newCellNumber == _playerLocation2);
                    },
                  ),
                ),
              ),
              // Last part
              Expanded(
                flex: 1,
                child: Container(
                  color: Colors.white,
                  child: GestureDetector(
                    onTap: () {
                      setState(() {
                        if (!_firstTap) {
                          _diceNumber = 6;
                          _firstTap = true;
                        } else {
                          _diceNumber = Random().nextInt(6) + 1;
                        }
                        if (_buildCell1) {
                          _playerLocation += _diceNumber;
                          if (_playerLocation > totalCells) {
                            _playerLocation = totalCells;
                          }
                          if (snakes.containsKey(_playerLocation)) {
                            _playerLocation = snakes[_playerLocation]!;
                          }
                        } else {
                          _playerLocation2 += _diceNumber;
                          if (_playerLocation2 > totalCells) {
                            _playerLocation2 = totalCells;
                          }
                          if (snakes.containsKey(_playerLocation2)) {
                            _playerLocation2 = snakes[_playerLocation2]!;
                          }
                        }
                        _buildCell1 = !_buildCell1;
                      });
                    },
                    child: Card(
                      margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 2.0),
                      color: _getPlayerColor(_buildCell1),
                      child: Padding(
                        padding: const EdgeInsets.all(10.0),
                        child: Image.asset(
                          'assets/images/dice-$_diceNumber.png',
                          width: 50,
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
         
        ],
      ),
    ),
  );
}

问题原因与解决方法

核心问题

你直接用screenHeight计算分区高度,但这个值包含了状态栏、AppBar的高度,导致GridView实际可用空间被高估,内容超出后因设置了NeverScrollableScrollPhysics无法滚动,最终底部行被遮挡。

解决方案

1. 修正可用高度计算

用屏幕高度减去状态栏和AppBar的高度,得到真正可分配给Column的空间:

@override
Widget build(BuildContext context) {
  final screenWidth = MediaQuery.of(context).size.width;
  final screenHeight = MediaQuery.of(context).size.height;
  // 获取状态栏高度
  final statusBarHeight = MediaQuery.of(context).padding.top;
  // AppBar默认高度
  final appBarHeight = kToolbarHeight;
  // 实际可用于内容的高度
  final availableHeight = screenHeight - statusBarHeight - appBarHeight;

  final double partHeight = availableHeight / 10;
  // 修正aspectRatio:考虑GridView的padding和间距
  final double itemWidth = (screenWidth - 5*2 - 7*(10-1)) / 10; // 总宽度 - 左右padding - 列间距总和
  final double aspectRatio = itemWidth / partHeight;

  // ... 其余代码不变
}
2. 用LayoutBuilder获取父组件可用尺寸

更稳妥的方式是用LayoutBuilder获取Column实际能使用的尺寸,自动适配AppBar等组件的占用空间:

@override
Widget build(BuildContext context) {
  final screenWidth = MediaQuery.of(context).size.width;

  return Scaffold(
    appBar: AppBar(
      title: const Text('Snake and Ladder'),
    ),
    body: LayoutBuilder(
      builder: (context, constraints) {
        // constraints.maxHeight是父组件(body)的可用高度,已排除AppBar
        final availableHeight = constraints.maxHeight;
        final double partHeight = availableHeight / 10;
        final double itemWidth = (screenWidth - 5*2 - 7*(10-1)) / 10;
        final double aspectRatio = itemWidth / partHeight;

        return Center(
          child: Stack(
            children: [
              Column(
                children: [
                  Expanded(
                    flex:9,
                    child: Container(
                      color: Colors.white,
                      child: GridView.builder(
                        padding: const EdgeInsets.all(5.0),
                        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                          crossAxisCount:10,
                          mainAxisSpacing:10.0,
                          crossAxisSpacing:7,
                          childAspectRatio: aspectRatio,
                        ),
                        itemCount: totalCells,
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (context, index) {
                          // ... itemBuilder逻辑不变
                        },
                      ),
                    ),
                  ),
                  Expanded(flex:1, child: /* 卡片组件不变 */)
                ],
              )
            ],
          ),
        );
      },
    ),
  );
}
3. 额外优化

如果_buildCell的内容有固定内边距或大小,计算aspectRatio时要把这些因素纳入,避免单元格内容挤压变形。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:54:59