Flutter中如何用动态数量List实现GridView时间槽单选变色?
解决Flutter GridView时间槽单选变色问题
能不能用变量替代常量初始化选中状态列表?
完全可以,不需要固定常量,直接用统计得到的总槽位数变量来处理就行,甚至更推荐用单个变量记录选中索引的方式——逻辑更清晰,还能节省内存。
解决方案1:单个变量记录选中索引(推荐)
不需要维护长度等于总槽位数的List,只用一个selectedIndex变量记录当前选中的槽位索引,UI根据索引是否匹配切换颜色,点击时直接更新索引就能自动取消其他槽位的选中状态。
import 'package:flutter/material.dart'; class TimeSlotPicker extends StatefulWidget { final int totalSlots; // 直接传入统计得到的总槽位数变量 const TimeSlotPicker({super.key, required this.totalSlots}); @override State<TimeSlotPicker> createState() => _TimeSlotPickerState(); } class _TimeSlotPickerState extends State<TimeSlotPicker> { int? selectedIndex; // null表示未选中任何槽位 @override Widget build(BuildContext context) { return GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: widget.totalSlots, itemBuilder: (context, index) { bool isSelected = selectedIndex == index; return GestureDetector( onTap: () { setState(() { selectedIndex = index; // 点击更新选中索引,自动取消其他选中 }); }, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: isSelected ? Colors.blue : Colors.grey.shade200, borderRadius: BorderRadius.circular(8), ), child: Text( '槽位 ${index + 1}', style: TextStyle( color: isSelected ? Colors.white : Colors.black87, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ), ); }, ); } } // 使用示例:直接传入统计好的槽位数 // TimeSlotPicker(totalSlots: 15)
解决方案2:动态生成List管理选中状态
如果坚持要用List维护选中状态,也可以用变量初始化,不需要常量。注意如果总槽位数可能动态变化,要重写didUpdateWidget方法更新List。
import 'package:flutter/material.dart'; class TimeSlotPicker extends StatefulWidget { final int totalSlots; const TimeSlotPicker({super.key, required this.totalSlots}); @override State<TimeSlotPicker> createState() => _TimeSlotPickerState(); } class _TimeSlotPickerState extends State<TimeSlotPicker> { late List<bool> selectedSlots; @override void initState() { super.initState(); // 用传入的totalSlots变量初始化选中列表,默认全未选中 selectedSlots = List.generate(widget.totalSlots, (_) => false); } // 若总槽位数可能动态变化,更新组件时同步更新选中列表 @override void didUpdateWidget(covariant TimeSlotPicker oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.totalSlots != widget.totalSlots) { setState(() { selectedSlots = List.generate(widget.totalSlots, (_) => false); }); } } @override Widget build(BuildContext context) { return GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: widget.totalSlots, itemBuilder: (context, index) { bool isSelected = selectedSlots[index]; return GestureDetector( onTap: () { setState(() { // 先取消所有槽位的选中状态 for (int i = 0; i < selectedSlots.length; i++) { selectedSlots[i] = false; } // 再选中当前槽位 selectedSlots[index] = true; }); }, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: isSelected ? Colors.blue : Colors.grey.shade200, borderRadius: BorderRadius.circular(8), ), child: Text( '槽位 ${index + 1}', style: TextStyle( color: isSelected ? Colors.white : Colors.black87, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ), ); }, ); } }
方案对比
- 方案1逻辑简单、内存开销小,完全适配单选场景,优先推荐。
- 方案2适合后续可能扩展为多选的场景,但单选下没必要额外遍历List取消选中状态。
内容的提问来源于stack exchange,提问作者Muhammad Umar
相关产品推荐
相关产品推荐

