I have gridView.builder and have elevatedButton in my grid, when i press my button, button click event showing for all my grid tiles:
How can i show this loader effect only on the button that is pressed
void _onSubmit() {
if (Constants.USER_TOKEN != null && Constants.USER_TOKEN != '') {
print('NO');
} else {
showDialog<void>(
context: context,
builder: (BuildContext dialogContext) {
return SimpleDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0)),
title: Text(
'Пройдите регистрацию, чтобы добавлять в корзину',
textAlign: TextAlign.center,
),
children: <Widget>[
TextButton(
child: Text('Пройти регистрацию'),
onPressed: () {
Navigator.of(dialogContext).pop(); // Dismiss alert dialog
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => MainScreen(currentIndex: 3)),
(route) => false);
},
style: ButtonStyle(alignment: Alignment.center),
),
],
);
},
);
}
setState(() => _isLoading = true);
Future.delayed(
const Duration(seconds: 2),
() => setState(() => _isLoading = false),
);
}
ElevatedButton.icon(
icon: _isLoading
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 3,
),
)
: const Icon(Icons.shopping_basket),
onPressed: () => _onSubmit(),
CodePudding user response:
You need to define new variable like this:
int selectedIndex = -1;
then pass the item index to your item and use it Like this:
ElevatedButton.icon(
icon: _isLoading && selectedIndex == index //<-- add this
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 3,
),
)
: const Icon(Icons.shopping_basket),
onPressed: () {
selectedIndex = index;//<-- add this
_onSubmit();
},
)
also remember to reset the selectedIndex
after loading finish:
selectedIndex = -1;
Thanks to @Ivo, if you want to have more than one loading at the same time you need this approach:
first define the list of bool like this:
List<bool> itemLoading = [];
when your items list get ready fill this list with default value like this:
itemLoading = List.generate(yourItemsList.length, (_) => false);
then you need update this list in your Item like this:
ElevatedButton.icon(
icon: itemLoading[index] //<-- add this
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 3,
),
)
: const Icon(Icons.shopping_basket),
onPressed: () {
itemLoading[index] = true;//<-- add this
_onSubmit(index);
},
)
then in your _onSubmit
do this:
void _onSubmit(int index) {
if (Constants.USER_TOKEN != null && Constants.USER_TOKEN != '') {
print('NO');
} else {
showDialog<void>(
context: context,
builder: (BuildContext dialogContext) {
return SimpleDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0)),
title: Text(
'Пройдите регистрацию, чтобы добавлять в корзину',
textAlign: TextAlign.center,
),
children: <Widget>[
TextButton(
child: Text('Пройти регистрацию'),
onPressed: () {
Navigator.of(dialogContext).pop(); // Dismiss alert dialog
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => MainScreen(currentIndex: 3)),
(route) => false);
},
style: ButtonStyle(alignment: Alignment.center),
),
],
);
},
);
}
Future.delayed(
const Duration(seconds: 2),
() => setState(() => itemLoading[index] = false),
);
}
CodePudding user response:
Another solution that allows having multiple loading items at the same time is to keep track which indexes are loading. Instead of having a boolean _isLoading_
we define it as a Set like this:
Set<int> _isLoading = {};
Then edit you _onSubmit
to also pass an index like
void _onSubmit(int index) {
and at the end of it
setState(() => _isLoading.add(index));
Future.delayed(
const Duration(seconds: 2),
() => setState(() => _isLoading.remove(index)),
);
And finally at your button instead of
icon: _isLoading
you write
icon: _isLoading.contains(index)
and also
onPressed: () => _onSubmit(index)