12
votes

I am new to Flutter and playing around with it. So, please be patient with me.

Following exception is thrown when clicking on a specific menu item of a PopupMenuButton, but always the second time only:

'package:flutter/src/widgets/navigator.dart': Failed assertion: line 1846 pos 12: '!_debugLocked': is not true.

Here the setup:

For specifying the menu items following class has been defined:

class PopupMenuChoice {
  const PopupMenuChoice({this.title, this.pageRoute});

  final String title;
  final MaterialPageRoute pageRoute;
}

Definition of the PopupMenuButton in actions property of an AppBar:

new PopupMenuButton<PopupMenuChoice>(
    itemBuilder: (BuildContext context) {
      return _popupMenus.map((PopupMenuChoice choice) {
        return new PopupMenuItem<PopupMenuChoice>(
          value: choice,
          child: new Text(choice.title),
        );
      }).toList();
    },
    onSelected: _popupMenuSelected,
),

Corresponding Widgets are defined in following class (the AppBar is created in "return new Scaffold" of this class):

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _saved = new Set<WordPair>();
  final _popupMenus = <PopupMenuChoice>[];

  ...
}

As you can see there are private variables for holding WordPair objects, but also for the menu choices.

The _popupMenus list is setup in the "build override":

@override
Widget build(BuildContext context) {
    // Setup page routes
    if (_popupMenus.where((p) => p.title == 'Saved Suggestions').length == 0) {
      final _pageRouteSavedSuggestions = new MaterialPageRoute(
        builder: (context) {
          final tiles = _saved.map(
            (pair) {
              return new ListTile(
                title: new Text(
                  pair.asPascalCase,
                  style: _biggerFont,
                ),
              );
            },
          );
          final divided = ListTile
              .divideTiles(
                context: context,
                tiles: tiles,
              )
              .toList();

          return new Scaffold(
            appBar: new AppBar(
              title: new Text('Saved Suggestions'),
            ),
            body: new ListView(children: divided),
          );
        },
      );
      _popupMenus.add(new PopupMenuChoice(
          title: 'Saved Suggestions', pageRoute: _pageRouteSavedSuggestions));
    }

    if (_popupMenus.where((p) => p.title == 'TEST Page').length == 0) {
      final _pageRouteTest = new MaterialPageRoute(
        builder: (context) {
          return new Scaffold(
            appBar: new AppBar(
              title: new Text('TEST Page'),
            ),
            body: new Text('Some content...'),
          );
        },
      );
      _popupMenus.add(
          new PopupMenuChoice(title: 'TEST Page', pageRoute: _pageRouteTest));
    }
    ...

In defined MaterialPageRoute of PopupMenuChoice private variables might be access (e.g. _saved).

Here corresponding event handler for onSelected of PopupMenuButton:

void _popupMenuSelected(PopupMenuChoice choice) {
  Navigator.of(context).push(choice.pageRoute);
}

Can anybody explain why this exception gets thrown? And how can it be prevented?

Thanks, Roger


Additional information from debug console when clicking the second time on specific menu item:

E/flutter (17133): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception: E/flutter (17133): 'package:flutter/src/widgets/routes.dart': Failed assertion: line 177 pos 12: '!_transitionCompleter.isCompleted': Cannot install a MaterialPageRoute after disposing it. E/flutter (17133): #0
_AssertionError._doThrowNew (dart:core/runtime/liberrors_patch.dart:37:39) E/flutter (17133): #1
_AssertionError._throwNew (dart:core/runtime/liberrors_patch.dart:33:5) E/flutter (17133): #2
TransitionRoute.install (package:flutter/src/widgets/routes.dart) E/flutter (17133): #3 ModalRoute.install (package:flutter/src/widgets/routes.dart:740:11) E/flutter (17133): #4 NavigatorState.push (package:flutter/src/widgets/navigator.dart:1444:11) E/flutter (17133): #5 RandomWordsState.build._popupMenuSelected (file:///D:/Flutter%20Projects/startup_namer/lib/main.dart:166:29) E/flutter (17133): #6
_PopupMenuButtonState.showButtonMenu. (package:flutter/src/material/popup_menu.dart) E/flutter (17133): #7
_RootZone.runUnary (dart:async/zone.dart:1381:54) E/flutter (17133): #8 _FutureListener.handleValue (dart:async/future_impl.dart:129:18) E/flutter (17133): #9
Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:633:45) E/flutter (17133): #10
Future._propagateToListeners (dart:async/future_impl.dart:662:32) E/flutter (17133): #11 Future._completeWithValue (dart:async/future_impl.dart:477:5) E/flutter (17133): #12
Future._asyncComplete. (dart:async/future_impl.dart:507:7) E/flutter (17133): #13
_microtaskLoop (dart:async/schedule_microtask.dart:41:21) E/flutter (17133): #14 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)

3
You're getting this error because you are trying to reuse a MaterialPageRoute after it's already been disposed (that's why it works the first time and then once you dispose it by closing out of it, you get this error the second time). You probably should be generating them as needed and not all at once upon initialization.Kirollos Morkos
@KirollosMorkos please have a look, I'm getting the same error hereFarhana Naaz Ansari

3 Answers

2
votes

Have you tried:

void _popupMenuSelected(PopupMenuChoice choice) {
  Navigator.push(context, choice.pageRoute);
}
1
votes
    void _popupMenuSelected(PopupMenuChoice choice) {
      await Future.delayed(const Duration(milliseconds: 100));
      Navigator.push(context, choice.pageRoute);
    }
0
votes

Suppose You have Auth Block , There You have isLoggedIn = false; and a method where signin network call happening :

Suppose you Design the method like this :

logIn(String userId,String Passwrod){
 url ....

if(response.body == 200){
isLoggedIn = true ;
return response.body;
}
}

Now In Your Auth page : The Problem is :

At the Begging of the page class you have checker :

(isLoggedIn){
Navigator.push(context, choice.pageRoute);
}

and When User Click On Submit button there is something like this :

(){
Navigator.push(context, choice.pageRoute);

}

Now The Problem is You get Both Called means LoggedIn = true and then the submit button response here is the problem . remove the initial

(isLoggedIn){
Navigator.push(context, choice.pageRoute);
}

Your Problem Will be Solved.

Use this Checker into Another Page Not at the Login Page