flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.12k stars 27.43k forks source link

showDialog asked to be BugReported #91402

Closed Hu1buerger closed 3 years ago

Hu1buerger commented 3 years ago

Steps to Reproduce

  1. in a Widgets build call as follows
  2. The code

    showObsureEvilDialog(BuildContext context) async{
    String? selectedOption = await showDialog<String>(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: const Text("Search failed"),
            content: const Text("would you like to create the obscure evil?"),
            actions: <Widget>[
              TextButton(
                child: const Text("No"),
                onPressed: () => Navigator.of(context).pop("No"),
              ),
              TextButton(
                child: const Text("Yes"),
                onPressed: () => Navigator.of(context).pop("Yes"),
              )
            ],
          );
        });
    
    if(selectedOption == "Yes") Navigator.of(context).popAndPushNamed("NAMED_ROUTE_WHERE_OK_SHOULD_LEAD");
    }
  3. Examine [https://api.flutter.dev/flutter/material/AlertDialog-class.html] as this should be exactly the same

Expected results:

We expect that flutter upon button press the dialog gets terminated and dosnt throw and in some cases navigates us away

Actual results:

══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
'package:flutter/src/widgets/navigator.dart': Failed assertion: line 5098 pos 12: '!_debugLocked':
is not true.

When the exception was thrown, this was the stack:
#2      NavigatorState.pop (package:flutter/src/widgets/navigator.dart:5098:12)
#3      _AddLogEntryState.showObsureEvilDialog.<anonymous closure>.<anonymous closure> (package:obsurepackage/src/add_entrys/add_log_entry.dart:137:56)
#4      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
#5      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:193:24)
#6      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:608:11)
#7      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
#8      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7)
#9      PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:558:9)
#10     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:94:12)
#11     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:139:9)
#12     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:400:8)
#13     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:137:18)
#14     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:123:7)
#15     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:440:19)
#16     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:420:22)
#17     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:278:11)
#18     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
#19     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
#20     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:296:7)
#21     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:279:7)
#25     _invoke1 (dart:ui/hooks.dart:185:10)
#26     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:293:7)
#27     _dispatchPointerDataPacket (dart:ui/hooks.dart:98:31)
(elided 5 frames from class _AssertionError and dart:async)

Handler: "onTap"
Recognizer:
  TapGestureRecognizer#b726e
Logs can be provided if needed. ``` Analyzing obsureevil... info • Don't put any logic in createState • lib/src/add_entrys/add_log_entry.dart:28:42 • no_logic_in_create_state info • Prefer using lowerCamelCase for constant names • lib/src/add_entrys/add_log_entry.dart:33:20 • constant_identifier_names info • Dead code • lib/src/add_entrys/add_log_entry.dart:59:5 • dead_code info • Avoid `print` calls in production code • lib/src/add_entrys/add_log_entry.dart:89:15 • avoid_print info • Avoid `print` calls in production code • lib/src/add_entrys/add_log_entry.dart:93:15 • avoid_print info • Avoid `print` calls in production code • lib/src/add_entrys/add_log_entry.dart:108:21 • avoid_print 6 issues found. (ran in 3.1s) ``` ``` [✓] Flutter (Channel stable, 2.5.2, on macOS 11.6 20G165 darwin-x64, locale en-DE) • Flutter version 2.5.2 at /usr/local/Caskroom/flutter/2.5.1/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 3595343e20 (6 days ago), 2021-09-30 12:58:18 -0700 • Engine revision 6ac856380f • Dart version 2.14.3 [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/user/Library/Android/sdk • Platform android-31, build-tools 31.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165) • All Android licenses accepted. [✗] Xcode - develop for iOS and macOS ✗ Xcode installation is incomplete; a full installation is necessary for iOS development. Download at: https://developer.apple.com/xcode/download/ Or install Xcode via the App Store. Once installed, run: sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer sudo xcodebuild -runFirstLaunch ✗ CocoaPods not installed. CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side. Without CocoaPods, plugins will not work on iOS or macOS. For more info, see https://flutter.dev/platform-plugins To install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions. [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2020.3) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165) [✓] IntelliJ IDEA Ultimate Edition (version 2021.2.3) • IntelliJ at /Applications/IntelliJ IDEA.app • Flutter plugin version 60.1.4 • Dart plugin version 212.5486 [✓] IntelliJ IDEA Ultimate Edition (version EAP IU-213.4293.20) • IntelliJ at /Applications/IntelliJ IDEA 2021.3 EAP.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] VS Code (version 1.60.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension can be installed from: 🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter [✓] Connected device (2 available) • sdk gphone x86 (mobile) • emulator-5554 • android-x86 • Android 11 (API 30) (emulator) • Chrome (web) • chrome • web-javascript • Google Chrome 94.0.4606.71 ! Doctor found issues in 1 category. ```
danagbemava-nc commented 3 years ago

Hi @Hu1buerger, can you share the full sample that you are using?

I tried the sample in the link you provided but I was not getting any such errors.

Hu1buerger commented 3 years ago

I am pretty sure I used https://api.flutter.dev/flutter/material/AlertDialog-class.html#material.AlertDialog.2 yesterday.

And this https://api.flutter.dev/flutter/material/SimpleDialog-class.html#material.SimpleDialog.1 but I assume they should behave similarly as far as showDialog() is concerned.

Hu1buerger commented 3 years ago

While trying to get it working I encountered a similar exception with another cause.

$ flutter run --verbose
[...]
[   +1 ms] E/flutter ( 9699): The widget on which setState() or markNeedsBuild() was called was:
[        ] E/flutter ( 9699):   Overlay-[LabeledGlobalKey<OverlayState>#a4af6]
[        ] E/flutter ( 9699): The widget which was currently being built when the offending call was made was:
[        ] E/flutter ( 9699):   AddLogEntry
[        ] E/flutter ( 9699): #0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4305:11)
[        ] E/flutter ( 9699): #1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4320:6)
[        ] E/flutter ( 9699): #2      State.setState (package:flutter/src/widgets/framework.dart:1108:15)
[        ] E/flutter ( 9699): #3      OverlayState.rearrange (package:flutter/src/widgets/overlay.dart:438:5)
[        ] E/flutter ( 9699): #4      NavigatorState._flushHistoryUpdates (package:flutter/src/widgets/navigator.dart:4096:16)
[        ] E/flutter ( 9699): #5      NavigatorState._pushEntry (package:flutter/src/widgets/navigator.dart:4604:5)
[        ] E/flutter ( 9699): #6      NavigatorState.push (package:flutter/src/widgets/navigator.dart:4511:5)
[        ] E/flutter ( 9699): #7      showDialog (package:flutter/src/material/dialog.dart:1067:65)
[        ] E/flutter ( 9699): #8      _AddLogEntryState.showEvilDialog (package:evilcorp/src/add_entrys/add_log_entry.dart:85:36)
[        ] E/flutter ( 9699): #9      _AddLogEntryState.onSearchEmpty (package:evilcorp/src/add_entrys/add_log_entry.dart:75:27)
[        ] E/flutter ( 9699): #10     _AddLogEntryState.build (package:evilcorp/src/add_entrys/add_log_entry.dart:155:23)
[  +16 ms] E/flutter ( 9699): #11     StatefulElement.build (package:flutter/src/widgets/framework.dart:4782:27)
[        ] E/flutter ( 9699): #12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4665:15)
[        ] E/flutter ( 9699): #13     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4840:11)
[        ] E/flutter ( 9699): #14     Element.rebuild (package:flutter/src/widgets/framework.dart:4355:5)
[        ] E/flutter ( 9699): #15     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2620:33)
[        ] E/flutter ( 9699): #16     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
[        ] E/flutter ( 9699): #17     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:319:5)
[        ] E/flutter ( 9699): #18     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1143:15)
[  +44 ms] E/flutter ( 9699): #19     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1080:9)
[        ] E/flutter ( 9699): #20     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:996:5)
[        ] E/flutter ( 9699): #21     _rootRun (dart:async/zone.dart:1428:13)
[        ] E/flutter ( 9699): #22     _CustomZone.run (dart:async/zone.dart:1328:19)
[        ] E/flutter ( 9699): #23     _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
[        ] E/flutter ( 9699): #24     _invoke (dart:ui/hooks.dart:166:10)
[        ] E/flutter ( 9699): #25     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
[        ] E/flutter ( 9699): #26     _drawFrame (dart:ui/hooks.dart:129:31)
[        ] E/flutter ( 9699): 
[        ] I/flutter ( 9699): QueryField changed to Nap
[   +4 ms] I/TextInputPlugin( 9699): Composing region changed by the framework. Restarting the input method.
[  +78 ms] E/flutter ( 9699): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 3029 pos 18:
'!navigator._debugLocked': is not true.
[   +2 ms] E/flutter ( 9699): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)
[        ] E/flutter ( 9699): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
[        ] E/flutter ( 9699): #2      _RouteEntry.handlePush.<anonymous closure> (package:flutter/src/widgets/navigator.dart:3029:18)
[        ] E/flutter ( 9699): #3      TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart:407:15)
[        ] E/flutter ( 9699): #4      _rootRunUnary (dart:async/zone.dart:1436:47)
[        ] E/flutter ( 9699): #5      _CustomZone.runUnary (dart:async/zone.dart:1335:19)
[        ] E/flutter ( 9699): <asynchronous suspension>
[ +138 ms] E/flutter ( 9699): #6      TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart)
[        ] E/flutter ( 9699): <asynchronous suspension>
[        ] E/flutter ( 9699): 
[        ] W/IInputConnectionWrapper( 9699): getTextBeforeCursor on inactive InputConnection
[        ] W/IInputConnectionWrapper( 9699): getSelectedText on inactive InputConnection
[        ] W/IInputConnectionWrapper( 9699): getTextAfterCursor on inactive InputConnection
[        ] W/IInputConnectionWrapper( 9699): beginBatchEdit on inactive InputConnection
[        ] W/IInputConnectionWrapper( 9699): getTextBeforeCursor on inactive InputConnection
[        ] W/IInputConnectionWrapper( 9699): endBatchEdit on inactive InputConnection
[ +404 ms] E/flutter ( 9699): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 4595 pos 12: '!_debugLocked': is not
true.
[        ] E/flutter ( 9699): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)
[        ] E/flutter ( 9699): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
[        ] E/flutter ( 9699): #2      NavigatorState._pushEntry (package:flutter/src/widgets/navigator.dart:4595:12)
[        ] E/flutter ( 9699): #3      NavigatorState.push (package:flutter/src/widgets/navigator.dart:4511:5)
[        ] E/flutter ( 9699): #4      showDialog (package:flutter/src/material/dialog.dart:1067:65)
[        ] E/flutter ( 9699): #5      _AddLogEntryState.showEvilDialog (package:evilcorp/src/add_entrys/add_log_entry.dart:85:36)
[        ] E/flutter ( 9699): #6      _AddLogEntryState.onSearchEmpty (package:evilcorp/src/add_entrys/add_log_entry.dart:75:27)
[        ] E/flutter ( 9699): #7      _AddLogEntryState.build (package:evilcorp/src/add_entrys/add_log_entry.dart:155:23)
[        ] E/flutter ( 9699): #8      StatefulElement.build (package:flutter/src/widgets/framework.dart:4782:27)
[   +4 ms] E/flutter ( 9699): #9      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4665:15)
[        ] E/flutter ( 9699): #10     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4840:11)
[        ] E/flutter ( 9699): #11     Element.rebuild (package:flutter/src/widgets/framework.dart:4355:5)
[        ] E/flutter ( 9699): #12     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2620:33)
[        ] E/flutter ( 9699): #13     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
[        ] E/flutter ( 9699): #14     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:319:5)
[        ] E/flutter ( 9699): #15     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1143:15)
[        ] E/flutter ( 9699): #16     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1080:9)
[  +33 ms] E/flutter ( 9699): #17     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:996:5)
[        ] E/flutter ( 9699): #18     _rootRun (dart:async/zone.dart:1428:13)
[        ] E/flutter ( 9699): #19     _CustomZone.run (dart:async/zone.dart:1328:19)
[        ] E/flutter ( 9699): #20     _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
[        ] E/flutter ( 9699): #21     _invoke (dart:ui/hooks.dart:166:10)
[        ] E/flutter ( 9699): #22     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
[        ] E/flutter ( 9699): #23     _drawFrame (dart:ui/hooks.dart:129:31)
[        ] E/flutter ( 9699): 

FYI: evilcorp or evildialog are not the given names but will be used to redact

danagbemava-nc commented 3 years ago

Hi @Hu1buerger, I'm asking for your code because I assume your use case isn't just a simple button to show a dialog.

Also, the errors you're showing are indicative of an implementation issue. It looks to me like you're trying to show a dialog before the widget is built, but without your minimal reproducible sample, I can't confirm if this is true.

Hu1buerger commented 3 years ago

Sure will do. Will tag you when I release the code

Hu1buerger commented 3 years ago

@danagbemava-nc

import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:evilcorp/src/administration_log/administration_log_controller.dart';

/// Supports adding a logentry to the log
/// 
class AddLogEntry extends StatefulWidget {
  static const String routeName = "/addLogEntry";

  final OptionsProvider optsProvider;

  AddLogEntry({Key? key, required this.optsProvider})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => _AddLogEntryState(optsProvider);
}

class _AddLogEntryState extends State<AddLogEntry> {
  static const String title = "Add evilcomposite to the log";
  static const int max_options = 10;
  static const String DIALOG_ADD_OK = "Yes";
  static const String DIALOG_ADD_ABORT = "No";

  final OptionsProvider optsProvider;

  bool get searchFailed => currentOptions.isEmpty;

  TextEditingController searchQueryController = TextEditingController();
  List<Model> currentOptions = <Model>[];

  _AddLogEntryState(this.optsProvider);

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  List<Model> filterList(String query) =>
      optsProvider.options.where((element) => element.optionName.contains(query)).toList();

  void onCancel(BuildContext context) {
    Navigator.pop(context);
  }

  void onDone(BuildContext context) {
    Navigator.pop(context);
  }

  onSearchEmpty(BuildContext context) async {
    String result = await showEvilDialog(context);
    if (result == DIALOG_ADD_OK) Navigator.of(context).popAndPushNamed("SomeWhereToHANDLETheAddEvilOptionShit");
  }

  updateQuery(String query) {
    currentOptions = optsProvider.query(query);
    setState(() {});
  }

  Future<String> showEvilDialog(BuildContext context) async {
    String? selectedOption = await showDialog<String>(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: const Text("Search failed"),
            content: const Text("would you like to create the eviloption"),
            actions: <Widget>[
              TextButton(
                child: const Text("No"),
                onPressed: () => Navigator.of(context).pop(DIALOG_ADD_ABORT),
              ),
              TextButton(
                child: const Text("Yes"),
                onPressed: () => Navigator.of(context).pop(DIALOG_ADD_OK),
              )
            ],
          );
        });

    return selectedOption!;
  }

  Widget buildSearchWindow(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
          child: TextField(
            controller: searchQueryController,
            decoration: const InputDecoration(
                border: OutlineInputBorder(), hintText: 'Enter a search term', prefixIcon: Icon(Icons.search)),
            autocorrect: false,
            onChanged: (String value) {
              print("QueryField changed to $value");
              updateQuery(value);
            },
            onEditingComplete: () {
              print("Editing complete with ${searchQueryController.text}");
            },
          ),
        ),
        Expanded(
            child: Card(
              child: ListView.builder(
                  itemCount: min(max_options, currentOptions.length),
                  itemBuilder: (BuildContext context, int index) {
                    var currentItem = currentOptions[index];

                    return ListTile(
                      title: Text(currentItem.titleName),
                      subtitle: Text(currentItem.subTitleName),
                      onTap: () {
                        print("taped on ${currentItem.optionName}");
                      },
                    );
                  }),
            )),
        Padding(
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: const [Text("later"), Text("now")],
            ))
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    if (searchFailed) onSearchEmpty(context);

    // TODO: implement build
    return Scaffold(
        appBar: AppBar(
          title: const Text(title),
          actions: [
            IconButton(
              icon: const Icon(Icons.restart_alt_outlined),
              onPressed: () => onCancel(context),
            ),
            IconButton(
              icon: const Icon(Icons.done),
              onPressed: () => onDone(context),
            ),
          ],
        ),
        body: buildSearchWindow(context));
  }
}

This is the current version. I would have liked to use an Autocompleter but the option searched and the layout of this widget, with the future of using a Backdrop, and the issue of using a composite datatype kinda didn't work out.

danagbemava-nc commented 3 years ago

The issue is from if (searchFailed) onSearchEmpty(context); or rather where it is placed.

You can't show a dialog in the main build body. if your intent is to show the dialog after the user is done with entering their input, you can call the dialog from the onEditingComplete.

If you are using one of the many state management solutions we have in flutter, you can use that to handle it. Most of the state management libraries that we have have a Listener of some sort that allows you to listen to changes on a value and perform some action based on that value.

Hu1buerger commented 3 years ago

Sound good. Did think that that was the issue. Is already fixed. but shouldn't that be documented either for the navigator or the build method.

Hu1buerger commented 3 years ago

Is there a method that gets called after the build has finished?

danagbemava-nc commented 3 years ago

Yes, you can find more information on the Widget Lifecycle here https://medium.com/flutterdevs/explore-widget-lifecycle-in-flutter-e36031c697d0.

May I ask what you intend to do with that method?

Hu1buerger commented 3 years ago

I could imagine either calling the showDialog from the function that changes the state but without calling setState. That would unfortunately result in not displaying the latest state on ui. Or checking wether to display the dialog after the build was being performed

@danagbemava-nc I believe there is no method that gets called after each build

github-actions[bot] commented 3 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.