Skip to content

Commit 62e1963

Browse files
[interactive_media_ads] Updates README with a usage section and fix some interface docs (flutter#6988)
Part of flutter#134228
1 parent 69e7fc1 commit 62e1963

File tree

9 files changed

+324
-27
lines changed

9 files changed

+324
-27
lines changed

packages/interactive_media_ads/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.0.2+1
2+
3+
* Updates `README` with a usage section and fix app-facing interface documentation.
4+
15
## 0.0.2
26

37
* Adds Android implementation.

packages/interactive_media_ads/README.md

Lines changed: 263 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
2121
*
2222
* This must match the version in pubspec.yaml.
2323
*/
24-
const val pluginVersion = "0.0.2"
24+
const val pluginVersion = "0.0.2+1"
2525
}
2626

2727
override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) {

packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
<!--#docregion android_manifest-->
12
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
23
<!-- Required permissions for the IMA SDK -->
34
<uses-permission android:name="android.permission.INTERNET"/>
45
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
5-
6+
<!--#enddocregion android_manifest-->
67
<application
78
android:label="interactive_media_ads_example"
89
android:name="${applicationName}"

0 commit comments

Comments
 (0)