Flutter AdMob 廣告整合完整教學指南

身為一個在台灣的獨立開發者,我花了不少時間研究 Flutter App 的變現方式。試過各種方案後,Google AdMob 仍然是我認為最適合個人開發者起步的廣告平台。今天就來分享我在自己的 App 中整合 AdMob 的完整流程和踩過的坑。

為什麼選擇 AdMob?

在台灣做獨立開發,付費下載的市場其實不大。大部分使用者更習慣免費下載搭配廣告的模式。AdMob 的優勢在於:

  • 背後是 Google,廣告填充率高,在台灣市場也有不錯的表現
  • 與 Firebase 生態系整合良好,數據分析方便
  • 支援多種廣告格式,可以依據 App 類型靈活選擇
  • Flutter 有官方維護的 google_mobile_ads 套件,穩定度有保障

前置準備

註冊 AdMob 帳號

首先到 AdMob 官網 註冊帳號。注意,你需要一個有效的 Google 帳號,而且審核通過後才能開始放置廣告。我當時大約等了兩天就通過了。

建立 App 與廣告單元

在 AdMob 後台新增你的 App,然後建立廣告單元(Ad Unit)。常用的有三種:

  • Banner Ad(橫幅廣告):固定在畫面上方或下方的小型廣告
  • Interstitial Ad(插頁廣告):全螢幕廣告,適合在自然過場時顯示
  • Rewarded Ad(獎勵廣告):使用者主動觀看後獲得獎勵

每個廣告單元建立後會拿到一組 Ad Unit ID,後面程式碼會用到。

安裝套件與平台設定

pubspec.yaml 加入依賴:

dependencies:
  google_mobile_ads: ^5.3.0

Android 設定

android/app/src/main/AndroidManifest.xml<application> 標籤內加入:

<meta-data
    android:name="com.google.android.gms.ads.APPLICATION_ID"
    android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/>

把 value 換成你在 AdMob 後台拿到的 App ID。

iOS 設定

ios/Runner/Info.plist 加入:

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>

另外記得在 ios/Podfile 確認最低版本是 iOS 14.0 以上,不然編譯可能會出問題。

初始化 AdMob SDK

main.dartmain() 函數中初始化:

import 'package:google_mobile_ads/google_mobile_ads.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await MobileAds.instance.initialize();
  runApp(const MyApp());
}

這一步很重要,一定要在 runApp 之前完成初始化,否則廣告載入會失敗。

實作 Banner Ad

Banner 廣告是最基本也最不干擾使用者的格式。我通常會放在畫面底部:

class BannerAdWidget extends StatefulWidget {
  const BannerAdWidget({super.key});

  @override
  State<BannerAdWidget> createState() => _BannerAdWidgetState();
}

class _BannerAdWidgetState extends State<BannerAdWidget> {
  BannerAd? _bannerAd;
  bool _isLoaded = false;

  @override
  void initState() {
    super.initState();
    _loadAd();
  }

  void _loadAd() {
    _bannerAd = BannerAd(
      adUnitId: 'ca-app-pub-xxxxx/yyyyy', // 換成你的 Ad Unit ID
      size: AdSize.banner,
      request: const AdRequest(),
      listener: BannerAdListener(
        onAdLoaded: (ad) {
          setState(() => _isLoaded = true);
        },
        onAdFailedToLoad: (ad, error) {
          ad.dispose();
          debugPrint('Banner 載入失敗: $error');
        },
      ),
    )..load();
  }

  @override
  void dispose() {
    _bannerAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!_isLoaded || _bannerAd == null) return const SizedBox.shrink();
    return SizedBox(
      width: _bannerAd!.size.width.toDouble(),
      height: _bannerAd!.size.height.toDouble(),
      child: AdWidget(ad: _bannerAd!),
    );
  }
}

實作 Interstitial Ad

插頁廣告適合在遊戲關卡結束、頁面切換時顯示。重點是不要太頻繁,不然使用者體驗會很差:

class InterstitialAdManager {
  InterstitialAd? _interstitialAd;

  void loadAd() {
    InterstitialAd.load(
      adUnitId: 'ca-app-pub-xxxxx/yyyyy',
      request: const AdRequest(),
      adLoadCallback: InterstitialAdLoadCallback(
        onAdLoaded: (ad) {
          _interstitialAd = ad;
        },
        onAdFailedToLoad: (error) {
          debugPrint('插頁廣告載入失敗: $error');
        },
      ),
    );
  }

  void showAd() {
    if (_interstitialAd != null) {
      _interstitialAd!.fullScreenContentCallback = FullScreenContentCallback(
        onAdDismissedFullScreenContent: (ad) {
          ad.dispose();
          loadAd(); // 預先載入下一則
        },
      );
      _interstitialAd!.show();
      _interstitialAd = null;
    }
  }
}

我自己的做法是每隔三到五個關卡才顯示一次插頁廣告。過度投放廣告只會讓使用者想移除你的 App。

實作 Rewarded Ad

獎勵廣告是我最推薦的格式,因為使用者是主動選擇觀看的,體驗最好:

void loadRewardedAd() {
  RewardedAd.load(
    adUnitId: 'ca-app-pub-xxxxx/yyyyy',
    request: const AdRequest(),
    rewardedAdLoadCallback: RewardedAdLoadCallback(
      onAdLoaded: (ad) {
        ad.fullScreenContentCallback = FullScreenContentCallback(
          onAdDismissedFullScreenContent: (ad) => ad.dispose(),
        );
        ad.show(
          onUserEarnedReward: (ad, reward) {
            // 給使用者獎勵,例如額外生命、金幣等
            debugPrint('獲得獎勵: ${reward.amount} ${reward.type}');
          },
        );
      },
      onAdFailedToLoad: (error) {
        debugPrint('獎勵廣告載入失敗: $error');
      },
    ),
  );
}

測試廣告的注意事項

開發階段請務必使用 Google 提供的測試 Ad Unit ID,千萬不要用正式的 ID 去點擊,否則帳號可能會被停權。官方測試 ID 如下:

  • Banner: ca-app-pub-3940256099942544/6300978111
  • Interstitial: ca-app-pub-3940256099942544/1033173712
  • Rewarded: ca-app-pub-3940256099942544/5224354917

上架前記得切換回正式的 Ad Unit ID。

關於 app-ads.txt

如果你有自己的開發者網站,記得在根目錄放一個 app-ads.txt 檔案。這是 IAB 的標準,用來驗證你確實是該 App 的授權廣告發布者。我自己的網站上就有放這個檔案,內容大概長這樣:

google.com, pub-3594215621243517, DIRECT, f08c47fec0942fa0

在 AdMob 後台的「App 設定」裡可以找到對應的資訊。

結語

整合 AdMob 其實不困難,真正的挑戰在於找到廣告收益和使用者體驗之間的平衡。我的建議是先從 Banner 和 Rewarded Ad 開始,等流量穩定後再考慮插頁廣告。

最後提醒一下,AdMob 的政策經常更新,特別是關於兒童應用的 COPPA 規範。如果你的 App 可能有兒童使用者,一定要在 AdMob 後台正確設定「兒童導向處理方式」,這部分我會在另一篇文章詳細說明。

希望這篇教學對同樣在台灣做獨立開發的朋友有幫助,有問題歡迎留言討論!