Skip to content

Embedded Clips#

StorytellerEmbeddedClipsView lets you place the native Storyteller clips experience inline within your Flutter UI while keeping full control of layout, scrolling, and lifecycle.

Initialize successfully before mounting the view. Use a Clips collection containing published content for your API key and give the view bounded width and height.

The placement defaults below are verified for Flutter 12.1.2 (Android 12.2.0 / iOS 11.9.0) and 11.6.5 (Android 11.6.4 / iOS 11.6.1). Current bridge limitations are identified against the 12.1.2 audit baseline; compare Changelog.

Basic usage#

class InlineClips extends StatelessWidget {
  const InlineClips({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 420,
      child: StorytellerEmbeddedClipsView(
        collectionId: 'featured-highlights',
        openReason: StorytellerOpenReason.instanceMethod,
        shouldPlay: true,
        topLevelBack: false,
        onDataLoadStarted: () => debugPrint('Loading clips...'),
        onDataLoadComplete: (result) {
          if (!result.success) {
            debugPrint('Clips failed to load: ${result.error}');
          } else {
            debugPrint('Embedded Clips loaded successfully');
          }
        },
      ),
    );
  }
}

Key parameters:

  • collectionId (required) – the Storyteller collection to play.
  • clipId – start playback at a specific clip.
  • initialCategory – open the embedded UI within a category.
  • openReason – optional analytics attribution using StorytellerOpenReason.instanceMethod or StorytellerOpenReason.deeplink. Omit it to preserve the native Embedded Clips default.
  • adConfiguration – optional per-presentation StorytellerClipsAdConfiguration controlling first-Clip pre-roll, bottom-banner placement, and Android between-Clips provider ordering.
  • shouldPlay – autoplay behaviour (defaults to true).
  • isVisible – native platform-view visibility (defaults to true). Set this to false when retaining the player in an inactive tab.
  • topLevelBack – whether to enable top-level back behavior (defaults to false). Flutter does not expose the native top-level-back tap callback.
  • theme – apply a StorytellerTheme override.
  • context – attach analytics attribution on iOS; the current Android embedded bridge ignores this value.
  • topInset / bottomInset – provide safe area values in logical pixels. Consumed at creation on Android only; iOS ignores these values. Both default to 0.

Configure ad placements for one presentation#

Pass an ad configuration only when this Embedded Clips instance needs to override native placement behavior:

StorytellerEmbeddedClipsView(
  collectionId: 'featured-highlights',
  adConfiguration: const StorytellerClipsAdConfiguration(
    preRollEnabled: true,
    bottomBannerEnabled: true,
    betweenClipsAdProviderOrder: [
      StorytellerAdProvider.gam,
      StorytellerAdProvider.vast,
    ],
  ),
)

Pre-roll is supported on Android and iOS. Omit the nullable value to preserve the native default; Android defaults it on and iOS defaults it off in both compared baselines. Provider ordering remains Android-only.

All three fields are nullable. Only values you explicitly supply are sent to native code; the linked native SDK chooses defaults for missing fields. Provider ordering is supported on Android in both compared Flutter baselines and ignored on iOS. A null order keeps native module order, an empty list disables standard between-Clips providers for this presentation, and a populated list restricts and orders them. Omitting the whole adConfiguration object keeps the native Embedded Clips defaults for the current platform and SDK version.

For a production-ready configuration (including safe-area handling and controller-driven playback), review the Showcase MomentsScreen.

React to embedded callbacks#

Two callbacks are emitted by the current native bridges:

  • onDataLoadStarted() – when Storyteller begins loading collection data.
  • onDataLoadComplete(StorytellerEmbeddedClipsLoadResult result) – inspect result.success and result.error. Android provides a clip count in dataCount; iOS maps success to 1 and failure to 0, so its value is not a clip count.

The Dart constructor also accepts onUserActivityOccurred, but neither current native bridge emits that widget callback. Subscribe to the global analytics stream and use analytics attribution where available. Android's Embedded Clips bridge currently does not forward the widget's context value. These limitations apply to the audited 12.1.2 wrapper. See Changelog for other versions.

Control playback programmatically#

Attach a StorytellerEmbeddedClipsController to pause/resume playback, update retained-view visibility, reload data, or query the native back-navigation state, with the Android limitation below.

class ControllableEmbeddedClips extends StatefulWidget {
  const ControllableEmbeddedClips({super.key});

  @override
  State<ControllableEmbeddedClips> createState() =>
      _ControllableEmbeddedClipsState();
}

class _ControllableEmbeddedClipsState
    extends State<ControllableEmbeddedClips> {
  final _controller = StorytellerEmbeddedClipsController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: StorytellerEmbeddedClipsView(
            collectionId: 'live-stream',
            controller: _controller,
            shouldPlay: false,
          ),
        ),
        FilledButton(
          onPressed: () async {
            await _controller.setShouldPlay(true);
          },
          child: const Text('Play clips'),
        ),
      ],
    );
  }
}

Available controller actions:

  • reloadData() – refreshes the native data source.
  • canGoBack() – iOS reports nested back availability. In Flutter 12.1.2 with Android 12.1.2, this value is inverted relative to goBack(); do not use it to gate Android nested navigation.
  • goBack() – navigate back on Android. The current iOS Flutter bridge accepts this call but performs no action.
  • setShouldPlay(bool) – toggle playback.
  • setIsVisible(bool) – show or hide the retained native platform view without disposing its player state.
  • updateInsets({int top = 0, int bottom = 0}) – exposed by Dart, but not a working safe-area update in the current bridges: iOS performs no action and Android resets native insets to zero. For Android insets, set the widget's logical-pixel topInset and bottomInset at creation; recreate it with a new key when those values change.

Playback and visibility have separate controls. setShouldPlay(false) keeps an onscreen player rendered in its paused state. On Android, hiding a retained player also pauses native playback, even when shouldPlay is true; the player's state is retained. For an IndexedStack or another retained tab container, call setIsVisible(false) when that tab becomes inactive so native view-based ad media also receives a real visibility transition. Restore visibility before resuming playback when the tab becomes active again.

On Android, a retained player hidden with isVisible: false or setIsVisible(false) does not consume first-run Clips Instructions. They remain available to the first visible eligible player, whether full-screen or embedded.

Calls made before the platform view is created are ignored (the embedded clips controller does not queue commands). Invoke controller methods after the view has attached, such as from a native load callback or subsequent user interaction; a first-frame callback alone does not prove attachment.

Widget arguments configure the platform view when created. Rebuilding the same widget does not update native presentation parameters: use controller methods for playback/visibility changes and a new key for collection, destination, theme or ad configuration changes. A first-frame callback is not a readiness guarantee; pre-attachment controller calls are ignored. For loading or lifecycle problems, see Troubleshooting; for provider registration, see Ads.