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

1. 簡介

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

建構項目

本程式碼研究室會引導您使用 Google Mobile Ads 外掛程式 (適用於 Flutter),在 Flutter 應用程式中導入 AdMob 內嵌橫幅和 AdMob 原生內嵌廣告。

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

課程內容

  • 如何設定 Google Mobile Ads 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 starter:您將在本程式碼研究室中建構的起始程式碼。
  • android_studio_folder.png complete:本程式碼研究室完成後的程式碼。(適用於原生程式碼的 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」Android做為平台。

d51828db0e2e4f6c.png

  1. 啟用使用者指標並非完成本程式碼研究室的必要條件。不過,我們建議您這麼做,因為這樣才能更詳細地瞭解使用者行為。按一下「新增」即可完成程序。

b918bf44362813a9.png

建立廣告單元

如要新增廣告單元,請按照下列步驟操作:

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

橫幅

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

原生

  1. 按一下「新增廣告單元」
  2. 選取「原生進階」做為格式。
  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. 選取「原生進階」做為格式。
  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 應用程式,AdMob 內嵌廣告應用程式的 Android 和 iOS 版本都會使用該外掛程式。

將 Google Mobile Ads 外掛程式新增為依附元件

如要從 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 inline ads」專案中安裝外掛程式。

93ef6061e58ebc86.png

更新 AndroidManifest.xml (Android)

  1. 在 Android Studio 中開啟 android/app/src/main/AndroidManifest.xml 檔案。
  2. 新增 <meta-data> 標記,並將名稱設為 com.google.android.gms.ads.APPLICATION_ID,即可加入 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. 新增 GADApplicationIdentifier 鍵,並使用 AdMob 營利應用程式 ID 設定字串值。舉例來說,如果 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 初始化完成後取得結果。

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 橫幅的 BannerAd (AdSize.banner)。請注意,廣告事件監聽器已設定為在載入廣告時更新 UI (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();
}

大功告成!執行專案,然後按一下首頁的「Banner inline ad」按鈕。廣告載入後,您會在清單中間看到橫幅廣告。

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 for Editing in Android Studio」,開啟 Android 專案。

623ad3d2282ccbf8.png

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

d188bb51cf7c2d08.png

建立原生廣告版面配置

  1. 開啟 Android 專案後,在 Android Studio 的專案窗格中,以滑鼠右鍵按一下「app」,然後從內容選單中選取「New」>「Android Resource File」

2b629ee277a68fd7.png

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

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. 在「Project」窗格中,以滑鼠右鍵按一下 com.codelab.flutter.admobinlineads 套件,然後依序選取「New」>「Java Class」

9f3f111dd207a9b4.png

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

47ff82d92676e26.png

  1. 「New Class」對話方塊隨即顯示,請將所有欄位留空,然後按一下「OK」

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

e4ed232c358ffb19.png

  1. 實作 ListTileNativeAdFactory 類別,如下所示。請注意,這個類別會實作 GoogleMobileAdsPlugin.NativeAdFactory 介面中的 createNativeAd() 方法。

工廠類別負責建立檢視區塊物件,以算繪原生廣告。如程式碼所示,工廠類別會建立 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 for Editing in Android Studio」,開啟 Android 專案。

623ad3d2282ccbf8.png

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

d188bb51cf7c2d08.png

建立原生廣告版面配置

  1. 開啟 Android 專案後,在 Android Studio 的專案窗格中,以滑鼠右鍵按一下「app」,然後從內容選單中選取「New」>「Android Resource File」

2b629ee277a68fd7.png

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

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. 在「Project」窗格中,以滑鼠右鍵按一下 com.codelab.flutter.admobinlineads 套件,然後選取「New」>「Kotlin File/Class」

7311744cb97cad75.png

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

25691151b5814867.png

  1. 您會看到 ListTileNativeAdFactory 類別是在 com.codelab.flutter.admobinlineads 套件中建立。
  2. 實作 ListTileNativeAdFactory 類別,如下所示。請注意,這個類別會實作 GoogleMobileAdsPlugin.NativeAdFactory 介面中的 createNativeAd() 方法。

工廠類別負責建立檢視區塊物件,以算繪原生廣告。如程式碼所示,工廠類別會建立 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 上算繪原生廣告。

為 iOS (Objective-C) 實作 NativeAdFactory

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

62aa12c10e6d671f.png

準備原生廣告版面配置

您需要自訂檢視畫面 (*.xib),才能安排原生廣告素材資源。在本程式碼研究室中,我們使用預先設定的檢視區塊,盡量減少您的工作量。

在 Xcode 中開啟 iOS 專案,確認 ListTileNativeAdView.xib 存在於 Runner 專案中。

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() 方法。

工廠類別負責建立檢視區塊物件,以算繪原生廣告。如程式碼所示,工廠類別會建立 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 (Swift) 導入 NativeAdFactory

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

62aa12c10e6d671f.png

準備原生廣告版面配置

您需要自訂檢視畫面 (*.xib),才能安排原生廣告素材資源。在本程式碼研究室中,我們使用預先設定的檢視區塊,盡量減少您的工作量。

在 Xcode 中開啟 iOS 專案,確認 ListTileNativeAdView.xib 存在於 Runner 專案中。

a5f04a32f1868d4f.png

建立 ListTileNativeAdFactory 類別

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

9115c92543345ef1.png

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

請注意,這個類別會導入 FLTNativeAdFactory 通訊協定中的 createNativeAd() 方法。

工廠類別負責建立檢視區塊物件,以算繪原生廣告。如程式碼所示,工廠類別會建立 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 inline ad」按鈕。廣告載入後,您會在清單中間看到原生廣告。

f1671b0fa349ccf8.png 5ead873222c800eb.png

9. 大功告成!

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