Reputation: 51
I have a Grid.View.builder with Buttons inside. I want to be able to select just one Button at a time!
At the moment I can select Buttons but when I select other buttons the previous Button stays selected.
How can I achieve this? In my_card_grid.dart or my_card.dart?
This is my_card_grid.dart
import 'package:flutter/material.dart';
import 'package:cards/widgets/my_card.dart';
import 'package:provider/provider.dart';
import 'package:cards/Models/card_data.dart';
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);
},
);
},
itemCount: cardData.cardCount,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 2.5 / 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
),
),
);
},
);
}
}
and this is my_card.dart
import 'package:cards/Models/card.dart';
import 'package:cards/Screens/add_card.dart';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:cards/Models/card_data.dart';
import 'package:provider/provider.dart';
class MyCard extends StatefulWidget {
late String cardTitle;
Color pickerColor;
final VoidCallback deleteCallback;
MyCard(
{required this.cardTitle,
required this.pickerColor,
required this.deleteCallback});
@override
State<MyCard> createState() => _MyCardState();
}
class _MyCardState extends State<MyCard> {
bool selectedCard = false;
@override
Widget build(BuildContext context) {
return TextButton(
onLongPress: () => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
actionsAlignment: MainAxisAlignment.spaceEvenly,
title: Center(
child: Text(
widget.cardTitle,
style: TextStyle(
color: widget.pickerColor,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
color: Colors.black.withOpacity(0.3),
offset: 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: () {
widget.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: selectedCard ? Colors.black : Colors.white)),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
backgroundColor: MaterialStateProperty.all(widget.pickerColor),
elevation: MaterialStateProperty.all(10)),
onPressed: () {
print(selectedCard);
setState(() {
selectedCard = !selectedCard;
});
},
child: FittedBox(
fit: BoxFit.fitHeight,
child: Text(
widget.cardTitle,
style: TextStyle(
fontSize: 17,
color: useWhiteForeground(widget.pickerColor)
? const Color(0xffffffff)
: const Color(0xff000000),
),
),
),
);
}
}
Upvotes: 1
Views: 4104
Reputation: 78
Just a quick thought, I think we can achieve that by doing following modifications, which basically propagating selection info to parent:
MyCard
into StatelessWidget
, which takes in isSelected
for rendering purposeCardsGrid
into StatefulWidget
and adding a state int selectedIdx
.MyCard
component inside GestureDector
for adjusting selected
Could be something like:GestureDetector(
onTap() => setState(() => selected = index),
child: MyCard(selected == index),
)
As per requested, for Step 2,
class CardsGrid extends StatefulWidget {
CardsGrid({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _CardsGridState();
}
class _CardsGridState extends State<CardsGrid> {
int selectedIdx = -1; // or mark as late and initialize in initState() if u prefer
@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 GestureDetector( // ADD
onTap: () => setState(() => selectedIdx = index), // ADD
child: MyCard(
selected: index == selectedIdx, // ADD
cardTitle: card.name,
pickerColor: card.cardColor,
deleteCallback: () {
cardData.deleteCallback(card);
},
), // ADD
);
},
itemCount: cardData.cardCount,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 2.5 / 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
),
),
);
},
);
}
}
Update on 10-Oct-2022: as per requested, here's the sample code, can paste it into your dartpad online to try it out
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: CardsHolderWidget(),
),
),
);
}
}
class CardsHolderWidget extends StatefulWidget {
const CardsHolderWidget({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _CardsHolderWidgetState();
}
class _CardsHolderWidgetState extends State<CardsHolderWidget> {
int selected = -1;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) => GestureDetector(
onTap: () => setState(() => selected = index),
child: CardWidget(selected == index, '$index'),
),
);
}
}
class CardWidget extends StatelessWidget {
final bool selected;
final String index;
const CardWidget(this.selected, this.index, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: selected ? Colors.red : Colors.blue,
child: Text(index),
);
}
}
Upvotes: 1
Reputation: 51
So, I've found another article and tried to implement it in mine.
It works!
However, I can not unselect items. Anyone an idea?
here is the code
class MyCard extends StatefulWidget {
late String cardTitle;
Color pickerColor;
final VoidCallback deleteCallback;
bool isSelected = false;
Function(int) selectedCard;
final int index;
MyCard(
this.selectedCard, {
Key? key,
required this.isSelected,
required this.cardTitle,
required this.pickerColor,
required this.deleteCallback,
required this.index})
: super(key: key);
@override
State<MyCard> createState() => _MyCardState();
}
class _MyCardState extends State<MyCard> {
@override
Widget build(BuildContext context) {
return TextButton(
onLongPress: () => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
actionsAlignment: MainAxisAlignment.spaceEvenly,
title: Center(
child: Text(
widget.cardTitle,
style: TextStyle(
color: widget.pickerColor,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
color: Colors.black.withOpacity(0.3),
offset: 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: () {
widget.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: widget.isSelected ? Colors.black : Colors.white)),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
backgroundColor: MaterialStateProperty.all(widget.pickerColor),
elevation: MaterialStateProperty.all(10)),
onPressed: () {
widget.selectedCard(widget.index);
},
child: FittedBox(
fit: BoxFit.fitHeight,
child: Text(
widget.cardTitle,
style: TextStyle(
fontSize: 17,
color: useWhiteForeground(widget.pickerColor)
? const Color(0xffffffff)
: const Color(0xff000000),
),
),
),
);
}
}
class CardsGrid extends StatefulWidget {
CardsGrid({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _CardsGridState();
}
class _CardsGridState extends State<CardsGrid> {
int _selectedCard = -1;
selectedCard(index) {
setState(() {
_selectedCard = index;
});
}
@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(
selectedCard,
index: index,
isSelected: _selectedCard == index,
cardTitle: card.name,
pickerColor: card.cardColor,
deleteCallback: () {
cardData.deleteCallback(card);
},
);
},
itemCount: cardData.cardCount,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 2.5 / 1,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
),
),
);
},
);
}
}
Upvotes: 0
Reputation: 44066
If you want the logic of a radio button, it should look like a radio button to be familiar. Find out more at https://material.io/components/radio-buttons. There's example Flutter code there for an implementation as well.
Upvotes: 0