Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter setState() or markNeedsBuild() called when widget tree was locked

I am having trouble finding the source to this exception, and the app is quite complex so it is hard to show any relevant part of the code. This is my repository at the point where the error happens: Github repo

I suspect the following code might be the perpetrator:

Widget buildResultCard(Map result, BuildContext context) {     String name = result["value"];     String description =         result["label"].replaceAll(new RegExp(r"<(?:.|\n)*?>"), "");     TextEditingController controller = new TextEditingController(text: name);      Function onPressed = () {       showDialog(           context: context,           child: new AlertDialog(             title: new Text("Name your schedule"),             content: new TextField(               autofocus: true,               controller: controller,             ),             actions: <Widget>[               new FlatButton(                   onPressed: () {                     String givenName = controller.text;                     ScheduleMeta schedule = new ScheduleMeta(                         givenName: givenName,                         name: name,                         type: _selectedChoice.value,                         description: description);                      scheduleStore                         .dispatch(new AddScheduleAction(schedule: schedule));                      scheduleStore.dispatch(                         new SetCurrentScheduleAction(schedule: schedule));                      fetchAllSchedules(scheduleStore.state.schedules)                         .then((weeks) {                       scheduleStore.dispatch(                           new SetWeeksForCurrentScheduleAction(weeks: weeks));                     });                      Scaffold.of(context).showSnackBar(new SnackBar(                           content: new Text("Added " + givenName),                           action: new SnackBarAction(                               label: "Undo",                               onPressed: () {                                 scheduleStore.dispatch(                                     new RemoveScheduleAction(schedule: name));                                  Scaffold.of(context).showSnackBar(new SnackBar(                                       content: new Text(                                           "Deleted " + givenName),                                     ));                               }),                         ));                      Navigator.of(context).pop();                   },                   child: new Text("Add")),             ],           ));     };      return new Card(       child: new Column(         mainAxisSize: MainAxisSize.min,         children: <Widget>[           new ListTile(             leading: const Icon(Icons.schedule),             title: new Text(name),             subtitle: new Text(description),             isThreeLine: true,             dense: true,           ),           new ButtonTheme.bar(             child: new ButtonBar(               children: <Widget>[                 new FlatButton(                     child: const Text('Add Schedule'),                     onPressed: scheduleStore.state.schedules                             .any((schedule) => schedule.name == name)                         ? null                         : onPressed)               ],             ),           ),         ],       ),     );   } 

The error appears when showing a dialog, and when the user presses a button on the dialog, two Redux store dispatches are sent directly after each other. The UI behind the dialog subscribes to changes in the Redux store.

I thought Dart/Flutter was single-threaded so no collision like this could happen, where it seems like a thread is calling setState() on a widget while another has put a lock on the widget tree.

Is there a way of checking if the widget tree is locked so this can be avoided?

The stack provides this info:

I/flutter (13466): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ I/flutter (13466): The following assertion was thrown while finalizing the widget tree: I/flutter (13466): setState() or markNeedsBuild() called when widget tree was locked. I/flutter (13466): This _ModalScope widget cannot be marked as needing to build because the framework is locked. I/flutter (13466): The widget on which setState() or markNeedsBuild() was called was: I/flutter (13466):   _ModalScope([LabeledGlobalKey<_ModalScopeState>#cb4cc]; state: _ModalScopeState#d4c02()) I/flutter (13466):  I/flutter (13466): When the exception was thrown, this was the stack: I/flutter (13466): #0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3250) I/flutter (13466): #2      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3226) I/flutter (13466): #3      State.setState (package:flutter/src/widgets/framework.dart:1072) I/flutter (13466): #4      _ModalScopeState._routeSetState (package:flutter/src/widgets/routes.dart:473) I/flutter (13466): #5      ModalRoute.setState (package:flutter/src/widgets/routes.dart:552) I/flutter (13466): #6      ModalRoute.changedInternalState (package:flutter/src/widgets/routes.dart:889) I/flutter (13466): #7      TransitionRoute&&LocalHistoryRoute.removeLocalHistoryEntry (package:flutter/src/widgets/routes.dart:317) I/flutter (13466): #8      LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:267) I/flutter (13466): #9      DrawerControllerState.dispose (package:flutter/src/material/drawer.dart:147) I/flutter (13466): #10     StatefulElement.unmount (package:flutter/src/widgets/framework.dart:3550) I/flutter (13466): #11     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1626) I/flutter (13466): #12     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #13     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #14     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #15     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #16     MultiChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4421) I/flutter (13466): #17     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #18     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #19     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #20     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #21     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #22     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #23     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #24     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #25     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4321) I/flutter (13466): #26     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #27     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #28     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #29     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #30     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #31     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4321) I/flutter (13466): #32     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #33     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #34     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #35     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #36     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #37     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #38     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #39     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #40     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #41     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #42     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #43     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #44     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #45     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624) I/flutter (13466): #46     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427) I/flutter (13466): #47     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622) I/flutter (13466): #48     _InactiveElements._unmountAll (package:flutter/src/widgets/framework.dart:1636) I/flutter (13466): #49     BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:2228) I/flutter (13466): #50     BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2060) I/flutter (13466): #51     BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:2227) I/flutter (13466): #52     BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:505) I/flutter (13466): #53     BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:189) I/flutter (13466): #54     BindingBase&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:688) I/flutter (13466): #55     BindingBase&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:636) I/flutter (13466): #56     _drawFrame (file:///b/build/slave/Linux_Engine/build/src/flutter/lib/ui/hooks.dart:70) I/flutter (13466): (elided one frame from class _AssertionError) I/flutter (13466): ════════════════════════════════════════════════════════════════════════════════════════════════════ 
like image 806
Schwusch Avatar asked Jul 31 '17 07:07

Schwusch


2 Answers

Special case of using Scaffold and Drawer:

That fail can also happen if you try to rebuild the tree with opened Drawer. For example if you send a message to a Bloc that forces rebuilding of the whole page/screen.

Consider to call Navigator.pop(context) first in your tap handler.

like image 136
17 revs, 13 users 59% Avatar answered Oct 05 '22 14:10

17 revs, 13 users 59%


As a workaround wrap your code that calling setState into WidgetsBinding.addPostFrameCallback:

WidgetsBinding.instance         .addPostFrameCallback((_) => setState(() {})); 

That way you can be sure it gets executed after the current widget is built.

like image 20
Andrey Gordeev Avatar answered Oct 05 '22 16:10

Andrey Gordeev