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

Flutter布局实现:指定ID卡片固定首位且尺寸为其余卡片两倍

解决方案:实现首项放大的网格布局

GridView的默认布局逻辑是强制所有单元格使用统一尺寸,所以直接用它没法实现“首项卡片尺寸为其他两倍”的需求。下面提供两种可行方案:

方案一:原生CustomScrollView + 自定义网格代理

无需依赖第三方库,通过自定义SliverGridDelegate精准控制每个item的布局:

步骤1:自定义网格代理

class FeaturedGridDelegate extends SliverGridDelegateWithFixedCrossAxisCount {
  final int featuredIndex;

  FeaturedGridDelegate({
    required this.featuredIndex,
    required int crossAxisCount,
  }) : super(crossAxisCount: crossAxisCount);

  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    final crossAxisExtent = constraints.crossAxisExtent / crossAxisCount;
    final defaultTileHeight = crossAxisExtent; // 默认小卡片为正方形

    return _FeaturedGridLayout(
      crossAxisCount: crossAxisCount,
      crossAxisTileSize: crossAxisExtent,
      defaultTileHeight: defaultTileHeight,
      featuredIndex: featuredIndex,
    );
  }
}

class _FeaturedGridLayout extends SliverGridLayout {
  final int crossAxisCount;
  final double crossAxisTileSize;
  final double defaultTileHeight;
  final int featuredIndex;

  _FeaturedGridLayout({
    required this.crossAxisCount,
    required this.crossAxisTileSize,
    required this.defaultTileHeight,
    required this.featuredIndex,
  });

  @override
  double computeMaxScrollOffset(int childCount) {
    final normalItemCount = childCount - 1;
    final normalRows = (normalItemCount + crossAxisCount - 1) ~/ crossAxisCount;
    // 首项高度为小卡片的2倍,加上其余项总高度
    return defaultTileHeight * 2 + normalRows * defaultTileHeight;
  }

  @override
  SliverGridGeometry getGeometryForChildIndex(int index) {
    if (index == featuredIndex) {
      // 首项占满整行,高度为小卡片的2倍
      return SliverGridGeometry(
        scrollOffset: 0,
        crossAxisOffset: 0,
        mainAxisExtent: defaultTileHeight * 2,
        crossAxisExtent: crossAxisTileSize * crossAxisCount,
      );
    } else {
      // 调整索引,跳过首项
      final adjustedIndex = index - 1;
      final row = adjustedIndex ~/ crossAxisCount;
      final column = adjustedIndex % crossAxisCount;
      // 首项占了前2倍高度,其余项从下方开始排列
      final scrollOffset = defaultTileHeight * 2 + row * defaultTileHeight;
      return SliverGridGeometry(
        scrollOffset: scrollOffset,
        crossAxisOffset: column * crossAxisTileSize,
        mainAxisExtent: defaultTileHeight,
        crossAxisExtent: crossAxisTileSize,
      );
    }
  }

  @override
  int getMinChildIndexForScrollOffset(double scrollOffset) {
    if (scrollOffset < defaultTileHeight * 2) return 0;
    final row = ((scrollOffset - defaultTileHeight * 2) / defaultTileHeight).floor();
    return 1 + row * crossAxisCount;
  }

  @override
  int getMaxChildIndexForScrollOffset(double scrollOffset) {
    if (scrollOffset < defaultTileHeight * 2) return 0;
    final row = ((scrollOffset - defaultTileHeight * 2) / defaultTileHeight).floor();
    return 1 + (row + 1) * crossAxisCount - 1;
  }
}

步骤2:使用自定义代理构建布局

// 先将目标用户移到列表首位
final List<User> users = [targetUser, ...restUsers];

@override
Widget build(BuildContext context) {
  return CustomScrollView(
    slivers: [
      SliverGrid(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            final user = users[index];
            return index == 0
                ? _buildFeaturedUserCard(user) // 大卡片
                : _buildNormalUserCard(user); // 小卡片
          },
          childCount: users.length,
        ),
        gridDelegate: FeaturedGridDelegate(
          crossAxisCount: 2, // 网格列数
          featuredIndex: 0,
        ),
      ),
    ],
  );
}

// 大卡片实现
Widget _buildFeaturedUserCard(User user) {
  return Container(
    margin: EdgeInsets.all(8),
    decoration: BoxDecoration(
      color: Colors.lightBlueAccent,
      borderRadius: BorderRadius.circular(12),
    ),
    child: Center(
      child: Text(user.name, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
    ),
  );
}

// 小卡片实现
Widget _buildNormalUserCard(User user) {
  return Container(
    margin: EdgeInsets.all(8),
    decoration: BoxDecoration(
      color: Colors.grey[200],
      borderRadius: BorderRadius.circular(12),
    ),
    child: Center(child: Text(user.name)),
  );
}

方案二:使用第三方库flutter_staggered_grid_view

如果不想写复杂的自定义布局,推荐用专门处理不规则网格的flutter_staggered_grid_view库:

步骤1:添加依赖

在pubspec.yaml中添加:

dependencies:
  flutter_staggered_grid_view: ^0.7.0

步骤2:实现布局

final List<User> users = [targetUser, ...restUsers];

@override
Widget build(BuildContext context) {
  return StaggeredGridView.count(
    crossAxisCount: 2,
    padding: EdgeInsets.all(8),
    staggeredTiles: [
      // 首项占满2列,高度为小卡片的2倍
      StaggeredTile.extent(2, 200),
      // 其余项各占1列,高度100
      ...List.generate(users.length - 1, (index) => StaggeredTile.extent(1, 100)),
    ],
    mainAxisSpacing: 8,
    crossAxisSpacing: 8,
    children: users.map((user) {
      return users.indexOf(user) == 0
          ? _buildFeaturedUserCard(user)
          : _buildNormalUserCard(user);
    }).toList(),
  );
}

注意事项

  • 两种方案都需要先将目标用户移到列表第一个位置
  • 可根据需求调整大卡片的尺寸比例(比如宽度两倍/高度两倍/面积两倍)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 15:17:31