2

In my app the user is presented with a puzzle, drawn by a re-implementation of Flutter's CustomerPainter class and accepting moves made by tapping on the Canvas. The Canvas is continually updated and re-drawn to show the moves. When the user makes the last move and completes a correct solution, the app should tell the user so. The app is using Flutter's AlertDialog to issue such messages to the user.

The problem is that the app is deep in the paint() method of CustomPainter when a solution situation is detected. Any attempt to issue a message at that point crashes the app. The message to the user does appear, but by that time the app has crashed. The error messages are either:
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception:
setState() or markNeedsBuild() called during build.
or
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception:
Build scheduled during frame.

These messages are followed by comprehensive advice as to what has happened and what to do. There are also several suggestions on Stack Overflow, but I cannot get any of them to work.

If I write SchedulerBinding.instance.addPostFrameCallback in my widget-build it fails to compile because it wants several parts from BindingBase. SchedulerBinding is a mixin. If I try to mixin BindingBase to my Widget, compilation fails again because BindingBase is a class, with a constructor, not a mixin.

If I go the async way, using Future executeAfterBuild(), the app compiles but crashes again, apparently because the async function executes straight away (as is normal) and is not scheduled after the widget-build, as suggested in some of the Stack Overflow posts. The app is definitely not in setState() when it crashes, so maybe marksNeedsBuild() is triggering the crash somehow.

I am using Flutter 2.8.2 and Dart 2.15.1, plus sound null safety. Has Flutter's widget-scheduling policy changed? The Stack Overflow posts I refer to are up to 3 years old.

Ian Wadham
  • 21
  • 3

1 Answers1

0

have you tried putting setState in :

WidgetsBinding.instance?.addPostFrameCallback((_) {setState((){});})
Hooman
  • 753
  • 5
  • 13
  • Thank you, Hooman. Monkey see, monkey do. I inserted literally what you wrote in the build method of my State class. Surprisingly, the addPostFrameCallback line compiled (first time ever for me), but that sent the build method into an endless loop of setState-build... Well, I guess setState() triggers a rebuild, whether or not you actually change the State. – Ian Wadham Feb 08 '22 at 21:51
  • I found this excellent article that explains very well how widgets actually operate https://medium.com/flutter-community/widget-state-buildcontext-inheritedwidget-898d671b7956 by Didier Boelens. Back to the drawing-board! Meanwhile I have found that the async way works if you have a non-zero time delay and it will keep me going temporarily, but I think it is rather unsafe in the long run. – Ian Wadham Feb 08 '22 at 22:01
  • All fixed now, using addPostFrameCallback() to trigger a procedure that checks if an AlertDialog message is needed and, if so, pops it up over the Frame that was just now re-painted. – Ian Wadham Feb 10 '22 at 23:50