Flutter互动媒体广告插件interactive_media_ads的使用
Flutter互动媒体广告插件interactive_media_ads的使用
简介
interactive_media_ads
是一个用于在Flutter应用中集成Google Interactive Media Ads (IMA) SDK的插件。通过这个插件,你可以轻松地将多媒体广告集成到你的应用中。IMA SDK可以从任何符合VAST标准的广告服务器请求广告,并管理广告的播放。
支持平台
- Android: SDK 21+
- iOS: 12.0+
注意事项
- 目前不支持伴生广告(Companion ads)、背景音频广告(Background Audio ads)和Google动态广告插入(Google Dynamic Ad Insertion)。
IMA客户端侧概述
实现IMA客户端侧涉及五个主要的SDK组件:
- AdDisplayContainer: 广告渲染的容器对象。
- AdsLoader: 请求广告并处理广告请求响应事件。你应该只实例化一个广告加载器,并在整个应用程序生命周期中重用它。
- AdsRequest: 定义广告请求的对象。广告请求指定VAST广告标签的URL以及其他参数,如广告尺寸。
- AdsManager: 包含广告请求的响应,控制广告播放,并监听SDK发出的广告事件。
- AdsManagerDelegate: 处理广告或流初始化和播放期间发生的事件和错误。
使用步骤
1. 添加Android所需权限
如果在Android上构建,需要在 android/app/src/main/AndroidManifest.xml
中添加IMA SDK所需的用户权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required permissions for the IMA SDK -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>
2. 添加导入语句
在Dart文件中添加 interactive_media_ads
和 video_player
插件的导入语句。这两个插件应该已经在 pubspec.yaml
中添加。
import 'package:interactive_media_ads/interactive_media_ads.dart';
import 'package:video_player/video_player.dart';
3. 创建一个新的Widget
创建一个新的 StatefulWidget
来处理广告显示和内容播放。
/// Example widget displaying an Ad before a video.
class AdExampleWidget extends StatefulWidget {
/// Constructs an [AdExampleWidget].
const AdExampleWidget({super.key});
[@override](/user/override)
State<AdExampleWidget> createState() => _AdExampleWidgetState();
}
class _AdExampleWidgetState extends State<AdExampleWidget> with WidgetsBindingObserver {
// IMA sample tag for a pre-, mid-, and post-roll, single inline video ad.
static const String _adTagUrl =
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpost&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=';
// The AdsLoader instance exposes the request ads method.
late final AdsLoader _adsLoader;
// AdsManager exposes methods to control ad playback and listen to ad events.
AdsManager? _adsManager;
// Whether the widget should be displaying the content video. The content
// player is hidden while Ads are playing.
bool _shouldShowContentVideo = false;
// Controls the content video player.
late final VideoPlayerController _contentVideoController;
// Periodically updates the SDK of the current playback progress of the
// content video.
Timer? _contentProgressTimer;
// Provides the SDK with the current playback progress of the content video.
// This is required to support mid-roll ads.
final ContentProgressProvider _contentProgressProvider = ContentProgressProvider();
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 300,
child: !_contentVideoController.value.isInitialized
? Container()
: AspectRatio(
aspectRatio: _contentVideoController.value.aspectRatio,
child: Stack(
children: <Widget>[
// The display container must be on screen before any Ads can be
// loaded and can't be removed between ads. This handles clicks for
// ads.
_adDisplayContainer,
if (_shouldShowContentVideo)
VideoPlayer(_contentVideoController)
],
),
),
),
),
floatingActionButton:
_contentVideoController.value.isInitialized && _shouldShowContentVideo
? FloatingActionButton(
onPressed: () {
setState(() {
_contentVideoController.value.isPlaying
? _contentVideoController.pause()
: _contentVideoController.play();
});
},
child: Icon(
_contentVideoController.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
)
: null,
);
}
}
4. 添加视频播放器
实例化 AdDisplayContainer
用于播放广告,以及 VideoPlayerController
用于播放内容。
late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer(
onContainerAdded: (AdDisplayContainer container) {
_adsLoader = AdsLoader(
container: container,
onAdsLoaded: (OnAdsLoadedData data) {
final AdsManager manager = data.manager;
_adsManager = data.manager;
manager.setAdsManagerDelegate(AdsManagerDelegate(
onAdEvent: (AdEvent event) {
debugPrint('OnAdEvent: ${event.type} => ${event.adData}');
switch (event.type) {
case AdEventType.loaded:
manager.start();
case AdEventType.contentPauseRequested:
_pauseContent();
case AdEventType.contentResumeRequested:
_resumeContent();
case AdEventType.allAdsCompleted:
manager.destroy();
_adsManager = null;
case AdEventType.clicked:
case AdEventType.complete:
case _:
}
},
onAdErrorEvent: (AdErrorEvent event) {
debugPrint('AdErrorEvent: ${event.error.message}');
_resumeContent();
},
));
manager.init(settings: AdsRenderingSettings(enablePreloading: true));
},
onAdsLoadError: (AdsLoadErrorData data) {
debugPrint('OnAdsLoadError: ${data.error.message}');
_resumeContent();
},
);
// Ads can't be requested until the `AdDisplayContainer` has been added to
// the native View hierarchy.
_requestAds(container);
},
);
[@override](/user/override)
void initState() {
super.initState();
_contentVideoController = VideoPlayerController.networkUrl(
Uri.parse(
'https://storage.googleapis.com/gvabox/media/samples/stock.mp4',
),
)
..addListener(() {
if (_contentVideoController.value.isCompleted) {
_adsLoader.contentComplete();
}
setState(() {});
})
..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
}
5. 实现 build
方法
返回一个包含广告播放器和内容播放器的 Widget
。
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 300,
child: !_contentVideoController.value.isInitialized
? Container()
: AspectRatio(
aspectRatio: _contentVideoController.value.aspectRatio,
child: Stack(
children: <Widget>[
// The display container must be on screen before any Ads can be
// loaded and can't be removed between ads. This handles clicks for
// ads.
_adDisplayContainer,
if (_shouldShowContentVideo)
VideoPlayer(_contentVideoController)
],
),
),
),
),
floatingActionButton:
_contentVideoController.value.isInitialized && _shouldShowContentVideo
? FloatingActionButton(
onPressed: () {
setState(() {
_contentVideoController.value.isPlaying
? _contentVideoController.pause()
: _contentVideoController.play();
});
},
child: Icon(
_contentVideoController.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
)
: null,
);
}
6. 请求广告
处理广告请求并添加事件监听器来控制内容的显示或隐藏。
Future<void> _requestAds(AdDisplayContainer container) {
return _adsLoader.requestAds(AdsRequest(
adTagUrl: _adTagUrl,
contentProgressProvider: _contentProgressProvider,
));
}
Future<void> _resumeContent() async {
setState(() {
_shouldShowContentVideo = true;
});
if (_adsManager != null) {
_contentProgressTimer = Timer.periodic(
const Duration(milliseconds: 200),
(Timer timer) async {
if (_contentVideoController.value.isInitialized) {
final Duration? progress = await _contentVideoController.position;
if (progress != null) {
await _contentProgressProvider.setProgress(
progress: progress,
duration: _contentVideoController.value.duration,
);
}
}
},
);
}
await _contentVideoController.play();
}
Future<void> _pauseContent() {
setState(() {
_shouldShowContentVideo = false;
});
_contentProgressTimer?.cancel();
_contentProgressTimer = null;
return _contentVideoController.pause();
}
7. 释放资源
释放内容播放器并销毁 AdsManager
。
[@override](/user/override)
void dispose() {
super.dispose();
_contentProgressTimer?.cancel();
_contentVideoController.dispose();
_adsManager?.destroy();
WidgetsBinding.instance.removeObserver(this);
}
完整示例代码
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:interactive_media_ads/interactive_media_ads.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(const MaterialApp(home: AdExampleWidget()));
}
/// Example widget displaying an Ad before a video.
class AdExampleWidget extends StatefulWidget {
/// Constructs an [AdExampleWidget].
const AdExampleWidget({super.key});
[@override](/user/override)
State<AdExampleWidget> createState() => _AdExampleWidgetState();
}
class _AdExampleWidgetState extends State<AdExampleWidget> with WidgetsBindingObserver {
// IMA sample tag for a pre-, mid-, and post-roll, single inline video ad.
static const String _adTagUrl =
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpost&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=';
// The AdsLoader instance exposes the request ads method.
late final AdsLoader _adsLoader;
// AdsManager exposes methods to control ad playback and listen to ad events.
AdsManager? _adsManager;
// Last state received in `didChangeAppLifecycleState`.
AppLifecycleState _lastLifecycleState = AppLifecycleState.resumed;
// Whether the widget should be displaying the content video. The content
// player is hidden while Ads are playing.
bool _shouldShowContentVideo = false;
// Controls the content video player.
late final VideoPlayerController _contentVideoController;
// Periodically updates the SDK of the current playback progress of the
// content video.
Timer? _contentProgressTimer;
// Provides the SDK with the current playback progress of the content video.
// This is required to support mid-roll ads.
final ContentProgressProvider _contentProgressProvider = ContentProgressProvider();
late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer(
onContainerAdded: (AdDisplayContainer container) {
_adsLoader = AdsLoader(
container: container,
onAdsLoaded: (OnAdsLoadedData data) {
final AdsManager manager = data.manager;
_adsManager = data.manager;
manager.setAdsManagerDelegate(AdsManagerDelegate(
onAdEvent: (AdEvent event) {
debugPrint('OnAdEvent: ${event.type} => ${event.adData}');
switch (event.type) {
case AdEventType.loaded:
manager.start();
case AdEventType.contentPauseRequested:
_pauseContent();
case AdEventType.contentResumeRequested:
_resumeContent();
case AdEventType.allAdsCompleted:
manager.destroy();
_adsManager = null;
case AdEventType.clicked:
case AdEventType.complete:
case _:
}
},
onAdErrorEvent: (AdErrorEvent event) {
debugPrint('AdErrorEvent: ${event.error.message}');
_resumeContent();
},
));
manager.init(settings: AdsRenderingSettings(enablePreloading: true));
},
onAdsLoadError: (AdsLoadErrorData data) {
debugPrint('OnAdsLoadError: ${data.error.message}');
_resumeContent();
},
);
// Ads can't be requested until the `AdDisplayContainer` has been added to
// the native View hierarchy.
_requestAds(container);
},
);
[@override](/user/override)
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_contentVideoController = VideoPlayerController.networkUrl(
Uri.parse(
'https://storage.googleapis.com/gvabox/media/samples/stock.mp4',
),
)
..addListener(() {
if (_contentVideoController.value.isCompleted) {
_adsLoader.contentComplete();
}
setState(() {});
})
..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
}
[@override](/user/override)
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
if (!_shouldShowContentVideo) {
_adsManager?.resume();
}
case AppLifecycleState.inactive:
// Pausing the Ad video player on Android can only be done in this state
// because it corresponds to `Activity.onPause`. This state is also
// triggered before resume, so this will only pause the Ad if the app is
// in the process of being sent to the background.
if (!_shouldShowContentVideo && _lastLifecycleState == AppLifecycleState.resumed) {
_adsManager?.pause();
}
case AppLifecycleState.hidden:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
}
_lastLifecycleState = state;
}
Future<void> _requestAds(AdDisplayContainer container) {
return _adsLoader.requestAds(AdsRequest(
adTagUrl: _adTagUrl,
contentProgressProvider: _contentProgressProvider,
));
}
Future<void> _resumeContent() async {
setState(() {
_shouldShowContentVideo = true;
});
if (_adsManager != null) {
_contentProgressTimer = Timer.periodic(
const Duration(milliseconds: 200),
(Timer timer) async {
if (_contentVideoController.value.isInitialized) {
final Duration? progress = await _contentVideoController.position;
if (progress != null) {
await _contentProgressProvider.setProgress(
progress: progress,
duration: _contentVideoController.value.duration,
);
}
}
},
);
}
await _contentVideoController.play();
}
Future<void> _pauseContent() {
setState(() {
_shouldShowContentVideo = false;
});
_contentProgressTimer?.cancel();
_contentProgressTimer = null;
return _contentVideoController.pause();
}
[@override](/user/override)
void dispose() {
super.dispose();
_contentProgressTimer?.cancel();
_contentVideoController.dispose();
_adsManager?.destroy();
WidgetsBinding.instance.removeObserver(this);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 300,
child: !_contentVideoController.value.isInitialized
? Container()
: AspectRatio(
aspectRatio: _contentVideoController.value.aspectRatio,
child: Stack(
children: <Widget>[
// The display container must be on screen before any Ads can be
// loaded and can't be removed between ads. This handles clicks for
// ads.
_adDisplayContainer,
if (_shouldShowContentVideo)
VideoPlayer(_contentVideoController)
],
),
),
),
),
floatingActionButton:
_contentVideoController.value.isInitialized && _shouldShowContentVideo
? FloatingActionButton(
onPressed: () {
setState(() {
_contentVideoController.value.isPlaying
? _contentVideoController.pause()
: _contentVideoController.play();
});
},
child: Icon(
_contentVideoController.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
)
: null,
);
}
}
更多关于Flutter互动媒体广告插件interactive_media_ads的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter互动媒体广告插件interactive_media_ads的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中集成和使用interactive_media_ads
插件的示例代码。请注意,interactive_media_ads
是一个假定的插件名称,因为实际中可能存在不同的插件用于实现互动媒体广告。不过,我将提供一个通用的模板,你可以根据实际的插件文档进行调整。
首先,确保你已经在pubspec.yaml
文件中添加了插件依赖项(这里以interactive_media_ads
为例,实际名称可能会有所不同):
dependencies:
flutter:
sdk: flutter
interactive_media_ads: ^x.y.z # 替换为实际版本号
然后,运行flutter pub get
来安装依赖项。
接下来,在你的Flutter项目中,你可以按照以下步骤使用interactive_media_ads
插件:
- 导入插件:
在你的Dart文件中(比如main.dart
),导入插件:
import 'package:interactive_media_ads/interactive_media_ads.dart';
- 初始化广告:
通常,你需要一个广告单元ID(Ad Unit ID),这是从广告提供商那里获取的。以下是一个初始化广告的示例:
void main() {
runApp(MyApp());
// 假设在应用程序启动时初始化广告
_initializeInteractiveMediaAd();
}
void _initializeInteractiveMediaAd() {
// 创建一个InteractiveMediaAd实例
final InteractiveMediaAd interactiveMediaAd = InteractiveMediaAd(
adUnitId: 'YOUR_AD_UNIT_ID', // 替换为你的广告单元ID
listener: (InteractiveMediaAdEvent event, Map<String, dynamic> info) {
// 处理广告事件,比如加载完成、点击、关闭等
if (event == InteractiveMediaAdEvent.loaded) {
print('广告加载完成');
// 可以在这里显示广告
} else if (event == InteractiveMediaAdEvent.clicked) {
print('广告被点击');
} else if (event == InteractiveMediaAdEvent.closed) {
print('广告已关闭');
}
// 其他事件处理...
},
);
// 加载广告
interactiveMediaAd.load();
}
- 显示广告:
在适当的时候(比如用户完成某个任务或达到某个页面),显示广告:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Interactive Media Ads Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// 假设interactiveMediaAd已经在某处被初始化并加载
// 显示广告(这里需要确保广告已经加载完成)
interactiveMediaAd.show();
},
child: Text('显示广告'),
),
),
),
);
}
}
- 处理广告生命周期:
确保在应用程序的生命周期中适当地管理广告(比如暂停和恢复广告)。这通常涉及到在AppLifecycleState
变化时调用插件提供的方法。
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
// 应用进入后台时暂停广告(如果插件支持)
interactiveMediaAd.pause();
} else if (state == AppLifecycleState.resumed) {
// 应用回到前台时恢复广告
interactiveMediaAd.resume();
}
}
请注意,上述代码是一个模板,具体的API和方法名称可能会根据你使用的实际插件而有所不同。务必参考插件的官方文档来获取最准确的信息。