Fix: iOS node name in onPanEnd/onRotationEnd + null-safety (replicates upstream #31) - #4
Conversation
…acks - Fixed serializeLocalTransformation to handle nil nodes gracefully - Changed transform array from optional to non-optional Float array - Changed node name from optional to non-optional String - Store node reference before clearing panningNode to prevent nil access - Only invoke onPanEnd/onRotationEnd if node exists This fixes the issue where call.arguments["name"] was nil, causing the Dart callback handler to fail silently in the catch block.
- Added defensive null check for name before creating transformMap - Only invoke callback if name exists (consistent with iOS fix) - Prevents potential crashes when name is null from nodeData["name"]
There was a problem hiding this comment.
Code Review
This pull request introduces defensive checks in both the Android and iOS implementations of the AR Flutter plugin to prevent crashes and unexpected behaviors when nodes or their names are null during pan and rotation gestures. On Android, callbacks are only invoked if the node name is present. On iOS, the panning node reference is preserved before being cleared, and a guard statement is added to serializeLocalTransformation to return an empty dictionary if the node is nil. The reviewer suggested safely unwrapping the serialized transformation dictionary before invoking the platform channel methods on iOS to avoid sending null arguments to Flutter.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if let node = nodeToSerialize { | ||
| DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onPanEnd", arguments: serializeLocalTransformation(node: node))} | ||
| } |
There was a problem hiding this comment.
With serializeLocalTransformation updated to return an optional dictionary, we should safely unwrap the serialized transformation dictionary before invoking the platform channel method. This prevents sending nil arguments to Flutter, which would otherwise trigger a NoSuchMethodError when Dart tries to access keys on a null arguments object.
| if let node = nodeToSerialize { | |
| DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onPanEnd", arguments: serializeLocalTransformation(node: node))} | |
| } | |
| if let node = nodeToSerialize, let transformDict = serializeLocalTransformation(node: node) { | |
| DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onPanEnd", arguments: transformDict)} | |
| } |
| if let node = nodeToSerialize { | ||
| DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onRotationEnd", arguments: serializeLocalTransformation(node: node))} | ||
| } |
There was a problem hiding this comment.
Similarly to onPanEnd, we should safely unwrap the serialized transformation dictionary returned by serializeLocalTransformation before invoking onRotationEnd to avoid sending nil arguments and causing a crash on the Flutter side.
| if let node = nodeToSerialize { | |
| DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onRotationEnd", arguments: serializeLocalTransformation(node: node))} | |
| } | |
| if let node = nodeToSerialize, let transformDict = serializeLocalTransformation(node: node) { | |
| DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onRotationEnd", arguments: transformDict)} | |
| } |
Review — VERDICT: ✅ LGTM (with nit)Replicates upstream hlefe#31. Swift+Kotlin; not compilable by
Safe to merge. |
Replicates upstream PR hlefe#31 by @nikkon404.
Change: iOS
serializeLocalTransformationnow guards against a nil node and uses non-optional transform/name;onPanEnd/onRotationEndcapture the node before clearingpanningNodeand only fire when non-nil. Also adds a matching defensive null check on Android (name?.let { ... }).Original author: nikkon404. Upstream PR: hlefe#31