hotfy_wrapper_sdk

Wrapper SDK da Hotfy para Flutter — orquestração remota de anúncios (interstitial, app open, rewarded, banner, native) com AdMob e GAM, configurada via Hotfy App Console.

O que esse SDK te dá

  • Config remota: ad units por tela por segmento, decididos no console — mude no dashboard, sem rebuild do app
  • Pool embutido: interstitials são pré-carregados por ad unit, em paralelo, com re-preload automático após cada exibição
  • Boot ad: app open ou interstitial no slot do splash, com timeout e fallback
  • Rewarded ads: API Future<bool>
  • Banner & Native widgets: WrapperBanner e WrapperNativeAd
  • App Open on foreground: warm return ad com cooldown global
  • Logs padronizados: Ad [TYPE] ACTION key=value... pra grep / dashboards
  • API de eventos: pluga em qualquer analytics (Hotfy CDP, Mixpanel, Amplitude, Sentry…) — o SDK é zero-dep de analytics
  • Guards de test ad: bloqueia builds de produção de servir ad units de teste por engano

Instalação

flutter pub add hotfy_wrapper_sdk google_mobile_ads

Equivalente a adicionar no pubspec.yaml:

dependencies:
  hotfy_wrapper_sdk: ^0.1.0
  google_mobile_ads: ^5.0.0

Por que declarar google_mobile_ads no seu app também? O wrapper já o declara como dependência transitiva e o pub resolve pra uma única versão, mas declarar explicitamente no seu pubspec.yaml te dá controle de qual minor/patch ficar fixado na sua lockfile.

Você também precisa configurar o google_mobile_ads no app:

  • Android: AdMob App ID em android/app/src/main/AndroidManifest.xml
  • iOS: AdMob App ID + SKAdNetwork IDs em ios/Runner/Info.plist

Ver os docs oficiais.


Quick start

1. Inicializa uma vez no boot do app

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:hotfy_wrapper_sdk/hotfy_wrapper_sdk.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await MobileAds.instance.initialize();

  await HotfyWrapper.init(WrapperInitConfig(
    appId: const String.fromEnvironment('WRAPPER_APP_ID'),
    baseUrl: const String.fromEnvironment('WRAPPER_BASE_URL'),
    // O SDK classifica a origem do user (google_ads, meta_ads, organic, …)
    // a partir desses campos crus — passe o que tiver do install referrer.
    utmSource: attribution?.utmSource,
    utmMedium: attribution?.utmMedium,
    utmCampaign: attribution?.utmCampaign,
    utmContent: attribution?.utmContent,           // necessário pra detectar Meta Install Referrer encriptado
    gclid: attribution?.gclid,                     // se veio de Google Ads
    daysSinceInstall: daysSinceInstall,            // calculado do seu first-open timestamp
    useTestAds: kDebugMode,                        // opcional
    debug: kDebugMode,
  ));

  startAdPreload();   // pré-carrega interstitials configurados pro segmento resolvido

  runApp(const MyApp());
}

O backend resolve o segmento (d0_meta, d0_google, d0_organic, etc.) a partir desses campos. Passa o que tiver — o SDK escolhe a heurística certa internamente (sem precisar você classificar no app).

2. Mostra um interstitial

import 'package:hotfy_wrapper_sdk/hotfy_wrapper_sdk.dart';

Future<void> handleProceed() async {
  await showInterstitial('cart_proceed');
  if (mounted) Navigator.pushNamed(context, '/checkout');
}

A screenKey é o único contrato entre o app e o console. O console decide qual ad unit (ou fallback) servir pra essa tela no segmento do user. Se nenhum unit tá configurado, a chamada resolve silenciosamente (no-op).

3. Mostra boot ad durante o splash

import 'package:hotfy_wrapper_sdk/hotfy_wrapper_sdk.dart';

// antes de remover o splash / push da próxima tela
await loadAndShowBootAd(timeoutMs: 4000);

Suporta tanto app_open quanto interstitial no slot do boot, com fallback automático se o primário não preencher dentro do orçamento de tempo.

4. Mostra um rewarded ad

final earned = await showRewarded(screenKey: 'chat');
if (earned) unlockTurn();

5. Banner

import 'package:hotfy_wrapper_sdk/hotfy_wrapper_sdk.dart';

WrapperBanner(screenKey: 'home', size: WrapperBannerSize.adaptive),
WrapperBanner(screenKey: 'feed', size: WrapperBannerSize.mediumRectangle),

Tamanhos: adaptive (anchored adaptive — recomendado), inlineAdaptive, banner (320×50), largeBanner (320×100), mediumRectangle (300×250), fullBanner (468×60), leaderboard (728×90).

O widget renderiza SizedBox.shrink() se ads estiverem desativados ou não tiver unit configurado pra essa tela. Cai pra screens[X].banner_fallback se o primário falhar ao carregar.

6. Native ad

Native ads no google_mobile_ads suportam dois caminhos de renderização:

Template style (recomendado pra começar — sem código nativo):

import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:hotfy_wrapper_sdk/hotfy_wrapper_sdk.dart';

WrapperNativeAd(
  screenKey: 'feed',
  minHeight: 90,
  nativeTemplateStyle: NativeTemplateStyle(
    templateType: TemplateType.medium,
    mainBackgroundColor: Colors.white,
  ),
)

Factory (layout 100% custom — requer registrar NativeAdFactory no Android/iOS host app):

WrapperNativeAd(
  screenKey: 'feed',
  factoryId: 'listTile',
)

Nota: google_mobile_ads para nativos expõe apenas onAdImpression sem payload de receita (sem onPaidEvent). O wrapper emite impression pra native ads com revenueMicros, value, currency e precision nulos. Cheque if (e.revenueMicros != null) antes de usar campos de receita no listener.


Eventos — pluga em qualquer analytics

O SDK emite eventos tipados. Pluga eles em qualquer stack de analytics — o próprio SDK não tem dependência de analytics.

import 'package:hotfy_wrapper_sdk/hotfy_wrapper_sdk.dart';
import 'package:hotfy_cdp/hotfy_cdp.dart';

HotfyWrapper.on<AdImpressionEvent>(WrapperEventName.impression, (e) {
  HotfyCdp.trackAdImpression(AdImpressionData(
    revenueMicros: e.revenueMicros ?? 0,
    currency: e.currency ?? 'USD',
    precision: e.precision ?? 'ESTIMATED',
    adUnitId: e.adUnitId,
    adSource: e.network.value,
    adFormat: e.format.value,
  ));
});

HotfyWrapper.on<AdErrorEvent>(WrapperEventName.error, (e) {
  Sentry.captureMessage('Ad ${e.format} ${e.reason}: ${e.message}');
});

Eventos disponíveis

Evento Quando dispara
load Ad começou a carregar
show Ad foi mostrado pro user
close User fechou o ad
impression Evento onPaidEvent do GMA — receita confirmada (use pra revenue tracking)
error Falha no load ou show
skip Ad pulado (sem unit configurado, slot vazio, ads desativados, cooldown, etc.)

A função on() retorna uma função de unsubscribe:

final off = HotfyWrapper.on<AdImpressionEvent>(WrapperEventName.impression, listener);
// depois, se quiser parar de escutar:
off();

App Open on foreground (warm return)

Quando o backend retorna app_open_on_foreground: true no config do segmento ativo, o SDK passa a mostrar App Open ad quando o user volta do background (depois de ≥30s away). O cooldown global vem de app_open_cooldown_minutes e é compartilhado entre boot ad (cold start) e foreground ad.

Pra suprimir manualmente (ex: durante onboarding crucial):

HotfyWrapper.disableAppOpenOnForeground();

Persiste até HotfyWrapper.reset() ser chamado.


Como funciona

O pool é indexado por unitId, não por screenKey. Cada ad unit único ganha seu próprio slot, pré-carregado em paralelo no startup. Chamar showInterstitial(screenKey) faz lookup do unit configurado pra essa tela e mostra do slot dele — garantindo que o unit correto sempre apareça na tela correta.


Licença

UNLICENSED — proprietário. Copyright (c) 2026 Hotfy LLC. Todos os direitos reservados. Ver LICENSE pros termos. Sem uso, modificação ou distribuição sem permissão escrita prévia da Hotfy LLC.

Libraries

hotfy_wrapper_sdk
Hotfy Wrapper SDK for Flutter — orquestração remota de anúncios (interstitial, app open, rewarded, banner, native) com AdMob e GAM, configurada via Hotfy App Console.