Flutter中实现GridView内Button单选功能的方案咨询
要实现每次仅选中一个Button的功能,需要同时修改数据模型(CardData)、卡片组件(my_card.dart)和网格容器(my_card_grid.dart)——核心思路是把选中状态从单个卡片的局部状态,提升到全局数据层统一管理,让所有卡片共享同一个选中状态源。
1. 更新数据模型(CardData)
在card_data.dart的MyCardData类中添加选中状态的管理逻辑,用来记录当前选中的卡片唯一标识:
class MyCardData extends ChangeNotifier { // 原有代码(卡片列表、增删方法等)保持不变 List<MyCard> cards = []; int get cardCount => cards.length; // 新增:存储当前选中卡片的ID,未选中时为null int? selectedCardId; // 新增:更新选中卡片的方法 void selectCard(int cardId) { selectedCardId = cardId; notifyListeners(); // 通知所有监听组件更新UI } }
注:假设你的
MyCard模型类有唯一标识id,如果没有可以改用卡片索引或对象本身来区分。
2. 修改卡片组件(my_card.dart)
移除卡片内部的selectedCard局部状态,改为接收外部传入的选中状态和点击回调,让组件变成纯展示型:
// 改为StatelessWidget(如果不需要其他内部状态) class MyCard extends StatelessWidget { final String cardTitle; final Color pickerColor; final VoidCallback deleteCallback; final bool isSelected; final VoidCallback onTapCallback; const MyCard({ super.key, required this.cardTitle, required this.pickerColor, required this.deleteCallback, required this.isSelected, required this.onTapCallback, }); @override Widget build(BuildContext context) { return TextButton( onLongPress: () => showDialog<String>( // 原有弹窗逻辑完全保留 context: context, builder: (BuildContext context) => AlertDialog( actionsAlignment: MainAxisAlignment.spaceEvenly, title: Center( child: Text( cardTitle, style: TextStyle( color: pickerColor, fontWeight: FontWeight.bold, shadows: [ Shadow( color: Colors.black.withOpacity(0.3), offset: const Offset(0, 0), blurRadius: 15, ), ], ), ), ), content: const Text( 'Möchtest du die Karte wirklich löschen?', textAlign: TextAlign.center, style: TextStyle(fontSize: 18), ), actions: <Widget>[ TextButton( style: ButtonStyle( shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)) ), elevation: MaterialStateProperty.all(10), backgroundColor: MaterialStateProperty.all(Colors.white), ), onPressed: () { deleteCallback(); Navigator.pop(context); }, child: const Text( 'Karte löschen', style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 16, ), ), ), TextButton( style: ButtonStyle( shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)) ), elevation: MaterialStateProperty.all(10), backgroundColor: MaterialStateProperty.all(Colors.white), ), onPressed: () => Navigator.pop(context), child: const Text( 'Abbrechen', style: TextStyle(color: Colors.black, fontSize: 16), ), ), ], ), ), style: ButtonStyle( side: MaterialStateProperty.all(BorderSide( width: 5, color: isSelected ? Colors.black : Colors.white, )), shape: MaterialStateProperty.all( RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)) ), backgroundColor: MaterialStateProperty.all(pickerColor), elevation: MaterialStateProperty.all(10), ), onPressed: onTapCallback, child: FittedBox( fit: BoxFit.fitHeight, child: Text( cardTitle, style: TextStyle( fontSize: 17, color: useWhiteForeground(pickerColor) ? const Color(0xffffffff) : const Color(0xff000000), ), ), ), ); } }
3. 修改网格容器(my_card_grid.dart)
在构建MyCard时,传入当前卡片的选中状态判断结果,以及点击时触发全局选中状态更新的回调:
class CardsGrid extends StatelessWidget { const CardsGrid({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Consumer<MyCardData>( builder: (context, cardData, child) { return Padding( padding: const EdgeInsets.all(10), child: GridView.builder( clipBehavior: Clip.none, itemBuilder: (context, index) { final card = cardData.cards[index]; return MyCard( cardTitle: card.name, pickerColor: card.cardColor, deleteCallback: () { cardData.deleteCallback(card); }, // 判断当前卡片是否为选中状态 isSelected: cardData.selectedCardId == card.id, // 点击时更新全局选中的卡片ID onTapCallback: () { cardData.selectCard(card.id); }, ); }, itemCount: cardData.cardCount, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 150, childAspectRatio: 2.5 / 1, crossAxisSpacing: 0, mainAxisSpacing: 0, ), ), ); }, ); } }
原理说明
原来的实现中每个MyCard独立维护自己的选中状态,彼此没有关联,所以点击新卡片时旧卡片的选中状态不会改变。现在将选中状态统一放在全局的MyCardData中管理,所有卡片共享这一个状态源:点击任意卡片时,会更新全局的选中ID,所有卡片都会根据这个ID重新判断自己的选中状态,从而实现单选效果。
内容的提问来源于stack exchange,提问作者Val
相关产品推荐
相关产品推荐

