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

Flutter中如何实现卡片列表的单选功能?

实现GridView卡片单选的修改方案

原代码用全局布尔变量控制单选的逻辑不匹配需求,改成记录选中索引的方式就能解决,具体修改步骤如下:

1. 替换全局变量,添加选中索引状态

删掉全局的bool scenarioOption = true;,在State类内部添加:

int? selectedIndex; // 记录选中的卡片索引,null表示未选中

2. 修正Radio组件的单选逻辑

修改scenarioCard里的Radio配置,把value设为当前卡片索引,groupValue绑定selectedIndex,点击时更新选中状态:

Radio<int?>(
  value: index,
  groupValue: selectedIndex,
  onChanged: (value) {
    setState(() {
      selectedIndex = value;
    });
  },
)

3. 添加选中卡片的视觉反馈(可选但推荐)

给选中的卡片加边框或背景色区分,让用户直观看到选中状态:

Card(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(8),
    side: selectedIndex == index 
        ? BorderSide(color: Color(0xFF9CA3AF), width: 2) // 选中时的边框
        : BorderSide.none,
  ),
  color: selectedIndex == index 
      ? Color(0x109CA3AF) // 选中时的浅背景色
      : Colors.white,
  child: ... // 原有卡片内容
)

4. 激活Next按钮(可选)

只有选中卡片时才让按钮可点击,同时调整样式:

ElevatedButton(
  style: ElevatedButton.styleFrom(
    primary: selectedIndex != null ? Color(0xFF9CA3AF) : Color(0xFFD9D9D9),
    // 保留原有其他样式
  ),
  onPressed: selectedIndex != null 
      ? () {
          // 可在这里获取选中的卡片数据:controller.reportCards[selectedIndex!]
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => HomeScreenTabBar()),
          );
        }
      : null, // 未选中时按钮不可点击
  child: ... // 原有按钮文本
)

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:haufa/controllers/report_controller.dart';
import 'package:haufa/pages/Tabbar/homescreen_tabbar.dart';

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

  @override
  State<ReportSituationSelectRiskLevel> createState() =>
      _ReportSituationSelectRiskLevelState();
}

class _ReportSituationSelectRiskLevelState
    extends State<ReportSituationSelectRiskLevel> {
  final controller = ReportController();
  final ScrollController _controller = ScrollController();
  int? selectedIndex; // 记录选中的卡片索引

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: false,
      appBar: AppBar(
        toolbarHeight: 0,
        elevation: 0,
        backgroundColor: Colors.transparent,
      ),
      backgroundColor: Colors.white,
      body: Column(
        children: [
          SizedBox(height: 48.h),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              SizedBox(width: 60.w),
              Text(
                "Verify situation",
                style: GoogleFonts.inter(
                  fontSize: 18,
                  fontWeight: FontWeight.w500,
                  color: Color(0xFF374151), // 修正原代码多余的F
                ),
              ),
              IconButton(
                splashRadius: 21,
                color: Color(0xFF9CA3AF), // 修正原代码多余的F
                onPressed: () {
                  Navigator.pop(context);
                },
                icon: Icon(Icons.close),
              ),
            ],
          ),
          SizedBox(height: 28.h),
          Text(
            "Select a scenario",
            style: GoogleFonts.inter(
              fontSize: 16,
              fontWeight: FontWeight.w400,
              color: Color(0xFF374151), // 修正原代码多余的F
            ),
          ),
          SizedBox(height: 24.h),
          Container(
            height: 475.h,
            child: Scrollbar(
              child: GridView.builder(
                padding: EdgeInsets.only(left: 10.w, right: 10.w),
                itemCount: 12,
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                ),
                itemBuilder: (context, index) => scenarioCard(index),
              ),
            ),
          ),
          SizedBox(height: 24.h),
          Padding(
            padding: const EdgeInsets.only(left: 30.0, right: 30),
            child: ElevatedButton(
              style: ElevatedButton.styleFrom(
                elevation: 2,
                shadowColor: Color(0x0D000000), // 修正原代码颜色格式
                primary: selectedIndex != null ? Color(0xFF9CA3AF) : Color(0xFFD9D9D9),
                minimumSize: Size.fromHeight(37.53),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.all(
                    Radius.circular(6),
                  ),
                ),
              ),
              onPressed: selectedIndex != null
                  ? () {
                      // 获取选中的卡片数据
                      final selectedCard = controller.reportCards[selectedIndex!];
                      print("选中场景:${selectedCard.title}");
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => HomeScreenTabBar()),
                      );
                    }
                  : null,
              child: Text(
                "Next",
                style: GoogleFonts.inter(
                  fontSize: 16,
                  fontWeight: FontWeight.w600,
                  color: selectedIndex != null ? Colors.white : Color(0xFF8C8C8C),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget scenarioCard(int index) => Card(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
          side: selectedIndex == index
              ? BorderSide(color: Color(0xFF9CA3AF), width: 2)
              : BorderSide.none,
        ),
        color: selectedIndex == index ? Color(0x109CA3AF) : Colors.white,
        child: Container(
          height: 175.h,
          width: 149.w,
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.only(right: 100.0),
                child: Radio<int?>(
                  value: index,
                  groupValue: selectedIndex,
                  onChanged: (value) {
                    setState(() {
                      selectedIndex = value;
                    });
                  },
                ),
              ),
              SvgPicture.asset(controller.reportCards[index].svgImage),
              SizedBox(height: 8.h),
              Text(
                controller.reportCards[index].title,
                style: GoogleFonts.inter(
                  fontSize: 16,
                  fontWeight: FontWeight.w500,
                  color: Color(0xFF434343), // 修正原代码多余的F
                ),
              ),
            ],
          ),
        ),
      );
}

注:原代码中部分颜色值多了多余的F(如Color(0xFFF374151)),已一并修正避免颜色显示异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 11:30:52