You need to enable JavaScript to run this app. Flutter 如何通过 ListView.builder 生成样式统一内容唯一的 Card 卡片?
优惠活动
大模型
产品
解决方案
定价
更多

Flutter 如何通过 ListView.builder 生成样式统一内容唯一的 Card 卡片?

现有逻辑问题

你当前的代码逻辑确实存在问题:VideoCard组件内的图片资源、标题、难度、时长、点击跳转逻辑全部为硬编码的固定值,且ListView.builder循环时仅传入统一的long: true参数,没有给每个卡片传入不同的业务数据,自然所有卡片的内容都完全一致。


解决方案

你可以通过「数据模型封装+组件参数化改造」的方式实现需求,既保证所有卡片UI样式完全统一,又可以灵活配置每个卡片的内容和交互:

  1. 首先定义视频业务数据模型,统一管理所有可变字段
// 视频数据模型
class VideoModel {
  final String imagePath;
  final String title;
  final String difficulty;
  final String duration;
  final VoidCallback onTap;

  const VideoModel({
    required this.imagePath,
    required this.title,
    required this.difficulty,
    required this.duration,
    required this.onTap,
  });
}
  1. 改造VideoCard组件,所有可变内容改为从参数接收
class VideoCard extends material.StatelessWidget {
  final bool long;
  // 新增接收业务数据的参数
  final VideoModel videoData;
  const VideoCard({
    @material.required this.long,
    @material.required this.videoData,
    material.Key? key,
  }) : super(key: key);

  @override
  material.Widget build(material.BuildContext context) {
    return material.Padding(
      padding: const material.EdgeInsets.all(10.0),
      child: CardWidget(
        gradient: false,
        button: true,
        width: long ? 360 : 180,
        child: material.Column(
          mainAxisAlignment: material.MainAxisAlignment.start,
          children: <material.Widget>[
            material.Container(
              width: long ? 360 : 180,
              height: 90,
              decoration: material.BoxDecoration(
                image: material.DecorationImage(
                    // 使用传入的图片路径
                    image: material.AssetImage(videoData.imagePath),
                    fit: material.BoxFit.contain),
                borderRadius: material.BorderRadius.only(
                  topLeft: material.Radius.circular(10),
                  topRight: material.Radius.circular(10),
                ),
              ),
            ),
            material.Padding(
              padding: const material.EdgeInsets.all(8.0),
              child: material.Text(
                // 使用传入的标题
                videoData.title,
                overflow: material.TextOverflow.ellipsis,
                maxLines: 2,
                style: material.TextStyle(
                    color: material.Colors.white,
                    fontFamily: 'Red Hat Display',
                    backgroundColor: material.Colors.black,
                    fontSize: 16),
              ),
            ),
            material.Padding(
              padding: const material.EdgeInsets.symmetric(horizontal: 8.0),
              child: material.Row(
                children: <material.Widget>[
                  material.Icon(BoxIcons.bx_bar_chart_alt_2, size: 16),
                  material.Text(
                    // 使用传入的难度
                    videoData.difficulty,
                    style: material.TextStyle(
                        color: material.Color(0xFFADADAD),
                        fontFamily: 'Red Hat Display',
                        fontSize: 10),
                  ),
                  material.Spacer(),
                  material.Text(
                    // 使用传入的时长
                    videoData.duration,
                    style: material.TextStyle(
                        color: material.Color(0xFFADADAD),
                        fontFamily: 'Red Hat Display',
                        fontSize: 10),
                  ),
                  material.Icon(BoxIcons.bx_timer, size: 16),
                ],
              ),
            ),
            material.Spacer(),
            material.Padding(
              padding: const material.EdgeInsets.only(top: 6.0),
              child: material.GestureDetector(
                // 使用传入的点击回调
                onTap: videoData.onTap,
                child: material.Container(
                  padding: material.EdgeInsets.fromLTRB(0, 14, 0, 14),
                  decoration: material.BoxDecoration(gradient: Colors().waves),
                  child: material.Row(
                    mainAxisAlignment: material.MainAxisAlignment.spaceEvenly,
                    children: <material.Widget>[
                      material.Icon(BoxIcons.bx_play_circle,
                          color: material.Colors.black),
                      material.Text(
                        "Learn it",
                        style: material.TextStyle(
                            color: material.Colors.black,
                            fontFamily: 'Red Hat Display',
                            fontSize: 18),
                      )
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
  1. 准备自定义数据源,改造home.dart中的列表构建逻辑
// 在home.dart中提前定义所有卡片的业务数据
final List<VideoModel> videoList = [
  VideoModel(
    imagePath: 'assets/images/bitcoin.png',
    title: 'BITCOIN - A pioneer in Crypto!',
    difficulty: 'Beginner',
    duration: '10 mins',
    onTap: () {
      material.Navigator.push(
        context,
        CupertinoPageRoute(
          builder: (context) => VideoPage(),
        ),
      );
    },
  ),
  // 按照相同格式添加剩余9个卡片的自定义数据,onTap逻辑可单独配置
  VideoModel(
    imagePath: 'assets/images/ethereum.png',
    title: 'ETHEREUM - 智能合约首选平台',
    difficulty: 'Intermediate',
    duration: '15 mins',
    onTap: () {
      // 可自定义跳转逻辑
      material.Navigator.push(
        context,
        CupertinoPageRoute(
          builder: (context) => EthVideoPage(),
        ),
      );
    },
  ),
  // 其余卡片数据以此类推
];

// 改造原有的ListView.builder代码
SliverToBoxAdapter(
  child: Container(
    width: MediaQuery.of(context).size.width,
    height: 245,
    child: ListView.builder(
      scrollDirection: Axis.horizontal,
      // 直接用数据源长度作为itemCount,无需写死
      itemCount: videoList.length,
      itemBuilder: (context, index) {
        return VideoCard(
          long: true,
          // 传入对应索引的业务数据
          videoData: videoList[index],
        );
      }
    ),
  ),
),

这种实现方式把UI层和数据层完全分离,卡片的样式统一由VideoCard管控,内容和交互完全由数据源控制,后续新增/修改卡片内容只需要调整videoList即可,不需要修改组件代码,符合Flutter的组件封装规范。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 06:15:05