| name | implement-admob |
| description | Implements Google AdMob ads (banner, native, interstitial) in Flutter following the project architecture. Use whenever adding or modifying ad-related files, integrating the AdMob SDK, adding monetization via ads, creating banner or native ad widgets, managing interstitial ad lifecycle, or centralizing ad unit IDs. Covers AdConfig centralized IDs, AdService SDK init, InterstitialAdService lifecycle, AdBannerWidget, AdNativeWidget, DI registration, and anti-patterns. Activate even when the user says 'add ads to my app', 'show a banner ad', 'monetize with AdMob', 'integrate Google ads', 'show a native ad', or 'display an interstitial' without explicitly mentioning AdConfig or AdService. |
Implement AdMob â Flutter
Leitura RĂĄpida
- AdService: inicializa o SDK do Google Mobile Ads â chamado uma Ășnica vez no
AppInitializer.
- AdConfig: centraliza todos os ad unit IDs separados por plataforma (Android/iOS) â NUNCA coloque IDs hardcoded fora desta classe.
- InterstitialAdService: gerencia o ciclo de vida de anĂșncios intersticiais â injete na View via DI, nunca no Cubit.
- AdBannerWidget: widget autogerenciado para banner ads â recebe apenas
adUnitId, carrega e descarta sozinho.
- AdNativeWidget: widget autogerenciado para native ads â recebe
adUnitId e templateType.
- Quando exibir intersticial: chame
load() no initState() e show() com Future.delayed apĂłs o conteĂșdo estar visĂvel.
- Registro no DI:
AdService e InterstitialAdService â registerLazySingleton.
- Nunca passe
BuildContext para os services de anĂșncio.
- Nunca chame
MobileAds.instance.initialize() diretamente fora de AdService.
DependĂȘncias (pubspec.yaml)
dependencies:
google_mobile_ads: ^5.x.x
Estrutura de Arquivos
lib/
âââ common/
â âââ services/
â â âââ ads/
â â âââ ad_config.dart # IDs de anĂșncios por plataforma
â â âââ ad_service.dart # Inicialização do SDK
â â âââ interstitial_ad_service.dart # Gerenciamento de intersticiais
â âââ widgets/
â âââ ad_banner_widget.dart # Widget de banner
â âââ ad_native_widget.dart # Widget nativo
Inicialização (AppInitializer)
class AppInitializer {
static Future<void> initialize(AppFlavor flavor) async {
WidgetsFlutterBinding.ensureInitialized();
await AppInjector.setupDependencies(flavor: flavor);
// Inicializa o SDK de anĂșncios (sempre apĂłs setupDependencies)
await AppInjector.inject.get<AdService>().initialize();
}
}
AdConfig â IDs Centralizados
import 'dart:io';
class AdConfig {
const AdConfig._();
static String get nativeBanner1 {
if (Platform.isAndroid) return 'ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX';
if (Platform.isIOS) return 'ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX';
throw UnsupportedError('Plataforma nĂŁo suportada para anĂșncios');
}
static String get banner2 {
if (Platform.isAndroid) return 'ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX';
if (Platform.isIOS) return 'ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX';
throw UnsupportedError('Plataforma nĂŁo suportada para anĂșncios');
}
static String get interstitial {
if (Platform.isAndroid) return 'ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX';
if (Platform.isIOS) return 'ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX';
throw UnsupportedError('Plataforma nĂŁo suportada para anĂșncios');
}
}
Regras:
- â
SEMPRE use
Platform.isAndroid / Platform.isIOS com IDs separados
- â
Lance
UnsupportedError para plataformas nĂŁo suportadas
- â
Construtor privado
const AdConfig._() â nĂŁo instanciĂĄvel
- â NUNCA coloque IDs hardcoded fora de
AdConfig
- â Use
if com return separados â nĂŁo if/else
AdService â Inicialização do SDK
import 'dart:developer';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class AdService {
bool _initialized = false;
Future<void> initialize() async {
if (_initialized) return;
await MobileAds.instance.initialize();
_initialized = true;
log('AdService: Google Mobile Ads initialized');
}
}
Regras:
- â
Guard
if (_initialized) return para evitar inicialização dupla
- â
Use
log() do dart:developer â nunca print()
- â
Registrar como
registerLazySingleton
InterstitialAdService â AnĂșncio Intersticial
import 'dart:developer';
import 'package:base_app/common/services/ads/ad_config.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class InterstitialAdService {
InterstitialAd? _interstitialAd;
bool _isLoading = false;
bool get isReady => _interstitialAd != null;
Future<void> load() async {
if (_interstitialAd != null || _isLoading) return;
_isLoading = true;
await InterstitialAd.load(
adUnitId: AdConfig.interstitial,
request: const AdRequest(),
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: (ad) {
_interstitialAd = ad;
_isLoading = false;
log('InterstitialAdService: ad loaded');
ad.fullScreenContentCallback = FullScreenContentCallback(
onAdDismissedFullScreenContent: (ad) {
ad.dispose();
_interstitialAd = null;
load(); // Pré-carrega o próximo automaticamente
},
onAdFailedToShowFullScreenContent: (ad, error) {
log('InterstitialAdService: failed to show â $error');
ad.dispose();
_interstitialAd = null;
},
);
},
onAdFailedToLoad: (error) {
_isLoading = false;
log('InterstitialAdService: failed to load â $error');
},
),
);
}
void show() {
if (_interstitialAd == null) {
log('InterstitialAdService: ad not ready, loading...');
load();
return;
}
_interstitialAd!.show();
}
void dispose() {
_interstitialAd?.dispose();
_interstitialAd = null;
}
}
Regras:
- â
Guard duplo no
load()
- â
Pré-carrega o próximo no
onAdDismissedFullScreenContent
- â
show() seguro: tenta recarregar se nĂŁo estiver pronto
- â
Registrar como
registerLazySingleton
- â NUNCA passe
BuildContext para este serviço
- â NUNCA use no Cubit â apenas na View
AdBannerWidget
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class AdBannerWidget extends StatefulWidget {
const AdBannerWidget({
required this.adUnitId,
this.adSize = AdSize.banner,
super.key,
});
final String adUnitId;
final AdSize adSize;
@override
State<AdBannerWidget> createState() => _AdBannerWidgetState();
}
class _AdBannerWidgetState extends State<AdBannerWidget> {
BannerAd? _bannerAd;
bool _isLoaded = false;
@override
void initState() {
super.initState();
_loadAd();
}
void _loadAd() {
_bannerAd = BannerAd(
adUnitId: widget.adUnitId,
size: widget.adSize,
request: const AdRequest(),
listener: BannerAdListener(
onAdLoaded: (ad) {
if (mounted) setState(() => _isLoaded = true);
},
onAdFailedToLoad: (ad, error) {
log('AdBannerWidget: failed to load â $error');
ad.dispose();
_bannerAd = null;
},
),
)..load();
}
@override
void dispose() {
_bannerAd?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!_isLoaded || _bannerAd == null) return const SizedBox.shrink();
return SizedBox(
width: widget.adSize.width.toDouble(),
height: widget.adSize.height.toDouble(),
child: AdWidget(ad: _bannerAd!),
);
}
}
AdNativeWidget
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class AdNativeWidget extends StatefulWidget {
const AdNativeWidget({
required this.adUnitId,
this.templateType = TemplateType.medium,
super.key,
});
final String adUnitId;
final TemplateType templateType;
@override
State<AdNativeWidget> createState() => _AdNativeWidgetState();
}
class _AdNativeWidgetState extends State<AdNativeWidget> {
NativeAd? _nativeAd;
bool _isLoaded = false;
@override
void initState() {
super.initState();
_loadAd();
}
void _loadAd() {
_nativeAd = NativeAd(
adUnitId: widget.adUnitId,
listener: NativeAdListener(
onAdLoaded: (ad) {
if (mounted) setState(() => _isLoaded = true);
},
onAdFailedToLoad: (ad, error) {
log('AdNativeWidget: failed to load â $error');
ad.dispose();
_nativeAd = null;
},
),
request: const AdRequest(),
nativeTemplateStyle: NativeTemplateStyle(templateType: widget.templateType),
)..load();
}
@override
void dispose() {
_nativeAd?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!_isLoaded || _nativeAd == null) return const SizedBox.shrink();
return ConstrainedBox(
constraints: const BoxConstraints(minWidth: 320, minHeight: 90, maxHeight: 340),
child: AdWidget(ad: _nativeAd!),
);
}
}
Regras dos Widgets:
- â
SEMPRE
StatefulWidget com carregamento no initState()
- â
SEMPRE
dispose() com ?.dispose()
- â
SEMPRE verifique
if (mounted) antes de setState()
- â
Retorne
SizedBox.shrink() enquanto nĂŁo carregado
- â
Receba
adUnitId como parĂąmetro â nunca hardcoded
- â NUNCA retorne placeholder visĂvel (
Placeholder(), Container(color: Colors.grey))
DI Registration
// Ads
inject.registerLazySingleton<AdService>(AdService.new);
inject.registerLazySingleton<InterstitialAdService>(InterstitialAdService.new);
Uso nas Views
Banner / Nativo
// Native ad
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: AdNativeWidget(adUnitId: AdConfig.nativeBanner1),
)
// Banner padrĂŁo
AdBannerWidget(adUnitId: AdConfig.banner2)
Intersticial
class _MyViewState extends State<MyView> {
final _interstitialAdService = AppInjector.inject.get<InterstitialAdService>();
@override
void initState() {
super.initState();
_interstitialAdService.load();
Future.delayed(const Duration(seconds: 3), () {
if (mounted) _interstitialAdService.show();
});
}
@override
void dispose() {
// NĂO chame _interstitialAdService.dispose() â Ă© singleton
super.dispose();
}
}
Regras de uso na View:
- â
load() no initState() â prĂ©-carrega
- â
show() com Future.delayed â nunca imediatamente
- â
Verifique
mounted antes de show()
- â NUNCA chame
dispose() do service na View â Ă© singleton
- â NUNCA exiba intersticial dentro de
build()
Fluxo de DecisĂŁo
AnĂșncio inline no conteĂșdo (lista, feed)?
ââ Layout integrado â AdNativeWidget (TemplateType.medium)
ââ Banner compacto no rodapĂ© â AdBannerWidget (AdSize.banner)
AnĂșncio de tela cheia ao abrir conteĂșdo?
ââ InterstitialAdService: load() no initState + show() com delay
Novo slot de anĂșncio?
ââ Adicione getter estĂĄtico em AdConfig com IDs Android + iOS
Anti-Patterns
// â IDs hardcoded fora do AdConfig
AdBannerWidget(adUnitId: 'ca-app-pub-XXX/YYY')
// â Inicializar o SDK diretamente
await MobileAds.instance.initialize(); // fora de AdService
// â Exibir intersticial sem delay
void initState() {
super.initState();
_adService.show(); // ERRADO
}
// â InterstitialAdService no Cubit
class MyCubit extends Cubit<MyState> {
MyCubit(this._interstitialAdService); // ERRADO
}
// â Chamar dispose() do singleton
void dispose() {
_interstitialAdService.dispose(); // ERRADO
super.dispose();
}
Ăltima atualização: 28 de março de 2026