Skip to content

fix(analytics): address the Flutter SDK review on the tracking helpers - #1977

Merged
lohanidamodar merged 2 commits into
mainfrom
fix/analytics-flutter-review
Oct 7, 2026
Merged

lohanidamodar merged 2 commits into
mainfrom
fix/analytics-flutter-review

Conversation

@lohanidamodar

Copy link
Copy Markdown
Member

Addresses the four analytics findings from the review on appwrite/sdk-for-flutter#336, fixed in the generator rather than in the generated repo.

Engagement lost on inactive → resumed

Opening and dismissing the notification shade (or the app-switcher preview) transitions resumed → inactive → resumed without ever reaching hidden or paused. The interval is therefore still open, but the handler restarted it unconditionally — discarding every second accrued before the interruption and emitting an app_foregrounded for a background that never happened.

A new interval now starts only once the previous one has ended.

Async emitter failures were unhandled

AnalyticsEventEmitter declared void, but the documented wiring returns analytics.createEvent(...) — a future. The void contract discarded it, so the surrounding try/catch only ever caught synchronous throws, and a rejected request surfaced as an unhandled async error in the host app.

The typedef is now FutureOr<void>, and both helpers attach a handler to the returned future.

didRemove reported screens nobody saw

didRemove's previousRoute is the route below the removed one, not necessarily the visible one, so removing a buried route during stack cleanup recorded a screen view for a screen the user never reached.

The observer now tracks the top route and emits only when the visible screen actually changes. didReplace carried the identical flaw and got the same guard.

Helpers shipped into SDKs with no analytics service

This is the root cause behind the review's "example references a non-existent Analytics" comment, and it is a generator bug rather than a documentation one.

Both helper templates were registered with scope: default, so they were emitted into every generated SDK regardless of whether the spec carried an analytics service. An SDK generated from a spec without it got files naming an Analytics class the SDK never generates.

File entries can now declare requires, and SDK.php skips any entry whose required service is absent from the filtered services. The analytics companions are gated on analytics.

This affected Web as well — src/services/analytics-tracking.ts had the same unconditional entry — so both are gated.

Verification

  • php -l clean on SDK.php, Flutter.php, Web.php
  • All three touched Twig templates parse
  • $filteredServices confirmed in scope at the new check

Generated output has not been re-analyzed with dart analyze yet; the FutureOr/unawaited changes deserve that before merge.

Four findings from the review on appwrite/sdk-for-flutter#336.

`inactive` -> `resumed` (notification shade, app switcher preview) never
reaches `hidden` or `paused`, so the engagement interval is still open. The
handler restarted it anyway, discarding everything accrued before the
interruption and emitting a foreground event for a background that never
happened. A new interval now starts only once the previous one has ended.

The emitter returns the request future, but the typedef declared `void`, so
the surrounding try/catch only ever caught synchronous throws and a rejected
request surfaced as an unhandled async error in the host app. The typedef is
now `FutureOr<void>` and both helpers attach a handler to the future.

`didRemove` fires for buried routes, and its `previousRoute` is the route
below the removed one rather than the visible one, so cleaning up a middle
route reported a screen view for a screen nobody saw. The observer now tracks
the top route and emits only when the visible screen actually changes.

The helpers were emitted into every SDK regardless of the spec, so an SDK
generated without the analytics service shipped files referencing an
`Analytics` class it never generates — which is what the review hit. File
entries can now declare `requires`, and the analytics companions are gated on
the service being present.
@hansi-codes

hansi-codes Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

🟢 Tier S · Ready to merge

The follow-up changes address the previously reported issues, and no new defects were found in the incremental diff.

The pull request gates Flutter and Web analytics companions on the filtered analytics service and keeps their public exports aligned. It also preserves Flutter engagement intervals across inactive/resumed transitions, suppresses events for buried-route changes, and handles asynchronous emitter failures. Generation tests now check companion files and exports with analytics present and absent.

Latest changes: The newest commits condition public exports on analytics availability, convert emitter futures to Future before swallowing errors, and add generation coverage for analytics companions.

Verdict New comments Fixed Still open
✅ Approved 0 3 0
📂 Walkthrough · 8
File Change
src/SDK/Language/Flutter.php Require analytics before generating the Flutter observer and tracking companions.
src/SDK/Language/Web.php Require analytics before generating the Web tracking companion.
src/SDK/SDK.php Skip file entries whose required service is absent after filtering.
templates/flutter/lib/package.dart.twig Export analytics companions only when the filtered analytics service exists.
templates/flutter/lib/src/analytics_observer.dart.twig Track the visible route and safely swallow synchronous and asynchronous emitter failures.
templates/flutter/lib/src/analytics_tracking.dart.twig Retain open engagement intervals on resume and safely handle emitter futures.
templates/web/src/index.ts.twig Condition analytics tracking and type exports on service availability.
tests/generation/GenerationTest.php Check analytics companion files and public exports with and without the service.
✅ Fixed since the last review · 3
  • Gate the public exports alongside the helper files · src/SDK/SDK.php:1018
  • Convert typed request futures to void before swallowing errors · templates/flutter/lib/src/analytics_observer.dart.twig:165
  • Add generation coverage for service-dependent companions · src/SDK/SDK.php:1018

Reviewed the commits since c66264a · Details · Comment @hansi-codes review to re-run, or mention @hansi-codes with a question.

@hansi-codes hansi-codes 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.

🟡 Tier B · 2 blocking findings to address. Summary

Comment thread src/SDK/SDK.php
Comment thread templates/flutter/lib/src/analytics_observer.dart.twig Outdated
Comment thread src/SDK/SDK.php
Review follow-ups on the service gate.

Skipping the helper files left the entry points still exporting them, so an
SDK generated without analytics referenced missing modules and failed to
build. `lib/<package>.dart` and `src/index.ts` now gate those exports on the
same service, value and type exports alike.

`catchError` on a typed future must return that future's type, so the empty
handler would itself fail for an emitter returning `Future<Model>`. Both
helpers narrow with `then<void>` before attaching the handler.

Adds generation coverage for `requires`: the companions and their exports are
absent when the spec has no analytics service and present when it does, so the
contract cannot regress silently.

@hansi-codes hansi-codes 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.

🟢 Tier S · Looks good to merge. Summary

@lohanidamodar
lohanidamodar merged commit ecd4410 into main Oct 7, 2026
61 checks passed
@lohanidamodar
lohanidamodar deleted the fix/analytics-flutter-review branch October 7, 2026 07:15
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