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
相关产品推荐
相关产品推荐

