Looking up a deactivated widget's ancestor is unsafe using provider and snackbar

Viewed 310

I'm using DismissibleWidget to remove a note from a list. When the note is removed a Snackbar is called to give the chance for the user to undo the action. When the button is clicked, and the note is reinserted in the list, this error appears:

The following assertion was thrown while handling a gesture: Looking up a deactivated widget's ancestor is unsafe. At this point the state of the widget's element tree is no longer stable.

  1. The dismissible widget

                 Dismissible(
                    direction: DismissDirection.endToStart,
                    onDismissed: (direction){
                      var removedNote = list.notes[index];
                      Provider.of<ListProvider>(context, listen: false).removeNote(index);
    
                      Scaffold.of(context).showSnackBar(
                          SnackBar(
                            content: Text("Nota removida"),
                            action: SnackBarAction(
                              label: "Desfazer",
                              onPressed: (){
                                Provider.of<ListProvider>(context, listen: false).insertNote(removedNote, index);
                              },
                            ),
                            duration: Duration(seconds: 3),
                          ),
                      );
                    },
    

    ...

  2. The method in the provider

    void insertNote(NoteModel note, int index){
       notes.insert(index, note);
       fileRepository.saveToFile(notes);
       notifyListeners();
    

    }

1 Answers

I faced the some problem but with BLoC as sate management, my solution was taking the context from above that widget.

What went wrong?

You were trying to look for a context that is no longer available. This ocurrs also because of variable shadowing.

Solution?

Reach the content above that that widget.

Pseudo Flutter Code

class NameOfYourClass{
    build(BuildContext context1) {
      return ListView.builder(
          itemBuilder: (context2, index) {
             return MyItem(); 
          }
      );
    }
   }

Let's say you dismiss the MyItem with the Dismissible widget, the context2 will be no longer available, if you print the context2 it will look something like this(NOTE: When MyItem is Dismissed):

SliverList(delegate: SliverChildBuilderDelegate#66afb(estimated child count: 1), renderObject: RenderSliverList#593ed relayoutBoundary=up2 DETACHED)

As you can see, the item is DETACHED from the tree. If you print the context1 it will look something like this.

NameOfYourClass

So in conclusion, instead of doing this:

 Provider.of<ListProvider>(context2, listen: false).insertNote(removedNote, index);

Do this:

 Provider.of<ListProvider>(context1, listen: false).insertNote(removedNote, index);

Don't use context1 or context2, this is for illustration purposes.

Related