@@ -4,12 +4,274 @@ Flutter plugin for the [Interactive Media Ads SDKs][1].
44
55[ ![ pub package] ( https://img.shields.io/pub/v/interactive_media_ads.svg )] ( https://pub.dev/packages/interactive_media_ads )
66
7- A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS.
7+ IMA SDKs make it easy to integrate multimedia ads into your websites and apps. IMA SDKs can request
8+ ads from any [ VAST-compliant] [ 2 ] ad server and manage ad playback in your apps. With IMA client-side
9+ SDKs, you maintain control of content video playback, while the SDK handles ad playback. Ads play in
10+ a separate video player positioned on top of the app's content video player.
811
912| | Android | iOS |
1013| -------------| ---------| -------|
1114| ** Support** | SDK 19+ | 12.0+ |
1215
1316** This package is still in development.**
1417
18+ ## IMA client-side overview
19+
20+ Implementing IMA client-side involves five main SDK components, which are demonstrated in this
21+ guide:
22+
23+ * [ AdDisplayContainer] [ 3 ] : A container object where ads are rendered.
24+ * [ AdsLoader] [ 4 ] : Requests ads and handles events from ads request responses. You should only
25+ instantiate one ads loader, which can be reused throughout the life of the application.
26+ * [ AdsRequest] [ 5 ] : An object that defines an ads request. Ads requests specify the URL for the VAST
27+ ad tag, as well as additional parameters, such as ad dimensions.
28+ * [ AdsManager] [ 6 ] : Contains the response to the ads request, controls ad playback,
29+ and listens for ad events fired by the SDK.
30+ * [ AdsManagerDelegate] [ 8 ] : Handles ad events and errors that occur during ad or stream
31+ initialization and playback.
32+
33+ ## Usage
34+
35+ This guide demonstrates how to integrate the IMA SDK into a new ` Widget ` using the [ video_player] [ 7 ]
36+ plugin to display content.
37+
38+ ### 1. Add Android Required Permissions
39+
40+ If building on Android, add the user permissions required by the IMA SDK for requesting ads in
41+ ` android/app/src/main/AndroidManifest.xml ` .
42+
43+ <? code-excerpt "example/android/app/src/main/AndroidManifest.xml (android_manifest)"?>
44+ ``` xml
45+ <manifest xmlns : android =" http://schemas.android.com/apk/res/android" >
46+ <!-- Required permissions for the IMA SDK -->
47+ <uses-permission android : name =" android.permission.INTERNET" />
48+ <uses-permission android : name =" android.permission.ACCESS_NETWORK_STATE" />
49+ ```
50+
51+ ### 2. Add Imports
52+
53+ Add the import statements for the ` interactive_media_ads ` and [ video_player] [ 7 ] . Both plugins should
54+ already be added to your ` pubspec.yaml ` .
55+
56+ <? code-excerpt "example/lib/main.dart (imports)"?>
57+ ``` dart
58+ import 'package:interactive_media_ads/interactive_media_ads.dart';
59+ import 'package:video_player/video_player.dart';
60+ ```
61+
62+ ### 3. Create a New Widget
63+
64+ Create a new [ StatefulWidget] ( https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html )
65+ that handles displaying Ads and playing content.
66+
67+ <? code-excerpt "example/lib/main.dart (example_widget)"?>
68+ ``` dart
69+ /// Example widget displaying an Ad before a video.
70+ class AdExampleWidget extends StatefulWidget {
71+ /// Constructs an [AdExampleWidget].
72+ const AdExampleWidget({super.key});
73+
74+ @override
75+ State<AdExampleWidget> createState() => _AdExampleWidgetState();
76+ }
77+
78+ class _AdExampleWidgetState extends State<AdExampleWidget> {
79+ // IMA sample tag for a single skippable inline video ad. See more IMA sample
80+ // tags at https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags
81+ static const String _adTagUrl =
82+ 'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=';
83+
84+ // The AdsLoader instance exposes the request ads method.
85+ late final AdsLoader _adsLoader;
86+
87+ // AdsManager exposes methods to control ad playback and listen to ad events.
88+ AdsManager? _adsManager;
89+
90+ // Whether the widget should be displaying the content video. The content
91+ // player is hidden while Ads are playing.
92+ bool _shouldShowContentVideo = true;
93+
94+ // Controls the content video player.
95+ late final VideoPlayerController _contentVideoController;
96+ // ···
97+ @override
98+ Widget build(BuildContext context) {
99+ // ···
100+ }
101+ }
102+ ```
103+
104+ ### 4. Add the Video Players
105+
106+ Instantiate the [ AdDisplayContainer] [ 3 ] for playing Ads and the
107+ [ VideoPlayerController] ( https://pub.dev/documentation/video_player/latest/video_player/VideoPlayerController-class.html )
108+ for playing content.
109+
110+ <? code-excerpt "example/lib/main.dart (ad_and_content_players)"?>
111+ ``` dart
112+ late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer(
113+ onContainerAdded: (AdDisplayContainer container) {
114+ // Ads can't be requested until the `AdDisplayContainer` has been added to
115+ // the native View hierarchy.
116+ _requestAds(container);
117+ },
118+ );
119+
120+ @override
121+ void initState() {
122+ super.initState();
123+ _contentVideoController = VideoPlayerController.networkUrl(
124+ Uri.parse(
125+ 'https://storage.googleapis.com/gvabox/media/samples/stock.mp4',
126+ ),
127+ )
128+ ..addListener(() {
129+ if (_contentVideoController.value.isCompleted) {
130+ _adsLoader.contentComplete();
131+ setState(() {});
132+ }
133+ })
134+ ..initialize().then((_) {
135+ // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
136+ setState(() {});
137+ });
138+ }
139+ ```
140+
141+ ### 5. Implement the ` build ` Method
142+
143+ Return a ` Widget ` that contains the ad player and the content player.
144+
145+ <? code-excerpt "example/lib/main.dart (widget_build)"?>
146+ ``` dart
147+ @override
148+ Widget build(BuildContext context) {
149+ return Scaffold(
150+ body: Center(
151+ child: SizedBox(
152+ width: 300,
153+ child: !_contentVideoController.value.isInitialized
154+ ? Container()
155+ : AspectRatio(
156+ aspectRatio: _contentVideoController.value.aspectRatio,
157+ child: Stack(
158+ children: <Widget>[
159+ // The display container must be on screen before any Ads can be
160+ // loaded and can't be removed between ads. This handles clicks for
161+ // ads.
162+ _adDisplayContainer,
163+ if (_shouldShowContentVideo)
164+ VideoPlayer(_contentVideoController)
165+ ],
166+ ),
167+ ),
168+ ),
169+ ),
170+ floatingActionButton:
171+ _contentVideoController.value.isInitialized && _shouldShowContentVideo
172+ ? FloatingActionButton(
173+ onPressed: () {
174+ setState(() {
175+ _contentVideoController.value.isPlaying
176+ ? _contentVideoController.pause()
177+ : _contentVideoController.play();
178+ });
179+ },
180+ child: Icon(
181+ _contentVideoController.value.isPlaying
182+ ? Icons.pause
183+ : Icons.play_arrow,
184+ ),
185+ )
186+ : null,
187+ );
188+ }
189+ ```
190+
191+ ### 6. Request Ads
192+
193+ Handle requesting ads and add event listeners to handle when content should be displayed or hidden.
194+
195+ <? code-excerpt "example/lib/main.dart (request_ads)"?>
196+ ``` dart
197+ Future<void> _requestAds(AdDisplayContainer container) {
198+ _adsLoader = AdsLoader(
199+ container: container,
200+ onAdsLoaded: (OnAdsLoadedData data) {
201+ final AdsManager manager = data.manager;
202+ _adsManager = data.manager;
203+
204+ manager.setAdsManagerDelegate(AdsManagerDelegate(
205+ onAdEvent: (AdEvent event) {
206+ debugPrint('OnAdEvent: ${event.type}');
207+ switch (event.type) {
208+ case AdEventType.loaded:
209+ manager.start();
210+ case AdEventType.contentPauseRequested:
211+ _pauseContent();
212+ case AdEventType.contentResumeRequested:
213+ _resumeContent();
214+ case AdEventType.allAdsCompleted:
215+ manager.destroy();
216+ _adsManager = null;
217+ case AdEventType.clicked:
218+ case AdEventType.complete:
219+ }
220+ },
221+ onAdErrorEvent: (AdErrorEvent event) {
222+ debugPrint('AdErrorEvent: ${event.error.message}');
223+ _resumeContent();
224+ },
225+ ));
226+
227+ manager.init();
228+ },
229+ onAdsLoadError: (AdsLoadErrorData data) {
230+ debugPrint('OnAdsLoadError: ${data.error.message}');
231+ _resumeContent();
232+ },
233+ );
234+
235+ return _adsLoader.requestAds(AdsRequest(adTagUrl: _adTagUrl));
236+ }
237+
238+ Future<void> _resumeContent() {
239+ setState(() {
240+ _shouldShowContentVideo = true;
241+ });
242+ return _contentVideoController.play();
243+ }
244+
245+ Future<void> _pauseContent() {
246+ setState(() {
247+ _shouldShowContentVideo = false;
248+ });
249+ return _contentVideoController.pause();
250+ }
251+ ```
252+
253+ ### 7. Dispose Resources
254+
255+ Dispose the content player and the destroy the [ AdsManager] [ 6 ] .
256+
257+ <? code-excerpt "example/lib/main.dart (dispose)"?>
258+ ``` dart
259+ @override
260+ void dispose() {
261+ super.dispose();
262+ _contentVideoController.dispose();
263+ _adsManager?.destroy();
264+ }
265+ ```
266+
267+ That's it! You're now requesting and displaying ads with the IMA SDK. To learn about additional SDK
268+ features, see the [ API reference] ( https://pub.dev/documentation/interactive_media_ads/latest/ ) .
269+
15270[ 1 ] : https://developers.google.com/interactive-media-ads
271+ [ 2 ] : https://www.iab.com/guidelines/vast/
272+ [ 3 ] : https://pub.dev/documentation/interactive_media_ads/latest/interactive_media_ads/AdDisplayContainer-class.html
273+ [ 4 ] : https://pub.dev/documentation/interactive_media_ads/latest/interactive_media_ads/AdsLoader-class.html
274+ [ 5 ] : https://pub.dev/documentation/interactive_media_ads/latest/interactive_media_ads/AdsRequest-class.html
275+ [ 6 ] : https://pub.dev/documentation/interactive_media_ads/latest/interactive_media_ads/AdsManager-class.html
276+ [ 7 ] : https://pub.dev/packages/video_player
277+ [ 8 ] : https://pub.dev/documentation/interactive_media_ads/latest/interactive_media_ads/AdsManagerDelegate-class.html
0 commit comments