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.

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) {
          debugPrint('Loaded ${result.dataCount} clips');
        },
      ),
    );
  }
}

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 expose a back button for nested navigation.
  • theme – apply a StorytellerTheme override.
  • context – attach analytics context metadata.
  • topInset / bottomInset – provide safe area values in logical pixels. Available on Android only.

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 11.6.3 defaults it on and iOS 11.6.1 defaults it off. 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 11.6.3+ 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#

Three callbacks surface native lifecycle events:

  • onDataLoadStarted() – when Storyteller begins loading collection data.
  • onDataLoadComplete(StorytellerEmbeddedClipsLoadResult result) – includes status and counts; inspect result.error when result.success is false.
  • onUserActivityOccurred(Map<String, dynamic> payload) – emits the same payloads you see on the global onUserActivityOccurred stream, scoped to this embedded instance.

Control playback programmatically#

Attach a StorytellerEmbeddedClipsController to pause/resume playback, update retained-view visibility, reload data, or propagate inset changes after creation.

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.
  • goBack() / canGoBack() – traverse nested embedded navigation.
  • setShouldPlay(bool) – toggle playback.
  • setIsVisible(bool) – show or hide the retained native platform view without disposing its player state.
  • updateInsets({int top, int bottom}) – sync safe areas when your layout changes.

Playback and visibility are independent. setShouldPlay(false) keeps an onscreen player rendered in its paused state. For an IndexedStack or another retained tab container, also call setIsVisible(false) when that tab becomes inactive so native view-based ad media receives a real visibility transition. Restore visibility before resuming playback when the tab becomes active again.

Calls made before the platform view is created are ignored (the embedded clips controller does not queue commands). Prefer invoking controller methods after the widget is on screen (for example, after the first frame) or in response to user interaction.