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

Flutter GridView响应式适配需求及宽屏布局变形问题

改造Flutter GridView为响应式布局

问题背景

现有自定义ListaGrid4x2Experiencias组件使用固定4列的GridView,在1042px宽度下显示正常,但屏幕宽度增大时组件出现变形。需要实现响应式布局:根据屏幕宽度自动调整列数,同时保持子组件宽高比不变。

解决方案

核心改动方向

  • 替换固定列数的网格代理,改用基于子项最大宽度的自动列数计算
  • 移除子组件中硬编码的屏幕宽度依赖,让布局由网格代理控制
  • 调整子组件内部元素的尺寸逻辑,确保宽高比一致

修改后的代码

1. 改造ListaGrid4x2Experiencias

class ListaGrid4x2Experiencias extends StatefulWidget {
  const ListaGrid4x2Experiencias({Key? key}) : super(key: key);

  @override
  State<ListaGrid4x2Experiencias> createState() => _ListaGrid4x2ExperienciasState();
}

class _ListaGrid4x2ExperienciasState extends State<ListaGrid4x2Experiencias> {
  final List<Experiencia> listaExperiencias = ListasFake.listaFakeExperiencias;

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      // 使用基于最大子项宽度的网格代理,自动计算列数
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200, // 每个子项的最大宽度,可根据需求调整
        childAspectRatio: 5 / 7, // 保持子项宽高比,适配图片+文字的整体比例
        crossAxisSpacing: 20,
        mainAxisSpacing: 5,
      ),
      itemCount: listaExperiencias.length,
      shrinkWrap: true,
      itemBuilder: (BuildContext context, index) {
        final experiencia = listaExperiencias[index];
        return GestureDetector(
          onTap: () {},
          child: Container(
            color: Colors.white,
            child: BoxExperienciaMuro(
              imagenPrincipal: experiencia.imagenPrincipal,
              denominacion: experiencia.denominacion,
              ubicacion: experiencia.ubicacion,
              unidadTiempo: experiencia.unidadTiempo,
              precioUnitario: experiencia.precioUnitario,
            ),
          ),
        );
      },
    );
  }
}

2. 改造BoxExperienciaMuro

class BoxExperienciaMuro extends StatefulWidget {
  const BoxExperienciaMuro({
    Key? key,
    required this.imagenPrincipal,
    required this.denominacion,
    required this.ubicacion,
    required this.unidadTiempo,
    required this.precioUnitario,
  }) : super(key: key);

  final String imagenPrincipal;
  final String denominacion;
  final String ubicacion;
  final String unidadTiempo;
  final String precioUnitario;

  @override
  State<BoxExperienciaMuro> createState() => _BoxExperienciaMuroState();
}

class _BoxExperienciaMuroState extends State<BoxExperienciaMuro> {
  @override
  Widget build(BuildContext context) {
    final urlImagen = Constantes.imagenesExperiencias + widget.imagenPrincipal;
    
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.only(bottom: 8.0),
          child: AspectRatio(
            aspectRatio: 1, // 图片区域宽高比1:1,可根据需求调整
            child: Stack(
              fit: StackFit.expand,
              children: [
                Container(
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10),
                    image: DecorationImage(
                      fit: BoxFit.cover,
                      image: NetworkImage(urlImagen),
                    ),
                  ),
                ),
                const Positioned(
                  bottom: 10,
                  left: 10,
                  child: BoxPercentProgressPlazasDisponibles(),
                ),
              ],
            ),
          ),
        ),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TextoNormalNegroNegrita(texto: widget.denominacion),
            const SizedBox(height: 4),
            TextoNormalNegroNormal(texto: widget.ubicacion),
            const SizedBox(height: 4),
            Row(
              children: [
                TextoNormalNegroNegrita(texto: widget.unidadTiempo),
                const SizedBox(width: 4),
                TextoNormalNegroNegrita(texto: widget.precioUnitario),
              ],
            ),
          ],
        ),
      ],
    );
  }
}

关键说明

  • 自动列数计算:SliverGridDelegateWithMaxCrossAxisExtent会根据屏幕宽度和maxCrossAxisExtent自动计算每行显示的列数,屏幕越宽列数越多
  • 保持宽高比:通过childAspectRatio控制整个子项的宽高比,结合AspectRatio组件确保图片区域的比例一致
  • 移除硬编码尺寸:去掉原代码中基于屏幕宽度计算的固定宽度,改用自适应布局,避免屏幕变化时的变形问题
  • 优化内部布局:使用SizedBox替代固定高度的容器,让文字区域自适应内容,同时保持间距一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 03:45:34