skip to Main Content

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?

Image

My code:

 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(),

2

Answers


  1. 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),
        );
      }
    
    Login or Signup to reply.
  2. 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) 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search