Home > Enterprise >  flutter future builder get Widget by index
flutter future builder get Widget by index

Time:12-07

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

enter image description here

 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) 
  • Related