Skip to content

Fix validated upstream issues: LateInit handlers (#8), updateNode (#6), dep constraints (#26, #29) - #6

Merged
iamEtornam merged 3 commits into
mainfrom
fix/upstream-issues-lateinit-updatenode-deps
Jul 8, 2026
Merged

Fix validated upstream issues: LateInit handlers (#8), updateNode (#6), dep constraints (#26, #29)#6
iamEtornam merged 3 commits into
mainfrom
fix/upstream-issues-lateinit-updatenode-deps

Conversation

@iamEtornam

Copy link
Copy Markdown
Owner

Addresses open issues from upstream hlefe/ar_flutter_plugin_2 that were validated as real bugs reproducible in this codebase. Native-only and device-specific issues are handled elsewhere or deferred (see bottom).

Fixes

🐛 LateInitializationError on session callbacks — upstream hlefe#8

ARSessionManager.onPlaneOrPointTap and onPlaneDetected were declared late (non-nullable), but _platformCallHandler guards them with if (x != null). Reading an unset late field throws LateInitializationError: Field 'onPlaneDetected' has not been initialized. — exactly the error reported. Made both nullable (matching onError), so the guards work and the callbacks are optional. Also removes two unnecessary_null_comparison analyzer warnings.

✨ Add ARObjectManager.updateNode() — upstream #6

The maintainer told users to call objectManager.updateNode(node) to sync transforms, but the method never existed. Added it — it invokes the transformationChanged platform method that the Android and iOS sides already handle, so no native change is needed.

📦 Widen permission_handler to >=11.3.1 <13.0.0 — upstream hlefe#26

Allows permission_handler 12.x (resolves to 12.0.3). All APIs used (Permission.camera, PermissionStatus.*, openAppSettings) are stable across 11→12. Widened rather than pinned so 11.x users aren't forced to upgrade.

🧹 Bump Dart SDK upper bound <3.0.0<4.0.0 — related to upstream hlefe#29 (hygiene)

Honest note: hlefe#29's reported symptom (incompatible with Flutter 3.35.6) did not reproduce here — pub's Dart-3 compatibility mode resolves fine against <3.0.0 on Dart 3.12. This bump removes the fragile reliance on that compat shim; it is hygiene, not a verified fix for hlefe#29's exact symptom.

Verification

Not included (validated + routed elsewhere / deferred)

…, widen deps (hlefe#26, hlefe#29)

- ARSessionManager.onPlaneOrPointTap/onPlaneDetected: late -> nullable, fixes
  'LateInitializationError: Field onPlaneDetected has not been initialized' (hlefe#8)
- ARObjectManager.updateNode(): add the documented transform-sync method that
  was referenced by the maintainer but never existed; reuses the native
  'transformationChanged' handler (#6)
- pubspec: widen permission_handler to >=11.3.1 <13.0.0 (hlefe#26); bump Dart SDK
  upper bound to <4.0.0 (hlefe#29, hygiene)
- tests: regression tests for hlefe#8 and #6

@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 introduces the updateNode method to ARObjectManager to synchronize node transformations with the native AR engine, resolves potential LateInitializationErrors in ARSessionManager by making event handlers nullable, updates dependency constraints in pubspec.yaml, and adds corresponding regression tests. The review feedback suggests minor improvements: using const for the MatrixValueNotifierConverter instantiation to avoid unnecessary runtime allocations, and replacing the null-assertion operator (!) with the safer, more idiomatic null-aware call operator (?.call(...)) when invoking the nullable event handlers.

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 thread lib/managers/ar_object_manager.dart Outdated
await _channel.invokeMethod<void>('transformationChanged', {
'name': node.name,
'transformation':
MatrixValueNotifierConverter().toJson(node.transformNotifier),

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 MatrixValueNotifierConverter has a const constructor, you can instantiate it as a compile-time constant using const to avoid unnecessary runtime object allocations.

Suggested change
MatrixValueNotifierConverter().toJson(node.transformNotifier),
const MatrixValueNotifierConverter().toJson(node.transformNotifier),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done in 01450b36df05a7651d3a3de7856f299ef60089d0 — applied as suggested. Thanks!

Comment thread lib/managers/ar_session_manager.dart Outdated
return ARHitTestResult.fromJson(e);
}).toList();
onPlaneOrPointTap(hitTestResults);
onPlaneOrPointTap!(hitTestResults);

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

Using the null-assertion operator ! on a mutable instance field is discouraged in Dart because the field's value could theoretically change or be set to null. It is more idiomatic and safer to use the null-aware call operator ?.call(...) instead.

Suggested change
onPlaneOrPointTap!(hitTestResults);
onPlaneOrPointTap?.call(hitTestResults);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done in 01450b36df05a7651d3a3de7856f299ef60089d0 — applied as suggested. Thanks!

Comment thread lib/managers/ar_session_manager.dart Outdated
if (onPlaneDetected != null) {
final planeCountResult = call.arguments as int;
onPlaneDetected(planeCountResult);
onPlaneDetected!(planeCountResult);

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

Using the null-assertion operator ! on a mutable instance field is discouraged in Dart. It is more idiomatic and safer to use the null-aware call operator ?.call(...) instead.

Suggested change
onPlaneDetected!(planeCountResult);
onPlaneDetected?.call(planeCountResult);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done in 01450b36df05a7651d3a3de7856f299ef60089d0 — applied as suggested. Thanks!

… never send

Android's handleTransformNode only calls result.* when handlePans||handleRotation
is enabled; both default to false, so an awaited transformationChanged would hang.
Mirror removeNode (void, no await). Document the Android gesture-flag requirement.
@iamEtornam

Copy link
Copy Markdown
Owner Author

Review loop — ✅ clean after 2 iterations

Iteration 1 — found a BLOCKER: updateNode was async and awaited transformationChanged, but Android's handleTransformNode (ArView.kt:649) wraps its whole body in if (handlePans || handleRotation) — both default false, so it never calls result.* and an awaited call would hang forever. (The pre-existing addNode transform listener never hit this because it's fire-and-forget.)

Fix applied: made updateNode void / fire-and-forget, mirroring removeNode. Documented that on Android the transform is applied only when handlePans/handleRotation was enabled in onInitialize.

Iteration 2 — LGTM. BLOCKER resolved and confirmed against the native code. No regressions.

Accepted tradeoff (not fixed, by design): a fire-and-forget invoke can log an uncaught async error if gestures are enabled and the node is missing (NODE_NOT_FOUND). This is identical to the existing removeNode behaviour, so updateNode matches the established pattern rather than diverging. Adding .catchError was considered and rejected to avoid inconsistency + scope creep into removeNode.

Verification: flutter analyze lib test → 0 errors (targeted unnecessary_null_comparison warnings gone); flutter test → 3 passing; hlefe#8 regression test proven to fail on the old late code and pass on the fix.

- const MatrixValueNotifierConverter() to avoid a runtime allocation
- onPlaneOrPointTap/onPlaneDetected: use ?.call(...) instead of ! on mutable fields
Per gemini-code-assist review on PR #6.
@iamEtornam
iamEtornam merged commit df8ce6a into main Jul 8, 2026
@iamEtornam
iamEtornam deleted the fix/upstream-issues-lateinit-updatenode-deps branch July 8, 2026 19:50
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.

1 participant