Pause and resume Genuin feeds with your app lifecycle

Stop Genuin video when an embed scrolls away, your screen changes, or your app goes to the background, and start it again when the viewer comes back.

Genuin embeds, placements, and full-screen feeds start playing video once they load. Each SDK pauses on its own in some situations. In the others, the video keeps playing, sometimes with sound, until your app calls pause. This page lists what each SDK handles, then the calls for the rest.

What each SDK handles on its own

SituationAndroid 2.1.5iOS 2.1.5Web 2.0.6
The embed scrolls out of viewA grid pauses. A carousel moves to the floating mini player when the embed has one, and otherwise keeps playing.Pauses when the view leaves the nearest scroll view, and resumes when it comes back.Pauses when the container leaves the viewport, and resumes when it comes back. With a floating player, playback moves to it.
Another screen covers yoursEmbed and placement views keep playing. A full-screen feed fragment pauses in its own onPause.Pauses 0.3 seconds after the view leaves the window, and resumes when it returns.Not applicable.
The app or tab goes to the backgroundEmbed and placement views keep playing. A full-screen feed fragment pauses.Pauses when the app resigns active, and resumes when it's active again.Pauses when the tab is hidden or the browser window loses focus, and resumes when it's back.
A phone call or similar audio interruptionPauses on a short loss of audio focus, and resumes when focus returns.Pauses, and resumes when iOS reports that playback can resume.No separate handling; the tab and focus rule above applies.

The floating mini player is the embed's Enable Floating View setting in Brand Control Center. The genuin-react-native package renders the native Android and iOS views, so those two columns apply to React Native. genuin_sdk 1.0.1 for Flutter wraps native SDK 1.1.9.x; its controls are in Step 4.

Step 1: Pause and resume on Android

  1. Tie every GenuinEmbedView and GenuinPlacementView to the screen that hosts it. Without these calls, audio continues after the user leaves the screen or the app:
ProductActivity.kt
kotlin
override fun onResume() {
    super.onResume()
    genuinEmbedView.play()        // resumes autoplay on the focused tile
}

override fun onPause() {
    genuinEmbedView.pause()       // stays paused until the next play()
    super.onPause()
}

override fun onDestroy() {
    genuinEmbedView.release()     // frees the players
    super.onDestroy()
}

In a Fragment, make the same calls from onResume, onPause, and onDestroyView. pause() sticks: the view doesn't restart when it scrolls back into view, and it waits for play(). When the user opens a video full screen, the SDK skips the next pause() so the transition plays smoothly.

  1. If a carousel sits in a scrolling layout and the embed has no floating player, pause it yourself when it leaves the screen:
Pause a carousel that scrolls out of view
kotlin
var carouselVisible = true
scrollView.setOnScrollChangeListener { _, _, _, _, _ ->
    val visible = genuinEmbedView.getLocalVisibleRect(Rect())
    if (visible != carouselVisible) {
        carouselVisible = visible
        if (visible) genuinEmbedView.play() else genuinEmbedView.pause()
    }
}
  1. A FeedEmbedFragment pauses and resumes with its own onPause and onResume. A fragment you hide with FragmentTransaction.hide() stays resumed, so call fragment.pause() when you hide it and fragment.play() when you show it.

The SDK requests audio focus when a video plays with sound. To give focus back to your own player, call GenuinSDK.abandonAudioFocus(). To follow Genuin playback in your own UI, register GenuinSDK.registerPlayerCallback, described in Integrate the Genuin Android SDK.

Step 2: Pause and resume on iOS

GenuinEmbedView and GenuinPlacementView pause and resume on their own when the view leaves the window, when the app moves to the background, and during audio interruptions. Full-screen feeds and standard walls pause when they disappear and when the app moves to the background. You need code in two cases:

  • The view stays in the window behind something else: A sheet, a popover, or your own overlay doesn't remove the embed from the window, so it keeps playing.
  • Your app plays its own media: Genuin doesn't pause for audio your app starts.

pause() and play() act once. After pause(), the SDK still resumes the video when the app becomes active again or the view scrolls back into view. To hold playback until you release it, use pauseByExternalPlayer() and playByExternalPlayer():

ProductViewController.swift
swift
import UIKit
import GenuinCore

final class ProductViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
    var embedView: GenuinEmbedView?   // the view you created with GenuinEmbedView(attributes:)

    func showSizeGuide(_ sheet: UIViewController) {
        embedView?.pauseByExternalPlayer()            // held through app switches and scrolling
        sheet.presentationController?.delegate = self
        present(sheet, animated: true)
    }

    // Runs when the person swipes the sheet away. Call playByExternalPlayer()
    // wherever you dismiss the sheet in code too.
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        embedView?.playByExternalPlayer()
    }
}

playByExternalPlayer() resumes only a view that pauseByExternalPlayer() paused, and only while the view is in a window. If the viewer paused the video themselves, pauseByExternalPlayer() has no effect. To follow Genuin playback in your UI, set GenuinSDK.shared.playbackDelegate, described in Integrate the Genuin iOS SDK.

Step 3: Pause and resume in React Native

On iOS, the native views handle the cases in the first table, and the components ignore their ref. On Android, Feed, StandardWall, and HomeFeed are fragments that pause with your activity. Carousel is a native view with no lifecycle of its own. It keeps playing after navigation and in the background until you call its ref.

useGenuinPlayback.ts
ts
import { useCallback, useEffect, type RefObject } from 'react';
import { AppState, Platform } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import type { PlayPauseRef } from 'genuin-react-native';

export function useGenuinPlayback(ref: RefObject<PlayPauseRef | null>) {
  useFocusEffect(
    useCallback(() => {
      ref.current?.resume();
      return () => ref.current?.pause();      // the screen loses focus or unmounts
    }, [ref])
  );

  useEffect(() => {
    if (Platform.OS !== 'android') return;   // iOS pauses on its own
    const subscription = AppState.addEventListener('change', (state) => {
      if (state === 'active') ref.current?.resume();
      else ref.current?.pause();
    });
    return () => subscription.remove();
  }, [ref]);
}

Call useGenuinPlayback(carouselRef) in the screen that renders the Carousel. On Android, pause() and resume() call the native view's pause() and play(), or the fragment's pause() and play(). When a Carousel unmounts, the package doesn't release its players, so the pause in the focus cleanup matters.

Step 4: Pause and resume in Flutter

genuin_sdk 1.0.1 has pause and resume methods on Android only. On iOS the plugin has no implementation, and each call throws MissingPluginException, so guard every call with Platform.isAndroid. Each method takes the same embedId and uniqueId you gave the widget.

WidgetPauseResume
CarouselpauseCarousel(Map)resumeCarousel(Map)
FeedpauseFeed(Map)resumeFeed(Map)
StandardWallpauseStandWall(Map)resumeStandWall(Map)
HomeFeedpauseHomeFeed()resumeHomeFeed()

Pause when the app goes to the background and when another route covers the screen. The route observer must also be in MaterialApp(navigatorObservers: [routeObserver]):

carousel_section.dart
dart
final routeObserver = RouteObserver<ModalRoute<void>>();

class _CarouselSectionState extends State<CarouselSection> with WidgetsBindingObserver, RouteAware {
  final _genuin = GenuinSdk();
  final Map<String, dynamic> _config = {'embedId': '{{EMBED_ID}}', 'uniqueId': 'home'};

  void _pause() { if (Platform.isAndroid) _genuin.pauseCarousel(_config); }
  void _resume() { if (Platform.isAndroid) _genuin.resumeCarousel(_config); }

  @override
  void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); }

  @override
  void didChangeDependencies() { super.didChangeDependencies(); routeObserver.subscribe(this, ModalRoute.of(context)!); }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) _pause();
    if (state == AppLifecycleState.resumed) _resume();
  }

  @override
  void didPushNext() => _pause();   // another route now covers this one
  @override
  void didPopNext() => _resume();   // this route is visible again

  @override
  void dispose() { routeObserver.unsubscribe(this); WidgetsBinding.instance.removeObserver(this); super.dispose(); }

  @override
  Widget build(BuildContext context) =>
      const SizedBox(height: 300, child: Carousel(embedId: '{{EMBED_ID}}', uniqueId: 'home'));
}

The state belongs to a StatefulWidget named CarouselSection. It imports dart:io, package:flutter/material.dart, package:genuin_sdk/genuin_sdk.dart, and package:genuin_sdk/genuin_carousel_embed_view.dart. Give every widget instance its own uniqueId, because the plugin finds the view by embedId plus uniqueId.

Step 5: Pause and resume on the web

The Web SDK plays a video only while the page has focus and some part of the embed's container is in the viewport. On desktop browsers, focus follows the window's focus and blur events and the tab's visibility. On iOS Safari, it follows visibilitychange, pagehide, and pageshow. The expand view keeps playing while it's open, wherever the container is.

To hold playback yourself, for example while your own video or a modal is open, send the player commands. They apply to every embed on the page and last until you send the opposite command:

Hold Genuin playback while a dialog is open
js
function openDialog(dialog) {
  window.genuin.emit("player:pause");
  dialog.showModal();
  dialog.addEventListener("close", () => window.genuin.emit("player:play"), { once: true });
}

After player:play, a video plays again only when its container is in view and the page has focus. To follow playback in your own UI, subscribe to onPlay and onPause. Their payloads carry isInView and isFocused, which tell you why the state changed. In a single-page app, call window.genuin.destroy() when you leave the section that shows embeds, and init again when you return.

Step 6: Test playback

Run each check with sound on, on a real device:

  1. Scroll the embed off the screen and back. Video stops and restarts as the first table describes, or your scroll listener takes over.
  2. Open another screen from yours, then come back.
  3. Send the app to the background for 10 seconds, then bring it back. Lock the screen as well.
  4. Start a phone call, or ask the voice assistant something, while a video plays with sound.
  5. Open a sheet or dialog over the embed, then close it.
  6. On the web, switch tabs, then click into another window and back.

Limitations

  • Android views ignore the lifecycle: GenuinEmbedView and GenuinPlacementView don't observe the activity. Without pause(), audio continues in the background.
  • Android carousels can play off screen: Without a floating player, a carousel that scrolls away keeps playing until you pause it.
  • iOS pause() doesn't hold: The SDK resumes after an app switch or a scroll back into view. Use pauseByExternalPlayer() to hold.
  • React Native refs work on Android only: On iOS, pause() and resume() have no effect.
  • Flutter controls are Android only: The iOS plugin throws MissingPluginException for every pause and resume method.
  • Web commands are page-wide: player:pause and player:play act on every embed on the page.

See also

TRUST AND COMPLIANCE

© 2026 Genuin Inc.

Genuin Footer