С целью изучения фреймворка 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, Таким образом, флаттер считает, что это, как бы, один и тот же виджет и выводит рекламу везде при любых переходах.
Самая позорная партнерка, которая блочит всех кто накопил минимальную сумма, я накопил 97$, но из за санкций не мог набрать 3$, я их собирал целых 3-4 месяца, и мне в чате мило улыбалась поддержка и говорила, какой адмоб плохой что наложили санкции, и спустя 2 минуты после заказа, мне пришло письмо, что мой аккаунт заблокирован!!! Они тупо сидели и ждали, когда я накоплю минималку. Это позорище 2022 года!!! Я никому не советую работать с этими людьми!