Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled Exception: '_currentHandle != null && _materializer != null': is not true. #76

Open
wilsonowilson opened this issue Aug 29, 2021 · 7 comments

Comments

@wilsonowilson
Copy link

In my application, I update the Window menu whenever a ChangeNotifier notifies its listeners. It looks something like this:

  @override
  void initState() {
    super.initState();
    notifier.addListener(_onDocumentOrComposerUpdated);
  }
  void _onDocumentOrComposerUpdated() {
      window.setWindowMenu(Menu(_buildMenu));
  }

This works, but I keep getting this error in the console:

Error and Stacktrace
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: 'package:nativeshell/src/menu_internal.dart': Failed assertion: line 118 pos 12: '_currentHandle != null && _materializer != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
#2      MenuState._doUnmaterialize (package:nativeshell/src/menu_internal.dart:118:12)
#3      MenuState._unmaterializeLocked (package:nativeshell/src/menu_internal.dart:114:28)
#4      MenuState.unmaterialize.<anonymous closure> (package:nativeshell/src/menu_internal.dart:29:32)
#5      Mutex.protect (package:nativeshell/src/mutex.dart:15:34)
<asynchronous suspension>
#6      MenuState.unmaterialize (package:nativeshell/src/menu_internal.dart:29:5)
<asynchronous suspension>
#7      LocalWindow.setWindowMenu (package:nativeshell/src/window.dart:253:7)
<asynchronous suspension>
@knopp
Copy link
Contributor

knopp commented Aug 29, 2021

This seems like a bug. A minimal reproducible example would help. However if you need to update menu, just store the menu instance (that you set as window menu) and call menu.update(). It is much better that way, as it will only update the parts of platform menu that have actually changed.

I.e.

  late Menu menu;
  @override
  void initState() {
    super.initState();
    menu = Menu(_buildMenu);
    window.setWindowMenu(menu);
    notifier.addListener(_onDocumentOrComposerUpdated);
  }
  void _onDocumentOrComposerUpdated() {
      menu.update();
  }

@wilsonowilson
Copy link
Author

wilsonowilson commented Aug 29, 2021

I'll try to come up with a reproducible sample of the bug.
I just tried menu.update(). It doesn't update the menu for some reason.

@knopp
Copy link
Contributor

knopp commented Aug 29, 2021

menu.update() definitely should update the menu. If it doesn't for any reason it is a bug. If you can provide any code that I can use to reproduce the problem that'd be great!

@wilsonowilson
Copy link
Author

Here's something very similar to what I'm doing. The command of interest is the "save" command. If the text controller's selection is collapsed, then its action is null, otherwise, it's set to a callback. Setting a new menu every time works (use that to reproduce the previous issue), but not using menu update.

class TextFieldMenuBuilder extends StatefulWidget {
  const TextFieldMenuBuilder({
    Key? key,
    required this.child,
  }) : super(key: key);

  final Widget child;

  @override
  _TextFieldMenuBuilderState createState() => _TextFieldMenuBuilderState();
}

class _TextFieldMenuBuilderState extends State<TextFieldMenuBuilder> {
  late Menu _menu;
  late final TextEditingController controller;

  @override
  void initState() {
    super.initState();
    controller = TextEditingController();
    controller.addListener(_onTextOrSelectionChanged);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _menu = Menu(_buildMenu);
    Window.of(context).setWindowMenu(_menu);
  }

  void _onTextOrSelectionChanged() {
    _menu.update();
  }

  List<MenuItem> _buildMenu() {
    return [
      if (Platform.isMacOS)
        MenuItem.children(
          title: 'App',
          children: [
            MenuItem.withRole(role: MenuItemRole.hide),
          ],
        ),
      MenuItem.children(
        title: '&File',
        children: [
          MenuItem(title: 'New', accelerator: cmdOrCtrl + 'n', action: null),
          MenuItem.separator(),
          // COMMAND OF INTEREST
          MenuItem(
            title: 'Save',
            accelerator: cmdOrCtrl + 's',
            action: controller.selection.isCollapsed ? null : () {},
          ),
        ],
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: TextField(
        controller: controller,
      ),
    );
  }
}

@knopp
Copy link
Contributor

knopp commented Aug 29, 2021

Thank you for the snippet. I was able to reproduce the problem. The issue is that NativeShell doesn't detect change from enabled to disabled, so it doesn't update the menu. If you for example modify the menu item title, i.e.

        MenuItem(
            title: controller.selection.isCollapsed ? 'Save_' : 'Save', // change title when section changes
            accelerator: cmdOrCtrl + 's',
            action: controller.selection.isCollapsed ? null : () {},
          ),

it will work as expected. Shouldn't be too difficult to fix.

@knopp
Copy link
Contributor

knopp commented Aug 29, 2021

Should be fixed by 869b4a8. You can try to specify the revision in your pubspec.yaml to see if it works for you.

@wilsonowilson
Copy link
Author

It works great! Thanks!

@knopp knopp closed this as completed Aug 29, 2021
@knopp knopp reopened this Aug 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants