Skip to content

Fix getCameraPose() on Android (replicates upstream #19) - #2

Merged
iamEtornam merged 2 commits into
mainfrom
upstream-pr-19-android-camerapose
Jul 8, 2026
Merged

Fix getCameraPose() on Android (replicates upstream #19)#2
iamEtornam merged 2 commits into
mainfrom
upstream-pr-19-android-camerapose

Conversation

@iamEtornam

Copy link
Copy Markdown
Owner

Replicates upstream PR hlefe#19 by @anshgandhi4.

Change: ARSessionManager.getCameraPose() now branches on platform — on Android it reads the pose from a Map (position + rotation quaternion) and composes a Matrix4, while iOS keeps the serialized-list path. Also adds .DS_Store to .gitignore and removes committed .DS_Store files.

Original author: anshgandhi4. Upstream PR: hlefe#19

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds platform-specific handling for retrieving the camera pose on Android in ARSessionManager, parsing position and rotation maps to compose a Matrix4. Feedback focuses on making this implementation more robust by avoiding unsafe null assertions (!) on dynamic map lookups and safely casting numeric coordinates to double to prevent runtime type errors. Additionally, it is recommended to use defaultTargetPlatform instead of Platform.isAndroid to improve testability and web compatibility, which also renders the new dart:io import unnecessary.

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.

Comment on lines +53 to +64
if (Platform.isAndroid) {
final serializedCameraPose = await _channel.invokeMethod<Map<dynamic, dynamic>>('getCameraPose', {});
final position = serializedCameraPose!['position'];
final rotation = serializedCameraPose!['rotation'];

final translation = Vector3(position!['x'], position!['y'], position!['z']);
final quaternion = Quaternion(rotation!['x'], rotation!['y'], rotation!['z'], rotation!['w']);
return Matrix4.compose(translation, quaternion, Vector3.all(1.0));
} else {
final serializedCameraPose = await _channel.invokeMethod<List<dynamic>>('getCameraPose', {});
return MatrixConverter().fromJson(serializedCameraPose!);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using the null assertion operator (!) repeatedly on dynamic map lookups (e.g., serializedCameraPose!['position'] and position!['x']) is risky and can easily lead to runtime crashes if any key is missing or null.

Additionally, values returned from platform channels for numeric coordinates can sometimes be serialized as int (e.g., 0 instead of 0.0). Passing an int directly to Vector3 or Quaternion constructors (which expect double) will throw a runtime TypeError in Dart.

Furthermore, using Platform.isAndroid from dart:io makes the code harder to unit test and can cause issues in web environments. Since package:flutter/material.dart is already imported, we can use defaultTargetPlatform == TargetPlatform.android instead.

Here is a safer, more robust implementation using defensive checks, safe double conversions, and defaultTargetPlatform:

      if (defaultTargetPlatform == TargetPlatform.android) {
        final serializedCameraPose = await _channel.invokeMethod<Map<dynamic, dynamic>>('getCameraPose', {});
        if (serializedCameraPose == null) return null;

        final position = serializedCameraPose['position'] as Map?;
        final rotation = serializedCameraPose['rotation'] as Map?;
        if (position == null || rotation == null) return null;

        final translation = Vector3(
          (position['x'] as num).toDouble(),
          (position['y'] as num).toDouble(),
          (position['z'] as num).toDouble(),
        );
        final quaternion = Quaternion(
          (rotation['x'] as num).toDouble(),
          (rotation['y'] as num).toDouble(),
          (rotation['z'] as num).toDouble(),
          (rotation['w'] as num).toDouble(),
        );
        return Matrix4.compose(translation, quaternion, Vector3.all(1.0));
      } else {
        final serializedCameraPose = await _channel.invokeMethod<List<dynamic>>('getCameraPose', {});
        if (serializedCameraPose == null) return null;
        return MatrixConverter().fromJson(serializedCameraPose);
      }

@@ -1,5 +1,6 @@
import 'dart:math' show sqrt;
import 'dart:typed_data';
import 'dart:io';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since Platform.isAndroid can be replaced with defaultTargetPlatform, this import is no longer needed and should be removed to keep the imports clean and avoid unnecessary dependencies on dart:io.

@iamEtornam

Copy link
Copy Markdown
Owner Author

Review — VERDICT: ✅ LGTM (with nits)

Replicates upstream hlefe#19. Verified: flutter analyze lib test = 0 errors; pub get resolves.

  • Dart↔native contract matches (the crux). handleGetCameraPose (ArView.kt:793-821) returns mapOf("position"->{x,y,z}, "rotation"->{x,y,z,w}) — a Map, not a list. The new Android branch reads exactly that; the old MatrixConverter().fromJson(List) would actually have thrown on this output, so this fixes a real bug.
  • Types OK. Kotlin Float encodes to Dart double via StandardMessageCodec, matching Vector3/Quaternion. Quaternion order (x,y,z,w) matches ARCore.
  • Null-safe. Any missing key / error path throws PlatformException, caught by the existing try/catch → returns null. iOS path unchanged.
  • 🔸 Nits: redundant ! assertions; import 'dart:io' makes the file web-incompatible (irrelevant for a mobile AR plugin); bundled formatting noise.

Safe to merge.

@iamEtornam
iamEtornam merged commit 58f6fdf into main Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants