Skip to content

Exception on build if constraints changed. #2162

@jonl-percsolutions-com

Description

@jonl-percsolutions-com

What is the bug?

An exception can occur in MapInteractiveViewer.onMapStateChange if the constraints changed during a rebuild. We have seen this in 8.1.1 - 8.2.2.

The following assertion was thrown while dispatching notifications for MapControllerImpl:
setState() or markNeedsBuild() called during build.

This MapInteractiveViewer widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: MapInteractiveViewer
  dependencies: [MediaQuery]
  state: MapInteractiveViewerState#aa93f(tickers: tracking 18 tickers)
The widget which was currently being built when the offending call was made was: LayoutBuilder
  renderObject: _RenderLayoutBuilder#d5341 relayoutBoundary=up9 NEEDS-LAYOUT NEEDS-PAINT
When the exception was thrown, this was the stack: 
#0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:5289:9)
#1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:5301:6)
#2      State.setState (package:flutter/src/widgets/framework.dart:1227:15)
#3      MapInteractiveViewerState.onMapStateChange (package:flutter_map/src/gestures/map_interactive_viewer.dart:182:5)
#4      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:439:24)
#5      ValueNotifier.value= (package:flutter/src/foundation/change_notifier.dart:564:5)
#6      MapControllerImpl.value= (package:flutter_map/src/map/controller/map_controller_impl.dart:81:49)
#7      MapControllerImpl.setNonRotatedSizeWithoutEmittingEvent (package:flutter_map/src/map/controller/map_controller_impl.dart:321:7)
#8      _FlutterMapStateContainer._updateAndEmitSizeIfConstraintsChanged (package:flutter_map/src/map/widget.dart:135:24)
#9      _FlutterMapStateContainer.build.<anonymous closure> (package:flutter_map/src/map/widget.dart:98:11)
#10     _LayoutBuilderElement._rebuildWithConstraints.updateChildCallback (package:flutter/src/widgets/layout_builder.dart:201:77)
#11     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:3056:19)
#12     _LayoutBuilderElement._rebuildWithConstraints (package:flutter/src/widgets/layout_builder.dart:240:12)
#13     RenderObject.invokeLayoutCallback.<anonymous closure> (package:flutter/src/rendering/object.dart:2827:17)
#14     PipelineOwner._enableMutationsToDirtySubtrees (package:flutter/src/rendering/object.dart:1161:15)
#15     RenderObject.invokeLayoutCallback (package:flutter/src/rendering/object.dart:2826:14)
#16     RenderConstrainedLayoutBuilder.rebuildIfNecessary (package:flutter/src/widgets/layout_builder.dart:293:5)
#17     _RenderLayoutBuilder.performLayout (package:flutter/src/widgets/layout_builder.dart:390:5)
#18     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#19     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:18)
#20     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#21     RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:243:12)
#22     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#23     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:18)
#24     _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1483:11)
#25     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#26     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:18)
#27     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#28     ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:62:11)
#29     RenderStack._computeSize (package:flutter/src/rendering/stack.dart:646:43)
#30     RenderStack.performLayout (package:flutter/src/rendering/stack.dart:673:12)
#31     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#32     RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:293:14)
#33     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#34     RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:243:12)
#35     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#36     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:18)
#37     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#38     ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:62:11)
#39     RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:1161:28)
#40     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:1255:32)
#41     RenderObject.layout (package:flutter/src/rendering/object.dart:2715:7)
#42     _RenderLayoutBuilder.performLayout (package:flutter/src/widgets/layout_builder.dart:392:14)
#43     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:2548:7)
#44     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1112:18)
#45     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1125:15)
#46     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:616:23)
#47     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:1231:13)
#48     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:482:5)
#49     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1442:15)
#50     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1355:9)
#51     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1208:5)
#52     _invoke (dart:ui/hooks.dart:316:13)
#53     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:428:5)
#54     _drawFrame (dart:ui/hooks.dart:288:31)

How can we reproduce it?

It is intermittent to reproduce, but the exception is clear.

If _FlutterMapStateContainer._updateAndEmitSizeIfConstraintsChanged actually detects a change, and the MapInteractiveViewer.onMapStateChange is attached as a listener, then this will cause a "setState" call to be invoked during the build process.

Do you have a potential solution?

Best solution is likely to modify the behavior of _FlutterMapStateContainer._updateAndEmitSizeIfConstraintsChanged` so the whole check occurs in a postFrameCallback

May also need to modify the behavior MapInteractiveViewerState.onMapStateChange so that setState is called within a post frame callback.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions