You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Crash when Toggleable widget disappears while pointer is down #76411
Thread 0 main thread_CastError: Null check operator used on a null value
at RenderObject.sendSemanticsEvent(object.dart:2503)
at RenderToggleable._handleTap(toggleable.dart:429)
at GestureRecognizer.invokeCallback(recognizer.dart:183)
at TapGestureRecognizer.handleTapUp(tap.dart:598)
at BaseTapGestureRecognizer._checkUp(tap.dart:302)
at BaseTapGestureRecognizer.acceptGesture(tap.dart:259)
at GestureArenaManager.sweep(arena.dart:157)
at GestureBinding.handleEvent(binding.dart:385)
at GestureBinding.dispatchEvent(binding.dart:361)
at RendererBinding.dispatchEvent(binding.dart:279)
at GestureBinding._handlePointerEventImmediately(binding.dart:316)
at GestureBinding._flushPointerEventQueue(binding.dart:280)
at GestureBinding._handlePointerDataPacket(binding.dart:221)
at _rootRunUnary(zone.dart:1206)
at _CustomZone.runUnary(zone.dart:1100)
at _CustomZone.runUnaryGuarded(zone.dart:1005)
at _invoke1(hooks.dart:172)
at PlatformDispatcher._dispatchPointerDataPacket(platform_dispatcher.dart:279)
at _dispatchPointerDataPacket(hooks.dart:96)
PointerDown is routed to the tap recognizer owned by the RenderToggleable.
Tap recognizer registers a route with the PointerRouter to receive all events for the pointer that caused the down event.
The RenderToggleable is detached from the tree (the tap recognizer doesn't know about this, its registered route stays in place).
The PointerUp event is dispatched to the tap recognizer via the registered route. The tap handler set by the RenderToggleable is called.
Tap handler calls sendSemanticsEvent (which assumes the render object is attached) and crashes.
The tap handler also calls other user provided callbacks, which may also crash (depending on what they do) if they are called in the detached state...
If we would be properly disposing the tap recognizer, it would unregister its pointer route and the crash would go away. Problem: There's no good point in the life cycle of the RenderObject to call dispose. Disposing in detach is not great because during global key reparenting, the render object could be attached again. If we instantiate a new TapRecognizer in attach, we wouldn't be able to detect a gesture that started before reparenting and ends after.
We probably have to move the ownership of the tap controller to e.g. a State object and dispose it in the State's dispose.
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.
Activity
goderbauer commentedon Feb 19, 2021
Test case to reproduce:
This crash appears to affect all widgets that are backed by the RenderToggleable: Checkbox, Switch, and Radio.
goderbauer commentedon Feb 19, 2021
The culprit is that the RenderToggleable owns a TapGestureRecognizer, which is never disposed:
flutter/packages/flutter/lib/src/material/toggleable.dart
Lines 63 to 67 in 75470a0
We now observe the following sequence of events:
The tap handler also calls other user provided callbacks, which may also crash (depending on what they do) if they are called in the detached state...
If we would be properly disposing the tap recognizer, it would unregister its pointer route and the crash would go away. Problem: There's no good point in the life cycle of the RenderObject to call dispose. Disposing in
detachis not great because during global key reparenting, the render object could be attached again. If we instantiate a new TapRecognizer inattach, we wouldn't be able to detect a gesture that started before reparenting and ends after.We probably have to move the ownership of the tap controller to e.g. a State object and dispose it in the State's dispose.
goderbauer commentedon Feb 19, 2021
This problem has also been discussed in #49999 (comment). The context there was a little different (memory leaks).
In #51606 the CupertinoSwitch was refactored to move the gesture recognizer out of the renderobject into a state object.
goderbauer commentedon Feb 19, 2021
Related: #14204
goderbauer commentedon Mar 3, 2021
The fix was reverted due to a failure in devicelab. Fixing that looks straight-forward. Hoping that I can re-apply the fix this week.
goderbauer commentedon Mar 4, 2021
The re-land is staged at #77263.
github-actions commentedon Aug 5, 2021
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 -vand a minimal reproduction of the issue.