ad_flow 5.2.0
A focused reliability release. Backward-compatible — no migration.
Never load while Mobile Ads init is in flight
No ad request is ever sent while the real MobileAds.initialize() future is still running, on every path:
- Ordinary first-frame fail-open / vacuous config — the request-config gate no longer honours fail-open while init is still running; it blocks with
AdBlockReason.requestConfigNotAppliedand fail-open applies only after init has genuinely completed (a config attempted and failed for real). - The
forwardConsent-starts-init-later path — for a mediationforwardConsentadopter the SDK's init only starts after forwarding succeeds, i.e. after the first config check already ran. The gate now re-settles request configuration after the forwarding barrier, so a placement can no longer slip through on the forwarding-path init-wait timeout while init is still in flight. Forward-before-init is preserved. - Prompt recovery — when init completes late, request configuration is applied at once and blocked slots recover automatically (no app code, no long cooldown, no duplicate config calls or load storms). A never-completing init leaves slots in an honest blocked state, never
AdLoading, never a request. Startup stays fully non-blocking.
In-flight disableAds() (Remove-Ads) protection
A disableAds() that lands after a load passed the gate but before the SDK returned its handle no longer publishes the late handle as loaded or keeps it warm. Banner, Native, Interstitial, Rewarded, Rewarded Interstitial, and App Open now re-check the current permission synchronously immediately before installing the handle, drop it if ads are disabled (AdBlocked(adsDisabled)), and re-warm on enableAds(). A transient indeterminate read never drops good inventory.
SSV backend-security guidance
Rewarded / rewarded-interstitial docs now explain, operationally: onReward is a client-side signal, not proof; attaching userId/customData does not prove backend verification; the backend must verify Google's callback signature and key_id, validate user / ad-unit / reward / custom data, and process transaction_id idempotently; the two fulfillment strategies; a no-double-grant warning; and a link to Google's official Flutter SSV guide.
Public API
+AdGate.isEnabled— one additive getter (mirrorsAdGate.consentGeneration). This is the entire delta; no removals, no signature/enum/default changes; hence a correct minor. No migration.
Known limitation
The upstream google_mobile_ads 9.0.0 App Open failed-load leak remains (the plugin omits dispose() on that branch and its callback carries no ad reference, so the seam cannot fix it) — low-severity, process-bounded.
Verification
543 tests pass; analyze & format clean; pana 160/160; publish dry-run 0 warnings; Android and iOS simulator example builds pass. No physical-device run was performed — not required for this pure-Dart lifecycle change.