Flutter и Appodeal: мой опыт подключения

С целью изучения фреймворка Flutter, начал я разрабатывать свое небольшое приложение. Сферическое TODO не мотивирует, поэтому я сразу нацелился сделать что-нибудь реальное и коммерческое. А значит, сразу же нужно было потренироваться и в подключении рекламы.

По разным причинам я остановился на агрегаторе Appodeal. Раньше с ними дел не имел, но так как «доход» с большой доле вероятности будет копеечный, то и неудача не разочарует.

На pub.dev есть несколько плагинов на эту тему, но с большими оценками и обновляемый только appodeal_flutter Его я и взял. И вполне успешно применил, но столкнулся с некоторыми нюансами, которые изложу здесь для себя и кого-нибудь, кто вдруг выйдет на эту статью.

UPD. Главный нюанс. В статистике appodeal просмотры рекламы не учитывались, пришлось отказаться в пользу admob.

Коротко упомяну, что изложено в описании к плагину. Плагин нужно, естественно, добавить в зависомости в файл pubspec.yaml. Дальше нужно разобраться с поддержкой Multidex (для Андроид), но в случае, если вы собираетесь сделать приложение с поддержкой Андроид 5 или ниже. Я не разбирался, так как поставил минимальную версию API 22 (Андроид 5.1) и то сомневаюсь, что будет актуально. Нормально работают современные приложения где-то от API 24.

Так как я не имею (пока) id в AdMob (реклама от Гугл), я вставил в AndroidManifest записи с «пустыми» данными:

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

Дальше, ориентируемся на пример, но учитываем, что он создан на основе одной страницы! К сожалению.

В описании, сказано, что где-то в самом начале, идеально в файле main.dart нужно инициализировать рекламу. То есть, обращаем внимание, что делать это нужно один раз в приложении или при изменении каких-либо параметров. Не нужно это делать на каждой странице или перед каждым вызовом рекламы.

ID appodeal устанавливаем один раз (он же у нас в личном кабинете для всего приложения выдается)

// Set the Appodeal app keys
Appodeal.setAppKeys(
  androidAppKey: '<your-appodeal-android-key>',
  iosAppKey: '<your-appodeal-ios-key>',
);

По идее, и типы рекламы инициализируем в самом начале, но, если ориентироваться на GDPR (не в России), то нужно учитывать, что пользователь может изменить свое разрешение (и нужно предоставить ему такую возможность).

// Initialize Appodeal
await Appodeal.initialize(
  hasConsent: true,
  adTypes: [AdType.BANNER, AdType.INTERSTITIAL, AdType.REWARD],
  testMode: true,
  verbose: true,
);

hasConsent — как раз и есть это разрешение (или не разрешение) показывать целевую рекламу, которая выдается на основе собранных о пользователе данных.

adType — типы рекламы, которые собираетесь показывать

testMode — пока запускаете на эмуляторах, ставите true, а то, как мне написали в поддержке, рекламодатели к этому очень плохо относятся. Не забудьте поставить в false, когда будете собирать релизную версию.

verbose — выдавать ли подробную информацию (логи) в лог флаттера. Там очень много сыпется всего, что немного раздражает и мешает отладке. С другой стороны, можно кое что узнать полезное.

Прежде чем инициализировать плагин и начать показывать рекламу, вам может потребоваться получить согласие пользователя на отслеживание в Интернете, в зависимости от его местоположения или операционной системы, которую он использует.

В зависимости от местонахождения ваших пользователей они могут быть защищены законами о конфиденциальности GDPR или CCPA. (Для России сейчас это не актуально, но мы же замахиваемся и на зарубежные рынки!?) Эти законы, среди прочего, требуют, чтобы разработчики приложений собирали согласие пользователей, прежде чем рекламодатели смогут отслеживать их в Интернете. Вы можете проверить, защищен ли пользователь какими-либо законами о конфиденциальности, вызвав функцию Appodeal.shouldShowConsent ():

Если пользователь защищен, то нужно запросить у него, разрешает ли он подробный сбор информации. В описании к плагину предлагается «два» варианта. Первый, самостоятельно разработать интерфейс для запроса и, второй, воспользоваться механизмом от Appodeal. И вот тут проявляется первый важный нюанс.

Функция

await Appodeal.requestConsentAuthorization();

не возвращает результата и не позволяет обработать свое завершение. То есть, мы не можем написать что-то типа «var result = await Appodeal.requestConsentAuthorization()» или «await Appodeal.requestConsentAuthorization().then(//обработка результата). В какой-то момент (в какой?) мы должны опросить Appodeal и получить ответ, разрешил ли нам пользователь или нет и установить соответственно hasConsent (и переинициализировать, если этот реквизит изменился).

var consent = await Appodeal.fetchConsentInfo();
 var hasConsent = consent.status == ConsentStatus.PERSONALIZED || consent.status == ConsentStatus.PARTLY_PERSONALIZED;
 await Appodeal.initialize(hasConsent: hasConsent, ...)

Поэтому, я выкрутился так. На странице заставки проверяю, нужно ли запрашивать разрешение, запрашиваю и… на странице заставки сделал кнопку «Далее…», которая опрашивает статус и переинициализирует при необходимости. В момент запуска Appodeal.requestConsentAuthorization() открывается «портянка» текста на английском языке и две кнопки «Yes» и «No». При нажатии на любую происходит возврат в наше приложение. В моем случае, к странице заставки и кнопке «Далее».

Я запросил у автора плагина информацию по этому вопросу, но он мне на электронное письмо не ответил, к сожалению.

Если кто-то смог придумать более красивое решение, напишите, пожалуйста, в комментариях.

Дальше, в нужных местах мы можем запускать рекламу. Просто написав, к примеру «child: AppodealBanner()». Я решил не использовать нижний Bar для показа баннеров, а обошелся следующей конструкцией (чтобы реклама всегда была внизу экрана и не мешала прокрутке основного окна).

Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children:[
  Expanded(
    SingleChildScrollView(//Здесь весь контент
    ),
    Container(//контейнер для рекламы
      child: Align(alignment: Alignment.bottomCenter,
        Child: Hero(tag: 'my_banner', child: AppodealBanner())
      )
    )
  )
  ]
)

За счет spaceBetween нижний контейнер сдвигается в самый низ, а expanded занимает оставшееся место и внутри него из-за SingleChildScrollView имеется прокрутка, если контент не убирается на экране.

Внимательный читатель заметит, что сам баннер я обернул еще и в виджет Hero. На самом деле я еще и баннер оборачиваю, а здесь упрощенный код. Hero же привел специально, потому что за счет этого обхожу второй нюанс!

Если мы выводим баннер на первой странице, потом push и переходим на вторую страницу. На ней мы так же выводим баннер. В какой-то момент возвращаемся на первую страницу (pop или просто по стрелке «Назад») и… баннер на первой странице не отображается, к сожалению. Очевидно, второй баннер «перебил» наш первый и тут выход или перерисовывать при каждом возвращении или…

Я обернул в Hero на всех страницах и указал один и тот же tag, Таким образом, флаттер считает, что это, как бы, один и тот же виджет и выводит рекламу везде при любых переходах.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *