v5.0.0 — consent-safe mediation and production hardening
A post-release adversarial audit of 4.0.0 plus a mediation-consent lifecycle redesign. The major is driven by mediation-privacy correctness: consent forwarding now runs before MobileAds.initialize() and fails closed by default, and the conceptually-invalid deferMediationInit is removed.
📦 pub.dev: https://pub.dev/packages/ad_flow/versions/5.0.0
Breaking changes
- Removed
AdFlowConfig.deferMediationInitandAdSdk.disableMediationInitialization(). Both drove the plugin's session-wide disable of Google mediation (an A/B-testing tool), not a defer/resume — conceptually invalid and revenue-harming.AdSdkremoval affects only direct seam implementers;FakeAdSdk/GmaAdSdkare updated. forwardConsentruns BEFOREMobileAds.initialize()and fails CLOSED by default. A failed/timed-out forward means the GMA SDK is not initialized and mediation-capable loads block withAdBlockReason.consentNotForwarded(retried in the background); init + serving recover when forwarding succeeds. UI is never blocked —initialize()returns immediately. Opt out only viaMediationConsentFailurePolicy.unsafeFailOpen.AdBlockReasongainedconsentNotForwarded— exhaustive switches need the new case (or a wildcard).
Added
AdFlow.initialize(forwardConsent:)— the fail-closed, before-init consent-forwarding barrier for networks that don't read the IAB TCF string themselves. Serialized (never invoked concurrently, even across the 15s wait bound —Future.timeoutdoes not cancel its source) and generation-guarded.AdFlowConfig.mediationConsentPolicy+MediationConsentFailurePolicy(failCloseddefault,unsafeFailOpen= explicit unsafe opt-out).
Fixed (correctness / reward integrity / reliability)
- Frequency-cap late-hydration overwrite (late store read now merges, never rolls memory back / double-counts).
- Runtime SSV in-flight race + fail-drop (re-apply on install; drop a warm ad on attach failure).
- Rewarded-interstitial: re-validate live permission + expiry after the unbounded intro.
- Async-callback + refreshed-banner-paid isolation; safe teardown (
safeUnawaited). validate()mirrors every constructor assert (release builds strip asserts).- Forwarder serialization across the timeout boundary (at most one invocation, strictly ordered).
- Stale-consent ad invalidation folded into
recheckGate()via an internal consent-generation stamp (no new public method). MediationNetworkExtrasasserts against an empty class name.
Verification
Static/tests: flutter analyze clean · 494 tests · coverage 85.2% · pana 160/160 · publish dry-run 0 warnings.
Runtime (Google-demand only, AdFlowConfig.test(), no Unity/overrides): Android emulator — all six formats shown; iOS simulator — banner, native, interstitial, rewarded, rewarded-interstitial shown, app-open loaded/ready. Physical-device testing intentionally deferred.
Unity mediation — known limitation (not a v5 blocker)
Unity mediation via gma_mediation_unity is temporarily unavailable with 5.0.0: the latest published adapter (1.8.1) requires google_mobile_ads: ^8.0.0 while 5.0.0 requires ^9.0.0. Google's main has an in-progress 1.9.0 for ^9.0.0 but it is unpublished; this release intentionally uses no git dependency, override, or vendored patch. Documented as an optional-integration limitation in doc/MEDIATION_SETUP.md. Unity mediation itself was not runtime-validated.
Upgrading
Most apps compile unchanged — see MIGRATION.md. Delete deferMediationInit if set; add the consentNotForwarded case to exhaustive AdBlockReason switches; if you use forwardConsent, note it now runs before init and fails closed.
Full notes: CHANGELOG.md.