Files
packages/packages/interactive_media_ads
dependabot[bot] 091acc58ac [dependabot]: Bump org.mockito.kotlin:mockito-kotlin from 6.1.0 to 6.2.3 in /packages/interactive_media_ads/android (#10983)
Bumps [org.mockito.kotlin:mockito-kotlin](https://github.com/mockito/mockito-kotlin) from 6.1.0 to 6.2.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/mockito/mockito-kotlin/releases">org.mockito.kotlin:mockito-kotlin's releases</a>.</em></p>
<blockquote>
<h2>v6.2.3</h2>
<h4>6.2.3</h4>
<ul>
<li>2026-01-26 - <a href="https://github.com/mockito/mockito-kotlin/compare/v6.2.2...v6.2.3">2 commit(s)</a> by Joshua Selbo, Mark Koops</li>
<li>Update exception message now that onGeneric is deprecated [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/583">#583</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/583">mockito/mockito-kotlin#583</a>)</li>
<li>Improve Result support in matchers and captors [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/582">#582</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/582">mockito/mockito-kotlin#582</a>)</li>
</ul>
<h2>v6.2.2</h2>
<h4>6.2.2</h4>
<ul>
<li>2026-01-21 - <a href="https://github.com/mockito/mockito-kotlin/compare/v6.2.1...v6.2.2">2 commit(s)</a> by Mark Koops</li>
<li>Fix eq() matcher NPE with nullable value class and null value [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/579">#579</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/579">mockito/mockito-kotlin#579</a>)</li>
<li>Improve support for stubbing Result return values for synchronous and suspendable functions [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/576">#576</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/576">mockito/mockito-kotlin#576</a>)</li>
</ul>
<h2>v6.2.1</h2>
<h4>6.2.1</h4>
<ul>
<li>2026-01-17 - <a href="https://github.com/mockito/mockito-kotlin/compare/v6.2.0...v6.2.1">2 commit(s)</a> by Joshua Selbo, Mark Koops</li>
<li>Solve 6.2.0 regression due to runblocking being unavailable [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/575">#575</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/575">mockito/mockito-kotlin#575</a>)</li>
<li>Use jvmToolchain API to configure builds [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/572">#572</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/572">mockito/mockito-kotlin#572</a>)</li>
</ul>
<h2>v6.2.0</h2>
<h4>6.2.0</h4>
<ul>
<li>2026-01-14 - <a href="https://github.com/mockito/mockito-kotlin/compare/v6.1.0...v6.2.0">27 commit(s)</a> by Joshua Selbo, Mark Koops, Tim van der Lippe</li>
<li>Improving stubbing support for suspend functions and value classes [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/569">#569</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/569">mockito/mockito-kotlin#569</a>)</li>
<li>Update PR template to run formatting [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/568">#568</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/568">mockito/mockito-kotlin#568</a>)</li>
<li>Ignore ktfmt formatting revision in git blame [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/567">#567</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/567">mockito/mockito-kotlin#567</a>)</li>
<li>Enable Spotless formatting check in CI [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/566">#566</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/566">mockito/mockito-kotlin#566</a>)</li>
<li>Apply ktfmt formatting [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/565">#565</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/565">mockito/mockito-kotlin#565</a>)</li>
<li>Add tooling for Spotless + ktfmt [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/562">#562</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/562">mockito/mockito-kotlin#562</a>)</li>
<li>Follow-up on primitive value classes [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/561">#561</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/561">mockito/mockito-kotlin#561</a>)</li>
<li>Update MockSettings usage [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/560">#560</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/560">mockito/mockito-kotlin#560</a>)</li>
<li>Fix anyOrNull matcher with value class wrapping primitive type [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/557">#557</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/557">mockito/mockito-kotlin#557</a>)</li>
<li>Enhance/unify and document stubbing API, more groundwork towards implementing improved suspend function support [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/556">#556</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/556">mockito/mockito-kotlin#556</a>)</li>
<li>Add wrappers for mockStatic, mockConstruction [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/551">#551</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/551">mockito/mockito-kotlin#551</a>)</li>
<li>Add MockedStatic.verify extension fun with lambda last [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/549">#549</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/549">mockito/mockito-kotlin#549</a>)</li>
<li>Extending/organizing MatchersTest.kt [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/548">#548</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/548">mockito/mockito-kotlin#548</a>)</li>
<li>Adding value class support to KArgumentCaptor. [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/547">#547</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/547">mockito/mockito-kotlin#547</a>)</li>
<li>Fix the output directory for the dokka Javadoc plugin [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/546">#546</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/546">mockito/mockito-kotlin#546</a>)</li>
<li><a href="https://redirect.github.com/mockito/mockito-kotlin/issues/544">#544</a> Adding value class support to the eq() argument matcher. [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/545">#545</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/545">mockito/mockito-kotlin#545</a>)</li>
<li>eq() argument matcher does not support for value class type arguments [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/544">#544</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/544">mockito/mockito-kotlin#544</a>)</li>
<li>Add note about releasing tags via GitHub UI [(<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/543">#543</a>)](<a href="https://redirect.github.com/mockito/mockito-kotlin/pull/543">mockito/mockito-kotlin#543</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="e08ea94ea5"><code>e08ea94</code></a> Update exception message now that onGeneric is deprecated (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/583">#583</a>)</li>
<li><a href="4d1b21174a"><code>4d1b211</code></a> Improve Result support in matchers and captors (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/582">#582</a>)</li>
<li><a href="d0492b00a5"><code>d0492b0</code></a> Improve support for stubbing Result return values for synchronous and suspend...</li>
<li><a href="2431eb6157"><code>2431eb6</code></a> Fix eq() matcher NPE with nullable value class and null value (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/579">#579</a>)</li>
<li><a href="13bec57b21"><code>13bec57</code></a> Fix regression due to runBlocking being unavailable (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/575">#575</a>)</li>
<li><a href="4199972251"><code>4199972</code></a> Use jvmToolchain API to configure builds (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/572">#572</a>)</li>
<li><a href="3b5498c517"><code>3b5498c</code></a> Improve stubbing support for suspend functions and value classes (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/569">#569</a>)</li>
<li><a href="40edae9ef3"><code>40edae9</code></a> Update PR template to run formatting (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/568">#568</a>)</li>
<li><a href="df0f359b30"><code>df0f359</code></a> Enable Spotless formatting check in CI (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/566">#566</a>)</li>
<li><a href="fa0c69e23d"><code>fa0c69e</code></a> Ignore ktfmt formatting revision in git blame (<a href="https://redirect.github.com/mockito/mockito-kotlin/issues/567">#567</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/mockito/mockito-kotlin/compare/v6.1.0...v6.2.3">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.mockito.kotlin:mockito-kotlin&package-manager=gradle&previous-version=6.1.0&new-version=6.2.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

</details>
2026-02-09 15:08:09 +00:00
..

interactive_media_ads

Flutter plugin for the Interactive Media Ads SDKs.

pub package

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 24+ 13.0+

NOTE:

  • Background Audio ads and Google Dynamic Ad Insertion methods are currently not supported.

IMA client-side overview

Implementing IMA client-side involves five main SDK components, which are demonstrated in this guide:

  • AdDisplayContainer: A container object where ads are rendered.
  • AdsLoader: Requests ads and handles events from ads request responses. You should only instantiate one ads loader, which can be reused throughout the life of the application.
  • AdsRequest: An object that defines an ads request. Ads requests specify the URL for the VAST ad tag, as well as additional parameters, such as ad dimensions.
  • AdsManager: Contains the response to the ads request, controls ad playback, and listens for ad events fired by the SDK.
  • AdsManagerDelegate: Handles ad events and errors that occur during ad or stream initialization and playback.

Usage

This guide demonstrates how to integrate the IMA SDK into a new Widget using the video_player plugin to display content.

1. Update Android App

If not building for Android, skip this step.

Update Android Manifest

Add the user permissions required by the IMA SDK for requesting ads in android/app/src/main/AndroidManifest.xml.

<?code-excerpt "example/android/app/src/main/AndroidManifest.xml (android_manifest)"?>
<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"/>

Update Android App-level Gradle

The IMA SDK requires library desugaring enabled, which you must do by setting coreLibraryDesugaringEnabled true and adding coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' as a dependency in the android/app/build.gradle file. For more details, see Java 11+ APIs available through desugaring with the nio specification.

<?code-excerpt "example/android/app/build.gradle (android_desugaring)"?>
android {
// ···
    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    // ···
}
// ···
dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
    // ···
}

2. Add Imports

Add the import statements for the interactive_media_ads and video_player. Both plugins should already be added to your pubspec.yaml.

<?code-excerpt "example/lib/readme_example.dart (imports)"?>
import 'package:interactive_media_ads/interactive_media_ads.dart';
import 'package:video_player/video_player.dart';

3. Create a New Widget

Create a new StatefulWidget that handles displaying Ads and playing content.

<?code-excerpt "example/lib/readme_example.dart (example_widget)"?>
/// 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) {
    // ···
  }

}

4. Add the Video Players

Instantiate the AdDisplayContainer for playing Ads and the VideoPlayerController for playing content.

<?code-excerpt "example/lib/readme_example.dart (ad_and_content_players)"?>
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(() {});
        });
}

5. Implement the build Method

Return a Widget that contains the ad player and the content player.

<?code-excerpt "example/lib/readme_example.dart (widget_build)"?>
@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. Request Ads

Handle requesting ads and add event listeners to handle when content should be displayed or hidden.

<?code-excerpt "example/lib/readme_example.dart (request_ads)"?>
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. Dispose Resources

Dispose the content player and destroy the AdsManager.

<?code-excerpt "example/lib/readme_example.dart (dispose)"?>
@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.

Contributing

For information on contributing to this plugin, see CONTRIBUTING.md.