IMA SDKs make it easy to integrate multimedia ads into your websites and apps. IMA SDKs can request ads from any VAST-compliant ad server and manage ad playback in your apps. With IMA client-side SDKs, you maintain control of content video playback, while the SDK handles ad playback. Ads play in a separate video player positioned on top of the app's content video player.
Android | iOS | |
---|---|---|
Support | SDK 21+ | 12.0+ |
NOTE:
Implementing IMA client-side involves five main SDK components, which are demonstrated in this guide:
This guide demonstrates how to integrate the IMA SDK into a new Widget
using the video_player plugin to display content.
If building on Android, add the user permissions required by the IMA SDK for requesting ads in android/app/src/main/AndroidManifest.xml
.
<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"/>
Add the import statements for the interactive_media_ads
and video_player. Both plugins should already be added to your pubspec.yaml
.
import 'package:interactive_media_ads/interactive_media_ads.dart'; import 'package:video_player/video_player.dart';
Create a new StatefulWidget that handles displaying Ads and playing content.
/// Example widget displaying an Ad before a video. class AdExampleWidget extends StatefulWidget { /// Constructs an [AdExampleWidget]. const AdExampleWidget({super.key}); @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. See more IMA sample // tags at https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags 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 Widget build(BuildContext context) { // ··· } }
Instantiate the AdDisplayContainer for playing Ads and the VideoPlayerController for playing content.
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 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(() {}); }); }
build
MethodReturn a Widget
that contains the ad player and the content player.
@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, ); }
Handle requesting ads and add event listeners to handle when content should be displayed or hidden.
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(); }
Dispose the content player and destroy the AdsManager.
@override void dispose() { super.dispose(); _contentProgressTimer?.cancel(); _contentVideoController.dispose(); _adsManager?.destroy(); // ··· }
That‘s it! You’re now requesting and displaying ads with the IMA SDK. To learn about additional SDK features, see the API reference.
For information on contributing to this plugin, see CONTRIBUTING.md
.