在 Flutter 應用程式中加入 AdMob 橫幅廣告和原生內嵌廣告

1. 簡介

在本程式碼研究室中,您將在 Flutter 應用程式中導入 AdMob 橫幅廣告和 AdMob 原生內嵌廣告。

建構項目

本程式碼研究室會引導您使用 Flutter 專用的 Google 行動廣告外掛程式,在 Flutter 應用程式中導入 AdMob 內嵌橫幅廣告和 AdMob 原生廣告內嵌廣告。

在執行本程式碼研究室的過程中,如果您遇到任何問題 (例如程式碼錯誤、文法錯誤、用詞不明確等),請使用程式碼研究室左下角的「回報錯誤」連結來回報問題。

課程內容

  • 如何設定 Google 行動廣告 Flutter 外掛程式
  • 如何在 Flutter 應用程式中導入內嵌橫幅廣告和獎勵廣告

軟硬體需求

  • Android Studio 4.1 以上版本
  • Xcode 12 以上版本 (適用於 iOS 開發作業)

您對 AdMob 使用體驗的評價如何?

新手 中級 還算容易

針對使用 Flutter 的經驗,您會給予什麼評價?

新手 中級 還算容易

2. 設定 Flutter 開發環境

您需要使用兩項軟體:Flutter SDK編輯器

您可以使用下列任一裝置執行程式碼研究室:

  • 將實體 AndroidiOS 裝置接上電腦,並設為開發人員模式。
  • iOS 模擬器 (需要安裝 Xcode 工具)。
  • Android Emulator (需要在 Android Studio 中設定)。
  • 瀏覽器 (必須使用 Chrome 進行偵錯)。
  • 下載 WindowsLinuxmacOS 桌面應用程式。您必須在要部署的平台上進行開發。因此,如果您想要開發 Windows 電腦版應用程式,就必須在 Windows 上進行開發,以便存取適當的建構鏈結。如要進一步瞭解作業系統的特定需求,請參閱 docs.flutter.dev/desktop

下載程式碼

下載 ZIP 檔案後,請解壓縮其中的內容。您會使用一個名為「admob-inline-ads-in-flutter-main」的資料夾。

或者,您也可以透過指令列複製 GitHub 存放區:

$ git clone https://github.com/googlecodelabs/admob-inline-ads-in-flutter

存放區包含三個資料夾:

  • android_studio_folder.png 範例程式碼:您將在本程式碼研究室中建構的程式碼。
  • android_studio_folder.png 完成:在本程式碼研究室中完成的程式碼。(適用於原生程式碼的 Java 和 Objective-C)
  • android_studio_folder.png complete_kotlin_swift:完成本程式碼研究室的程式碼。(原生程式碼的 Kotlin 和 Swift)

3. 設定 AdMob 應用程式和廣告單元

由於 Flutter 是多平台 SDK,您需要在 AdMob 中新增 Android 和 iOS 的應用程式和廣告單元。

設定 Android 裝置

如要設定 Android,您必須新增 Android 應用程式並建立廣告單元。

新增 Android 應用程式

  1. AdMob 控制台中,按一下「應用程式」選單中的「新增應用程式」
  2. 系統詢問「是否已在 Google Play 或 App Store 發布應用程式?」時,按一下「否」
  3. 請在應用程式名稱欄位中輸入 AdMob inline ads,然後選取「Android」做為平台。

d51828db0e2e4f6c.png

  1. 完成本程式碼研究室不需要啟用使用者指標。不過我們建議您這麼做,因為可以幫助您深入瞭解使用者行為。按一下「新增」完成程序。

b918bf44362813a9.png

建立廣告單元

新增廣告單元的步驟如下:

  1. AdMob 管理中心的「應用程式」選單中,選取「AdMob 內嵌廣告」應用程式。
  2. 按一下「廣告單元」選單。

橫幅廣告

  1. 按一下「新增廣告單元」
  2. 選取「橫幅」格式。
  3. 在「廣告單元名稱」欄位中輸入 android-inline-banner
  4. 按一下「建立廣告單元」完成設定程序。

原生

  1. 按一下「新增廣告單元」
  2. 選取「Native Advanced」做為格式。
  3. 在「廣告單元名稱」欄位中輸入 android-inline-native
  4. 按一下「建立廣告單元」完成設定程序。

新的廣告單元通常需要幾個小時才能放送廣告。

如果您想要立即測試廣告行為,請使用 Android 應用程式 ID/廣告單元 ID 和 iOS 應用程式 ID/廣告單元 ID 表格中所列的測試應用程式 ID 和廣告單元 ID。

設定 iOS 裝置

如要設定 iOS,您需要新增 iOS 應用程式並建立廣告單元。

新增 iOS 應用程式

  1. AdMob 控制台中,按一下「應用程式」選單中的「新增應用程式」
  2. 系統詢問「是否已在 Google Play 或 App Store 發布應用程式?」時,按一下「否」
  3. 請在應用程式名稱欄位中輸入 AdMob inline ads,然後選取「iOS」iOS做為平台。

a4c963c9aa09519.png

  1. 完成本程式碼研究室不需要啟用使用者指標。不過我們建議您這麼做,因為可以幫助您深入瞭解使用者行為。按一下「新增」完成程序。

b918bf44362813a9.png

建立廣告單元

新增廣告單元的步驟如下:

  1. AdMob 管理中心的「應用程式」選單中,選取「AdMob 內嵌廣告」應用程式。
  2. 按一下「廣告單元」選單。

橫幅廣告

  1. 按一下「新增廣告單元」
  2. 選取「橫幅」格式。
  3. 在「廣告單元名稱」欄位中輸入 ios-inline-banner
  4. 按一下「建立廣告單元」完成設定程序。

原生

  1. 按一下「新增廣告單元」
  2. 選取「Native Advanced」做為格式。
  3. 在「廣告單元名稱」欄位中輸入 ios-inline-native
  4. 按一下「建立廣告單元」完成設定程序。

新的廣告單元通常需要幾個小時才能放送廣告。

如果您想立即測試廣告行為,請使用下表所列的測試應用程式 ID 和廣告單元 ID。

選用:使用測試 AdMob 應用程式和廣告單元

如想按照程式碼研究室的說明操作,而非自行建立新的應用程式和廣告單元,您可以使用下表所列的測試 AdMob 應用程式 ID 和廣告單元 ID。

Android 應用程式 ID/廣告單元 ID

項目

應用程式 ID/廣告單元 ID

AdMob 應用程式 ID

ca-app-pub-3940256099942544~3347511713

橫幅廣告

ca-app-pub-3940256099942544/6300978111

原生

ca-app-pub-3940256099942544/2247696110

iOS 應用程式 ID/廣告單元 ID

項目

應用程式 ID/廣告單元 ID

AdMob 應用程式 ID

ca-app-pub-3940256099942544~1458002511

橫幅廣告

ca-app-pub-3940256099942544/2934735716

原生

ca-app-pub-3940256099942544/3986624511

如要進一步瞭解測試廣告,請參閱 Android 測試廣告iOS 測試廣告開發人員說明文件。

4. 新增 Google Mobile Ads Flutter 外掛程式

Flutter 會透過外掛程式提供不同平台專屬的服務。外掛程式可讓您存取各個平台上的服務和 API。

google_mobile_ads 外掛程式支援使用 AdMob API 載入及顯示橫幅廣告、插頁式廣告、獎勵廣告和原生廣告。

Flutter 是多平台 SDK,因此 google_mobile_ads 外掛程式同時適用於 iOS 和 Android。因此,如果您在 Flutter 應用程式中加入這個外掛程式,Android 和 iOS 版 AdMob 內嵌廣告應用程式都會使用這個外掛程式。

將 Google 行動廣告外掛程式新增為依附元件

如要從 AdMob 內嵌廣告專案存取 AdMob API,請將 google_mobile_ads 新增為專案根目錄中 pubspec.yaml 檔案的依附元件。

pubspec.yaml

...
dependencies:
  flutter:
    sdk: flutter
  google_fonts: ^0.3.9

  # TODO: Add google_mobile_ads as a dependency
  google_mobile_ads: ^1.2.0

...

按一下「Pub get」,即可在 AdMob 內嵌廣告專案中安裝外掛程式。

93ef6061e58ebc86.png

更新 AndroidManifest.xml (Android)

  1. 在 Android Studio 中開啟 android/app/src/main/AndroidManifest.xml 檔案。
  2. 加入名為「com.google.android.gms.ads.APPLICATION_ID」的 <meta-data> 代碼,加入 AdMob 應用程式 ID。舉例來說,如果您的 AdMob 應用程式 ID 是 ca-app-pub-3940256099942544~3347511713,就必須在 AndroidManifest.xml 檔案中新增下列幾行程式碼。

AndroidManifest.xml

<manifest>
    ...
    <application>
       ...
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-3940256099942544~3347511713"/>
    </application>

</manifest>

更新 Info.plist (iOS)

  1. 在 Android Studio 中開啟 ios/Runner/Info.plist 檔案。
  2. 加入包含 AdMob 應用程式 ID 字串值的 GADApplicationIdentifier 鍵。舉例來說,如果您的 AdMob 應用程式 ID 是 ca-app-pub-3940256099942544~1458002511,就必須在 Info.plist 檔案中新增下列幾行程式碼。

ios/Runner/Info.plist

...
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
...

5. 新增廣告專用的輔助類別

lib 目錄中建立名為 ad_helper.dart 的新檔案。接著,導入 AdHelper 類別,藉此提供 Android 和 iOS 的 AdMob 應用程式 ID 和廣告單元 ID。

請務必將 AdMob 應用程式 ID (ca-app-pub-xxxxxx~yyyyy) 和廣告單元 ID (ca-app-pub-xxxxxxx/yyyyyyyy) 替換為您在上一個步驟中建立的 ID。

ad_helper.dart

import 'dart:io';

class AdHelper {

  static String get bannerAdUnitId {
    if (Platform.isAndroid) {
      return "<YOUR_ANDROID_BANNER_AD_UNIT_ID";
    } else if (Platform.isIOS) {
      return "<YOUR_IOS_BANNER_AD_UNIT_ID>";
    } else {
      throw UnsupportedError("Unsupported platform");
    }
  }

  static String get nativeAdUnitId {
    if (Platform.isAndroid) {
      return "<YOUR_ANDROID_NATIVE_AD_UNIT_ID>";
    } else if (Platform.isIOS) {
      return "<YOUR_IOS_NATIVE_AD_UNIT_ID>";
    } else {
      throw UnsupportedError("Unsupported platform");
    }
  }
}

如果您想使用測試用的 AdMob 應用程式 ID 和測試廣告單元 ID,請使用下列程式碼片段。

ad_helper.dart

import 'dart:io';

class AdHelper {
  
  static String get bannerAdUnitId {
    if (Platform.isAndroid) {
      return 'ca-app-pub-3940256099942544/6300978111';
    } else if (Platform.isIOS) {
      return 'ca-app-pub-3940256099942544/2934735716';
    }
    throw UnsupportedError("Unsupported platform");
  }

  static String get nativeAdUnitId {
    if (Platform.isAndroid) {
      return 'ca-app-pub-3940256099942544/2247696110';
    } else if (Platform.isIOS) {
      return 'ca-app-pub-3940256099942544/3986624511';
    }
    throw UnsupportedError("Unsupported platform");
  }
}

6. 初始化 Google Mobile Ads SDK

載入廣告之前,您需要初始化 Google Mobile Ads SDK。開啟 lib/home_page.dart 檔案並修改 _initGoogleMobileAds(),在載入首頁前初始化 SDK。

請注意,您需要將 _initGoogleMobileAds() 方法的傳回類型從 Future<dynamic> 變更為 Future<InitializationStatus>,才能在完成 SDK 初始化結果後取得 SDK 初始化結果。

home_page.dart

// TODO: Import google_mobile_ads.dart
import 'package:google_mobile_ads/google_mobile_ads.dart';

import 'package:flutter/material.dart';

...

class HomePage extends StatelessWidget {

  ...

  Future<InitializationStatus> _initGoogleMobileAds() {
    // TODO: Initialize Google Mobile Ads SDK
    return MobileAds.instance.initialize();
  }
}

7. 新增橫幅廣告

在這個部分中,您將在清單中間顯示橫幅廣告,如下方螢幕截圖所示。

62c405c962909fd3.png

  1. 開啟 lib/banner_inline_page.dart 檔案。
  2. 新增下列幾行即可匯入 ad_helper.dartgoogle_mobile_ads.dart
...

// TODO: Import ad_helper.dart
import 'package:admob_inline_ads_in_flutter/ad_helper.dart';

// TODO: Import google_mobile_ads.dart
import 'package:google_mobile_ads/google_mobile_ads.dart';

class BannerInlinePage extends StatefulWidget {
  ...
}
  1. _BannerInlinePageState 類別中,為橫幅廣告新增下列成員和方法。

請注意,_kAdIndex 代表橫幅廣告展示的索引,用於計算 _getDestinationItemIndex() 方法的項目索引。

banner_inline_page.dart

class _BannerInlinePageState extends State<BannerInlinePage> {

  // TODO: Add _kAdIndex
  static final _kAdIndex = 4;

  // TODO: Add a banner ad instance
  BannerAd? _ad;

  ...

  // TODO: Add _getDestinationItemIndex()
  int _getDestinationItemIndex(int rawIndex) {
    if (rawIndex >= _kAdIndex && _ad != null) {
      return rawIndex - 1;
    }
    return rawIndex;
  }

  ...
}
  1. initState() 方法中,建立並載入 320x50 橫幅廣告 (AdSize.banner) 的 BannerAd。請注意,廣告事件監聽器已設為在載入廣告時更新使用者介面 (setState())。

banner_inline_page.dart

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

  // TODO: Load a banner ad
  BannerAd(
    adUnitId: AdHelper.bannerAdUnitId,
    size: AdSize.banner,
    request: AdRequest(),
    listener: BannerAdListener(
      onAdLoaded: (ad) {
        setState(() {
          _ad = ad as BannerAd;
        });
      },
      onAdFailedToLoad: (ad, error) {
        // Releases an ad resource when it fails to load
        ad.dispose();
        print('Ad load failed (code=${error.code} message=${error.message})');
      },
    ),
  ).load();
}
  1. 修改 build() 方法,以顯示可用的橫幅廣告 (如果有的話)。
  2. 更新 itemCount, 即可計算橫幅廣告項目,並更新 itemBuilder,,在廣告載入時在廣告索引 (_kAdIndex) 顯示橫幅廣告。
  3. 更新程式碼,使用 _getDestinationItemIndex() 方法擷取內容項目的索引。

banner_inline_page.dart

@override
Widget build(BuildContext context) {
  return Scaffold(
    ...
    body: ListView.builder(
      // TODO: Adjust itemCount based on the ad load state
      itemCount: widget.entries.length + (_ad != null ? 1 : 0),
      itemBuilder: (context, index) {
        // TODO: Render a banner ad
        if (_ad != null && index == _kAdIndex) {
          return Container(
            width: _ad!.size.width.toDouble(),
            height: 72.0,
            alignment: Alignment.center,
            child: AdWidget(ad: _ad!),
          );
        } else {
          // TODO: Get adjusted item index from _getDestinationItemIndex()
          final item = widget.entries[_getDestinationItemIndex(index)];

          return ListTile(
            ...
          );
        }
      },
    ),
  );
}
  1. dispose() 回呼方法中呼叫 BannerAd.dispose() 方法,即可釋出與 BannerAd 物件相關聯的資源。

banner_inline_page.dart

@override
void dispose() {
  // TODO: Dispose a BannerAd object
  _ad?.dispose();

  super.dispose();
}

大功告成!執行專案,然後按一下首頁的 [橫幅內嵌廣告] 按鈕。廣告載入後,您會在清單中央看到橫幅廣告。

a5f857a850539fe9.png c32af50872514224.png

8. 新增原生廣告

在這個部分中,您將在清單中間顯示原生廣告,如下方螢幕截圖所示。

f1671b0fa349ccf8.png

系統會透過平台原生的 UI 元件向使用者顯示原生廣告 (例如 Android 上的 View 或 iOS 上的 UIView)。

不過,您無法使用 Flutter 小工具直接建立原生 UI 元件。因此,您必須為每個平台導入 NativeAdFactory,用來從原生廣告物件 (Android 上的「NativeAd」和 iOS 中的「GADNativeAd」) 建立特定平台的原生廣告檢視畫面 (Android 上的 NativeAdView;iOS 上的 GADNativeAdView)。

導入 Android (Java) 適用的 NativeAdFactory

  1. 開啟 android/build.gradle 檔案 (或 android 資料夾下的任何檔案),然後按一下「Open to Editing in Android Studio」(在 Android Studio 中開啟,供編輯) 開啟 Android 專案。

623ad3d2282ccbf8.png

  1. 如果系統要求您選取視窗來開啟新專案,請按一下「New Window」,讓 Flutter 專案在處理 Android 專案時保持開啟狀態。

d188bb51cf7c2d08.png

建立原生廣告版面配置

  1. 開啟 Android 專案後,在 Android Studio 的專案窗格中的「app」上按一下滑鼠右鍵,然後依序選取「New」>「New」內容選單的 Android 資源檔案

2b629ee277a68fd7.png

  1. 在「New Resource File」對話方塊中,輸入 list_tile_native_ad.xml 做為檔案名稱。
  2. 選取「Layout」做為資源類型,然後輸入 com.google.android.gms.ads.nativead.NativeAdView 做為根元素。
  3. 按一下「OK」,建立新的版面配置檔案。

575f126dd018bc0.png

  1. 實作廣告版面配置,如下所示。請注意,版面配置應符合主要平台的使用者體驗視覺設計。

list_tile_native_ad.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.ads.nativead.NativeAdView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv_list_tile_native_ad_attribution_small"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#F19938"
            android:text="Ad"
            android:textColor="#FFFFFF"
            android:textSize="12sp" />

        <ImageView
            android:id="@+id/iv_list_tile_native_ad_icon"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:scaleType="fitXY"
            tools:background="#EDEDED" />

        <TextView
            android:id="@+id/tv_list_tile_native_ad_attribution_large"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:background="#F19938"
            android:gravity="center"
            android:text="Ad"
            android:textColor="#FFFFFF"
            android:visibility="invisible" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="80dp"
            android:layout_marginLeft="80dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_list_tile_native_ad_headline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:maxLines="1"
                android:textColor="#000000"
                android:textSize="16sp"
                tools:text="Headline" />

            <TextView
                android:id="@+id/tv_list_tile_native_ad_body"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:maxLines="1"
                android:textColor="#828282"
                android:textSize="14sp"
                tools:text="body" />

        </LinearLayout>

    </FrameLayout>

</com.google.android.gms.ads.nativead.NativeAdView>

建立 ListTileNativeAdFactory 類別

  1. 在「專案」窗格中,以滑鼠右鍵按一下 com.codelab.flutter.admobinlineads 套件,然後選取 新增 >Java 類別

9f3f111dd207a9b4.png

  1. 輸入 ListTileNativeAdFactory 做為名稱,然後從清單中選取「Class」

47ff82d92676e26.png

  1. 畫面上出現「New Class」對話方塊後,請將所有項目留空,然後按一下「OK」

您會看到 ListTileNativeAdFactory 類別已在套件 com.codelab.flutter.admobinlineads 中建立。

e4ed232c358ffb19.png

  1. 請按照以下方式實作 ListTileNativeAdFactory 類別。請注意,該類別會在 GoogleMobileAdsPlugin.NativeAdFactory 介面中實作 createNativeAd() 方法。

工廠類別負責建立用於顯示原生廣告的 View 物件。如程式碼所示,工廠類別會建立 UnifiedNativeAdView,並填入 NativeAd 物件。

ListTileNativeAdFactory.java

// TODO: Implement ListTileNativeAdFactory class

package com.codelab.flutter.admobinlineads;

import com.google.android.gms.ads.nativead.NativeAd;
import com.google.android.gms.ads.nativead.NativeAdView;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.Map;

import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;

class ListTileNativeAdFactory implements GoogleMobileAdsPlugin.NativeAdFactory {

    private final Context context;

    ListTileNativeAdFactory(Context context) {
        this.context = context;
    }

    @Override
    public NativeAdView createNativeAd(
            NativeAd nativeAd, Map<String, Object> customOptions) {
        NativeAdView nativeAdView = (NativeAdView) LayoutInflater.from(context)
                .inflate(R.layout.list_tile_native_ad, null);

        TextView attributionViewSmall = nativeAdView
                .findViewById(R.id.tv_list_tile_native_ad_attribution_small);
        TextView attributionViewLarge = nativeAdView
                .findViewById(R.id.tv_list_tile_native_ad_attribution_large);

        ImageView iconView = nativeAdView.findViewById(R.id.iv_list_tile_native_ad_icon);
        NativeAd.Image icon = nativeAd.getIcon();
        if (icon != null) {
            attributionViewSmall.setVisibility(View.VISIBLE);
            attributionViewLarge.setVisibility(View.INVISIBLE);
            iconView.setImageDrawable(icon.getDrawable());
        } else {
            attributionViewSmall.setVisibility(View.INVISIBLE);
            attributionViewLarge.setVisibility(View.VISIBLE);
        }
        nativeAdView.setIconView(iconView);

        TextView headlineView = nativeAdView.findViewById(R.id.tv_list_tile_native_ad_headline);
        headlineView.setText(nativeAd.getHeadline());
        nativeAdView.setHeadlineView(headlineView);

        TextView bodyView = nativeAdView.findViewById(R.id.tv_list_tile_native_ad_body);
        bodyView.setText(nativeAd.getBody());
        bodyView.setVisibility(nativeAd.getBody() != null ? View.VISIBLE : View.INVISIBLE);
        nativeAdView.setBodyView(bodyView);

        nativeAdView.setNativeAd(nativeAd);

        return nativeAdView;
    }
}

註冊 ListTileNativeAdFactory 類別

必須先將 NativeAdFactory 的例項註冊至 GoogleMobileAdsPlugin,才能從 Flutter 端使用。

  1. 開啟 MainActivity.java 檔案,然後覆寫 configureFlutterEngine() 方法和 cleanUpFlutterEngine() 方法。
  2. configureFlutterEngine() 方法中,使用專屬字串 ID (listTile) 註冊 ListTileNativeAdFactory 類別。

MainActivity.java

public class MainActivity extends FlutterActivity {

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);

        // TODO: Register the ListTileNativeAdFactory
        GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "listTile",
                new ListTileNativeAdFactory(getContext()));
    }

    ...
}
  1. 在清理過程中,您應取消註冊每個 NativeAdFactory 執行個體。在 cleanUpFlutterEngine() 方法中取消註冊 ListTileNativeAdFactory 類別。

MainActivity.java

public class MainActivity extends FlutterActivity {

    ...

    @Override
    public void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.cleanUpFlutterEngine(flutterEngine);

        // TODO: Unregister the ListTileNativeAdFactory
        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTile");
    }
}

您現在可以使用 ListTileNativeAdFactory 類別在 Android 上顯示原生廣告了。

導入 Android 適用的 NativeAdFactory (Kotlin)

  1. 開啟 android/build.gradle 檔案 (或 android 資料夾下的任何檔案),然後按一下「Open to Editing in Android Studio」(在 Android Studio 中開啟,供編輯) 開啟 Android 專案。

623ad3d2282ccbf8.png

  1. 如果系統要求您選取視窗來開啟新專案,請按一下「New Window」,讓 Flutter 專案在處理 Android 專案時保持開啟狀態。

d188bb51cf7c2d08.png

建立原生廣告版面配置

  1. 開啟 Android 專案後,在 Android Studio 的專案窗格中的「app」上按一下滑鼠右鍵,然後依序選取「New」>「New」內容選單的 Android 資源檔案

2b629ee277a68fd7.png

  1. 在「New Resource File」對話方塊中,輸入 list_tile_native_ad.xml 做為檔案名稱。
  2. 選取「Layout」做為資源類型,然後輸入 com.google.android.gms.ads.nativead.NativeAdView 做為根元素。
  3. 按一下「OK」,建立新的版面配置檔案。

575f126dd018bc0.png

  1. 實作廣告版面配置,如下所示。請注意,版面配置應符合主要平台的使用者體驗視覺設計。

list_tile_native_ad.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.ads.nativead.NativeAdView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv_list_tile_native_ad_attribution_small"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#F19938"
            android:text="Ad"
            android:textColor="#FFFFFF"
            android:textSize="12sp" />

        <ImageView
            android:id="@+id/iv_list_tile_native_ad_icon"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:scaleType="fitXY"
            tools:background="#EDEDED" />

        <TextView
            android:id="@+id/tv_list_tile_native_ad_attribution_large"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:background="#F19938"
            android:gravity="center"
            android:text="Ad"
            android:textColor="#FFFFFF"
            android:visibility="invisible" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="80dp"
            android:layout_marginLeft="80dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_list_tile_native_ad_headline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:maxLines="1"
                android:textColor="#000000"
                android:textSize="16sp"
                tools:text="Headline" />

            <TextView
                android:id="@+id/tv_list_tile_native_ad_body"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:maxLines="1"
                android:textColor="#828282"
                android:textSize="14sp"
                tools:text="body" />

        </LinearLayout>

    </FrameLayout>

</com.google.android.gms.ads.nativead.NativeAdView>

建立 ListTileNativeAdFactory 類別

  1. 在「專案」窗格中,以滑鼠右鍵按一下 com.codelab.flutter.admobinlineads 套件,然後選取 新增 >Kotlin 檔案/類別

7311744cb97cad75.png

  1. 輸入 ListTileNativeAdFactory 做為名稱,然後從清單中選取「Class」

25691151b5814867.png

  1. 您會看到 ListTileNativeAdFactory 類別已在套件 com.codelab.flutter.admobinlineads 中建立。
  2. 請按照以下方式實作 ListTileNativeAdFactory 類別。請注意,該類別會在 GoogleMobileAdsPlugin.NativeAdFactory 介面中實作 createNativeAd() 方法。

工廠類別負責建立用於顯示原生廣告的 View 物件。如程式碼所示,工廠類別會建立 NativeAdView,並填入 NativeAd 物件。

ListTileNativeAdFactory.kt

// TODO: Implement ListTileNativeAdFactory class

package com.codelab.flutter.admobinlineads

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin

class ListTileNativeAdFactory(val context: Context) : GoogleMobileAdsPlugin.NativeAdFactory {

    override fun createNativeAd(
            nativeAd: NativeAd,
            customOptions: MutableMap<String, Any>?
    ): NativeAdView {
        val nativeAdView = LayoutInflater.from(context)
                .inflate(R.layout.list_tile_native_ad, null) as NativeAdView

        with(nativeAdView) {
            val attributionViewSmall =
                    findViewById<TextView>(R.id.tv_list_tile_native_ad_attribution_small)
            val attributionViewLarge =
                    findViewById<TextView>(R.id.tv_list_tile_native_ad_attribution_large)

            val iconView = findViewById<ImageView>(R.id.iv_list_tile_native_ad_icon)
            val icon = nativeAd.icon
            if (icon != null) {
                attributionViewSmall.visibility = View.VISIBLE
                attributionViewLarge.visibility = View.INVISIBLE
                iconView.setImageDrawable(icon.drawable)
            } else {
                attributionViewSmall.visibility = View.INVISIBLE
                attributionViewLarge.visibility = View.VISIBLE
            }
            this.iconView = iconView

            val headlineView = findViewById<TextView>(R.id.tv_list_tile_native_ad_headline)
            headlineView.text = nativeAd.headline
            this.headlineView = headlineView

            val bodyView = findViewById<TextView>(R.id.tv_list_tile_native_ad_body)
            with(bodyView) {
                text = nativeAd.body
                visibility = if (nativeAd.body.isNotEmpty()) View.VISIBLE else View.INVISIBLE
            }
            this.bodyView = bodyView

            setNativeAd(nativeAd)
        }

        return nativeAdView
    }
}

註冊 ListTileNativeAdFactory 類別

必須先將 NativeAdFactory 的例項註冊至 GoogleMobileAdsPlugin,才能從 Flutter 端使用。

  1. 開啟 MainActivity.kt 檔案,然後覆寫 configureFlutterEngine() 方法和 cleanUpFlutterEngine() 方法。
  2. configureFlutterEngine() 方法中,使用專屬字串 ID (listTile) 註冊 ListTileNativeAdFactory 類別。

MainActivity.kt

class MainActivity: FlutterActivity() {

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // TODO: Register the ListTileNativeAdFactory
        GoogleMobileAdsPlugin.registerNativeAdFactory(
                flutterEngine, "listTile", ListTileNativeAdFactory(context))
    }

    ...
}
  1. 在清理過程中,您應取消註冊每個 NativeAdFactory 執行個體。在 cleanUpFlutterEngine() 方法中取消註冊 ListTileNativeAdFactory 類別。

MainActivity.kt

class MainActivity: FlutterActivity() {
    ...

    override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
        super.cleanUpFlutterEngine(flutterEngine)

        // TODO: Unregister the ListTileNativeAdFactory
        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTile")
    }
}

您現在可以使用 ListTileNativeAdFactory 類別在 Android 上顯示原生廣告了。

導入 NativeAdFactory for iOS (Objective-C)

開啟 ios/Podfile 檔案 (或「ios」資料夾下的任何檔案),然後按一下「在 Xcode 中開啟 iOS 模組」,即可開啟 iOS 專案。

62aa12c10e6d671f.png

準備原生廣告版面配置

您必須有自訂資料檢視 (*.xib),才能設定原生廣告素材資源。在本程式碼研究室中,系統會使用預先設定的檢視畫面,盡可能減少工作量。

在 Xcode 中開啟 iOS 專案,並確認 Runner 專案中有 ListTileNativeAdView.xib

a5f04a32f1868d4f.png

建立 ListTileNativeAdFactory 類別

  1. 在專案導覽工具的「Runner」群組上按一下滑鼠右鍵,然後選取「New File」,為新類別建立標頭檔案。

6455aab9e9881ca.png

  1. 在範本對話方塊中,選取「Header File」,並命名為 ListTileNativeAdFactory
  2. 建立 ListTileNativeAdFactory.h 檔案後,請按照以下方式定義 ListNativeAdFactory 類別:

ListTileNativeAdFactory.h

#ifndef ListTileNativeAdFactory_h
#define ListTileNativeAdFactory_h

// TODO: Import FLTGoogleMobileAdsPlugin.h
#import "FLTGoogleMobileAdsPlugin.h"

// TODO: Declare ListTileNativeAdFactory
@interface ListTileNativeAdFactory : NSObject<FLTNativeAdFactory>

@end


#endif /* ListTileNativeAdFactory_h */
  1. 從「Runner」群組中選取「New File」,建立 Objective-C 檔案。
  2. 在下一個對話方塊中,於「File」欄位中輸入 ListTileNativeAdFactory,然後選取「Empty File」做為檔案類型。

2c9c998c48db3a0.png

  1. 點選「下一步」後,系統會請您選取要建立新檔案的資料夾。保持不變,然後點選「建立」

8635ffe502d1f4ab.png

  1. 請按照以下方式實作 ListTileNativeFactory 類別。請注意,該類別會在 FLTNativeAdFactory 通訊協定中實作 createNativeAd() 方法。

工廠類別負責建立用於顯示原生廣告的 View 物件。如程式碼所示,工廠類別會建立 GADNativeAdView,並填入 GADNativeAd 物件。

ListTileNativeAdFactory.m

// TODO: Import ListTileNativeAdFactory.h
#import "ListTileNativeAdFactory.h"

// TODO: Implement ListTileNativeAdFactory
@implementation ListTileNativeAdFactory

- (GADNativeAdView *)createNativeAd:(GADNativeAd *)nativeAd
                             customOptions:(NSDictionary *)customOptions {
  GADNativeAdView *nativeAdView =
    [[NSBundle mainBundle] loadNibNamed:@"ListTileNativeAdView" owner:nil options:nil].firstObject;

  ((UILabel *)nativeAdView.headlineView).text = nativeAd.headline;

  ((UILabel *)nativeAdView.bodyView).text = nativeAd.body;
  nativeAdView.bodyView.hidden = nativeAd.body ? NO : YES;

  ((UIImageView *)nativeAdView.iconView).image = nativeAd.icon.image;
  nativeAdView.iconView.hidden = nativeAd.icon ? NO : YES;

  nativeAdView.callToActionView.userInteractionEnabled = NO;

  nativeAdView.nativeAd = nativeAd;

  return nativeAdView;
}

@end

註冊 ListTileNativeAdFacotry 類別

FLTNativeAdFactory 的實作項目必須先註冊至 FLTGoogleMobileAdsPlugin,才能從 Flutter 端使用。

開啟 AppDelegate.m 檔案,然後呼叫 [FLTGoogleMobileAdsPlugin registerNativeAdFactory] 方法,使用專屬字串 ID (listTile) 註冊 ListTileNativeAdFactory

AppDelegate.m

#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"

// TODO: Import ListTileNativeAdFactory.h
#import "ListTileNativeAdFactory.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];

  // TODO: Register ListTileNativeAdFactory
  ListTileNativeAdFactory *listTileFactory = [[ListTileNativeAdFactory alloc] init];
  [FLTGoogleMobileAdsPlugin registerNativeAdFactory:self
                                        factoryId:@"listTile"
                                  nativeAdFactory:listTileFactory];

  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

您現在可以使用 ListTileNativeAdFactory 在 iOS 上顯示原生廣告了。

導入 iOS 專用 NativeAdFactory (Swift)

開啟 ios/Podfile 檔案 (或「ios」資料夾下的任何檔案),然後按一下「在 Xcode 中開啟 iOS 模組」,即可開啟 iOS 專案。

62aa12c10e6d671f.png

準備原生廣告版面配置

您必須有自訂資料檢視 (*.xib),才能設定原生廣告素材資源。在本程式碼研究室中,系統會使用預先設定的檢視畫面,盡可能減少工作量。

在 Xcode 中開啟 iOS 專案,並確認 Runner 專案中有 ListTileNativeAdView.xib

a5f04a32f1868d4f.png

建立 ListTileNativeAdFactory 類別

  1. 在專案導覽工具的「Runner」群組上按一下滑鼠右鍵,然後選取「New File」,為新類別建立標頭檔案。

9115c92543345ef1.png

  1. 在範本對話方塊中,選取「Swift File」,並將其命名為 ListTileNativeAdFactory
  2. 建立 ListTileNativeAdFactory.swift 檔案後,實作 ListNativeAdFactory 類別。

請注意,該類別會在 FLTNativeAdFactory 通訊協定中實作 createNativeAd() 方法。

工廠類別負責建立用於顯示原生廣告的 View 物件。如程式碼所示,工廠類別會建立 GADNativeAdView,並填入 GADNativeAd 物件。

ListTileNativeAdFactory.swift

// TODO: Import google_mobile_ads
import google_mobile_ads

// TODO: Implement ListTileNativeAdFactory
class ListTileNativeAdFactory : FLTNativeAdFactory {

    func createNativeAd(_ nativeAd: GADNativeAd,
                        customOptions: [AnyHashable : Any]? = nil) -> GADNativeAdView? {
        let nibView = Bundle.main.loadNibNamed("ListTileNativeAdView", owner: nil, options: nil)!.first
        let nativeAdView = nibView as! GADNativeAdView

        (nativeAdView.headlineView as! UILabel).text = nativeAd.headline

        (nativeAdView.bodyView as! UILabel).text = nativeAd.body
        nativeAdView.bodyView!.isHidden = nativeAd.body == nil

        (nativeAdView.iconView as! UIImageView).image = nativeAd.icon?.image
        nativeAdView.iconView!.isHidden = nativeAd.icon == nil

        nativeAdView.callToActionView?.isUserInteractionEnabled = false

        nativeAdView.nativeAd = nativeAd

        return nativeAdView
    }
}

註冊 ListTileNativeAdFacotry 類別

FLTNativeAdFactory 的實作項目必須先註冊至 FLTGoogleMobileAdsPlugin,才能從 Flutter 端使用。

開啟 AppDelegate.m 檔案,然後呼叫 FLTGoogleMobileAdsPlugin.registerNativeAdFactory() 方法,使用專屬字串 ID (listTile) 註冊 ListTileNativeAdFactory

AppDelegate.swift

import UIKit
import Flutter

// TODO: Import google_mobile_ads
import google_mobile_ads

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    // TODO: Register ListTileNativeAdFactory
    let listTileFactory = ListTileNativeAdFactory()
    FLTGoogleMobileAdsPlugin.registerNativeAdFactory(
        self, factoryId: "listTile", nativeAdFactory: listTileFactory)

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

您現在可以使用 ListTileNativeAdFactory 在 iOS 上顯示原生廣告了。

將原生廣告與 Flutter 小工具整合

  1. 開啟 lib/native_inline_page.dart 檔案接著,新增下列程式碼來匯入 ad_helper.dartgoogle_mobile_ads.dart

native_inline_page.dart

...

// TODO: Import ad_helper.dart
import 'package:admob_inline_ads_in_flutter/ad_helper.dart';

// TODO: Import google_mobile_ads.dart
import 'package:google_mobile_ads/google_mobile_ads.dart';

class NativeInlinePage extends StatefulWidget {
  ...
}
  1. _NativeInlinePageState 類別中,為原生廣告新增下列成員和方法。

請注意,_kAdIndex 代表橫幅廣告展示的索引,用於計算 _getDestinationItemIndex() 方法的項目索引。

native_inline_page.dart

class _NativeInlinePageState extends State<NativeInlinePage> {

  // TODO: Add _kAdIndex
  static final _kAdIndex = 4;

  // TODO: Add a native ad instance
  NativeAd? _ad;

  ...

  // TODO: Add _getDestinationItemIndex()
  int _getDestinationItemIndex(int rawIndex) {
    if (rawIndex >= _kAdIndex && _ad != null) {
      return rawIndex - 1;
    }
    return rawIndex;
  }

  ...
}
  1. initState() 方法中,建立及載入使用 ListTileNativeAdFactory 產生原生廣告檢視畫面的 NativeAd

請注意,系統會使用你為外掛程式註冊工廠時使用的原廠 ID (listTile)。

native_inline_page.dart

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

  // TODO: Create a NativeAd instance
  _ad = NativeAd(
    adUnitId: AdHelper.nativeAdUnitId,
    factoryId: 'listTile',
    request: AdRequest(),
    listener: NativeAdListener(
      onAdLoaded: (ad) {
        setState(() {
          _ad = ad as NativeAd;
        });
      },
      onAdFailedToLoad: (ad, error) {
        // Releases an ad resource when it fails to load
        ad.dispose();
        print('Ad load failed (code=${error.code} message=${error.message})');       },
    ),
  );

  _ad.load();
}
  1. 修改 build() 方法,以顯示可用的橫幅廣告 (如果有的話)。
  2. 更新 itemCount, 即可計算橫幅廣告項目,並更新 itemBuilder,,在廣告載入時在廣告索引 (_kAdIndex) 顯示橫幅廣告。
  3. 更新程式碼,使用 _getDestinationItemIndex() 方法擷取內容項目的索引。

native_inline_page.dart

@override
Widget build(BuildContext context) {
  return Scaffold(
    ...
    body: ListView.builder(
      // TODO: Adjust itemCount based on the ad load state
      itemCount: widget.entries.length + (_ad != null ? 1 : 0),
      itemBuilder: (context, index) {
        // TODO: Render a banner ad
        if (_ad != null && index == _kAdIndex) {
          return Container(
            height: 72.0,
            alignment: Alignment.center,
            child: AdWidget(ad: _ad!),
          );
        } else {
          // TODO: Get adjusted item index from _getDestinationItemIndex()
          final item = widget.entries[_getDestinationItemIndex(index)];

          return ListTile(
            ...
          );
        }
      },
    ),
  );
}
  1. dispose() 回呼方法中呼叫 NativeAd.dispose() 方法,釋出與 NativeAd 物件相關聯的資源。

native_inline_page.dart

@override
void dispose() {
  // TODO: Dispose a NativeAd object
  _ad?.dispose();

  super.dispose();
}

大功告成!執行專案,然後按一下首頁的「Native 內嵌廣告」按鈕。廣告載入後,清單中央會顯示原生廣告。

f1671b0fa349ccf8.png 5ead873222c800eb.png

9. 全部完成!

您已完成程式碼研究室。您可以在 android_studio_folder.png「complete」android_studio_folder.png「complete_kotlin_swift」資料夾中找到本程式碼研究室的完成程式碼。