This is a developers' guide for setting up Storyteller Compose SDK for native Android apps. This guide will cover the basic technical steps for integrating Storyteller Compose SDK into your app.
Note: While this guide focuses on the Jetpack Compose implementation, corresponding traditional Android Views (e.g., StorytellerStoriesRowView, StorytellerStoriesGridView, StorytellerClipsRowView, StorytellerClipsGridView) also exist and can be used in XML layouts or programmatically. Their configuration attributes and delegate usage are similar to their Compose counterparts.
Before you can add the Android SDK to your app, you will need to obtain an API Key. This is a secret key used to authenticate the SDK in your app. Throughout this document it will be marked as [APIKEY].
At first we will need a nesting composable. You can use any composable layout you want: Box, Column, LazyColumn, etc.
@ComposablefunMainScreen(){Box(){// ...}}
Storyteller Composables
Now it's time to add the Storyteller Composables to your app. The Storyteller Composables are the building blocks of the Storyteller SDK.
StorytellerStoriesRow(modifier=Modifier,dataModel=StorytellerStoriesDataModel(categories=emptyList()),// data model with the configuration for your Composables. We will describe how to use StorytellerDataModel below.delegate=listViewDelegate,// delegate for the Composables. We will describe how to use StorytellerListViewDelegate below.state=rememberStorytellerRowState()// state for the Composables. We will describe how to use StorytellerRowState below.)
From Android SDK 11.7.0, Story and Clip Compose lists automatically animate thumbnails when the Storyteller response includes an animated asset. No host-app configuration is required.
Only one eligible visible thumbnail plays at a time across all Story and Clip Compose lists in the foreground app. Rectangular Story tiles and circular Story tiles use separate shape-specific animated assets, while rectangular Clip tiles use the Clip animated asset and circular Clip tiles always retain their static thumbnail. A Story tile does not fall back to the animated asset for a different shape. Playback is silent, and its player resources are released while the owning list is scrolling, when the list or app is no longer visible, or when the system animator duration scale is disabled. Playback resources are recreated automatically when a thumbnail becomes eligible again. The existing static thumbnail remains visible while video loads and is retained when the animated asset for that tile shape is missing or cannot be played.
Using the Storyteller Composables
You can always modify your Composables during recomposition.
Reload Data:
Just like normal composable lists, they have a list state, so as Storyteller lists, they have a state, and we will use it to reload the data. There is an extra parameter state in the Composables, which is set by default. Let's see how it's used:
valstate=rememberStorytellerRowState()// or rememberStorytellerGridState() for StorytellerStoriesGrid or StorytellerClipsGridStorytellerStoriesRow(// ...state=state)// At any point in your code you can call `reloadData` method, maybe after doing PTR or after some event.LaunchedEffect(Unit){state.reloadData()}
Setting categories and collections, applying themes and styles
You can use StorytellerStoriesDataModel object for configuring your Stories Composables. It has the following properties:
StorytellerStoriesDataModel(categories=listOf("category1","category2"),// list of stories categories for the Composable. If you want to use all categories, you can pass an empty list.theme=UiTheme(),// theme for the Composable. A global theme will be set if no value will be passed here.uiStyle=StorytellerListViewStyle.LIGHT,// a style of the Composable. It can be AUTO, LIGHT or DARK. AUTO will represent the theme of the app.displayLimit=6,// a limit of the stories to be displayed in the Composable. Default is Int.MAX_VALUE.cellType=StorytellerListViewCellType.SQUARE,// a cell type of the Composable. It can be SQUARE or ROUND.visibleTiles=null,// a number of visible tiles for rows. Default is null and turned off. If the value is set for greater than 0, the visible tiles will be turned on. The height of the row will be calculated based on the number of visible tiles and if set to eg. 3.5 it will always show three and a half tile independently of the screen size.context=mapOf("placementId"to"home_stories","location"to"Home"),// Optional analytics context containing integrator-defined key-value pairs. See [Analytics](Analytics.md#context) for more details.)
Note: StorytellerStoriesGrid supports only cellType = StorytellerListViewCellType.SQUARE.
You can use StorytellerClipsDataModel object for configuring your Clips Composables. It has the following properties:
StorytellerClipsDataModel(collection="collection1",// a string representing your collection of Clips.theme=UiTheme(),// theme for the Composable. A global theme will be set if no value will be passed here.uiStyle=StorytellerListViewStyle.LIGHT,// a style of the Composable. It can be AUTO, LIGHT or DARK. AUTO will represent the theme of the app.displayLimit=6,// a limit of the clips to be displayed in the Composable. Default is Int.MAX_VALUE.cellType=StorytellerListViewCellType.SQUARE,// a cell type of the Composable. It can be SQUARE or ROUND.visibleTiles=null,// a number of visible tiles for rows. Default is null and turned off. If the value is set for greater than 0, the visible tiles will be turned on. The height of the row will be calculated based on the number of visible tiles and if set to eg. 3.5 it will always show three and a half tile independently of the screen size.adConfiguration=Storyteller.StorytellerClipsAdConfiguration(preRollEnabled=false,betweenClipsAdProviderOrder=listOf(Storyteller.StorytellerAdProvider.VAST,Storyteller.StorytellerAdProvider.GAM,),frequency=4,initialIndex=1,),// Per-presentation Clips ad controls. See Ads.md.context=mapOf("placementId"to"home_clips","location"to"Home"),// Optional analytics context containing integrator-defined key-value pairs. See [Analytics](Analytics.md#context) for more details.)
Note: StorytellerClipsGrid supports only cellType = StorytellerListViewCellType.SQUARE.
adConfiguration is passed to the player opened from this row or grid. A null provider order preserves registered module order; an explicit order is an allowlist; an empty order disables standard between-Clip requests. Nullable frequency and initialIndex values independently inherit remote cadence, while valid supplied values override it only for that player opening. See Per-presentation Clips Ad Controls.
The StorytellerListViewDelegate will provide you necessary callbacks for the state of data inside you Composables.
This delegate is used to track when data load starts, when it completes, when a tile is tapped, and when player was dismissed by user.
We recommend using remember for the delegate's object to avoid creating new ones during recomposition.
vallistViewDelegatebyremember("your_item_id"){valvalue=object:StorytellerListViewDelegate{overridefunonPlayerDismissed(){// handle player dismissed event}overridefunonDataLoadStarted(){// handle loading started event}overridefunonDataLoadComplete(success:Boolean,error:Error?,dataCount:Int){// handle data load complete event}overridefunonTileTapped(tileType:StorytellerTileType){// handle tile tap event - called before the player Activity is opened// tileType can be either StorytellerTileType.Story or StorytellerTileType.Clip}}mutableStateOf(value)}StorytellerStoriesRow(delegate=listViewDelegate,// ...)
The StorytellerTileType is a sealed class that represents the type of tile that was tapped. It contains information about the tapped item and can be one of two types:
The SDK populates these common properties for both Story and Clip callbacks:
id: String - The unique identifier of the Story or Clip
title: String? - The title known at tap time
tileIndex: Int? - The current 1-based position in the displayed row or grid
metadata: Map<String, String>? - A snapshot of the complete custom content metadata map, including entries such as publisher
title is null if refreshed content can no longer be resolved when the tap is handled, and it can be an empty string when the source supplies no title text. tileIndex is null if the item is no longer present in the displayed list. metadata is null when the source omitted metadata or the content can no longer be resolved, and it is empty when the source supplied no entries. The SDK never reuses details from another tile.
The public Story and Clip subtype constructors remain available for application-created values. Because title, position, and metadata are callback context, values created directly by an application—or created with the data class copy method—return null for those three common properties. The generated equals, hashCode, and toString functions continue to use only the legacy subtype constructor properties; they do not include callback context.
modifier - a Modifier object that will be applied to the StorytellerStoriesGrid composable.
delegate - a StorytellerListViewDelegate object that will be used to handle data load events and player dismissed event.
state - a StorytellerGridState object that can be used to reload data and it has a reference to the actual grid state.
the rememberStorytellerGridState() has two parameters:
lazyGridState - a LazyGridState object that will be used to handle the state of the grid.
enablePullToRefresh - a Boolean flag that indicates if the pull to refresh should be enabled. it's off if the grid is not scrollable. and it's off by default if the grid is scrollable.
isScrollable - a Boolean flag that indicates if the grid is scrollable, if true the grid can be put in a non scrollable container. if false the grid can be put in a scrollable container, but a display limit should be set wisely as it wil layout all tiles in the grid.
isEnabled - a Boolean flag that indicates if the tile click should be enabled.
modifier - a Modifier object that will be applied to the StorytellerClipsGrid composable.
delegate - a StorytellerListViewDelegate object that will be used to handle data load events and player dismissed event.
state - a StorytellerGridState object that can be used to reload data and it has a reference to the actual grid state.
the rememberStorytellerGridState() has two parameters:
lazyGridState - a LazyGridState object that will be used to handle the state of the grid.
enablePullToRefresh - a Boolean flag that indicates if the pull to refresh should be enabled. it's off if the grid is not scrollable. and it's off by default if the grid is scrollable.
isScrollable - a Boolean flag that indicates if the grid is scrollable, if true the grid can be put in a non scrollable container. if false the grid can be put in a scrollable container, but a display limit should be set wisely as it will layout all tiles in the grid.
isEnabled - a Boolean flag that indicates if the tile click should be enabled.
searchInput - an optional SearchInput object that can be used to filter clips in the grid.
isVisible - a Boolean flag that indicates if the grid should be visible. Defaults to true.
{"slug": "storyteller-lists", "page_title": "Jetpack Compose Lists", "page_url": "StorytellerLists/", "canonical_url": "/android/StorytellerLists/", "markdown": "# Storyteller Jetpack Compose SDK\n\nThis is a developers' guide for setting up Storyteller Compose SDK for native Android apps. This guide will cover the basic technical steps for integrating Storyteller Compose SDK into your app.\n\n_Note: While this guide focuses on the Jetpack Compose implementation, corresponding traditional Android Views (e.g., `StorytellerStoriesRowView`, `StorytellerStoriesGridView`, `StorytellerClipsRowView`, `StorytellerClipsGridView`) also exist and can be used in XML layouts or programmatically. Their configuration attributes and delegate usage are similar to their Compose counterparts._\n\n## Getting Started\n\n### Resources\n\n- [Storyteller Showcase App (Compose)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/main/src/main/java/com/getstoryteller/storytellershowcaseapp/ShowcaseApp.kt#L24)\n\n### Showcase examples\n\n- [Lists + Cards feed (`HomeScreen`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/app/src/main/java/com/getstoryteller/storytellershowcaseapp/ui/features/home/HomeScreen.kt#L164)\n- [Storyteller list rendering (`StorytellerItem`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/app/src/main/java/com/getstoryteller/storytellershowcaseapp/ui/features/storyteller/StorytellerItem.kt#L58)\n\n### How to Add the SDK to Your Project\n\nBefore you can add the Android SDK to your app, you will need to obtain an API Key. This is a secret key used to authenticate the SDK in your app. Throughout this document it will be marked as `[APIKEY]`.\n\n#### SDK Installation\n\nThe Compose SDK can be included in your project using [Gradle](https://gradle.org/). It is recommended to use [Android Studio](https://developer.android.com/studio). If you are having problems with configuring your build, check out the [Android Studio guide](https://developer.android.com/studio/build) or [Gradle guides](https://gradle.org/guides/).\n\n> Note: Starting from 9.3.0 SDK version Compose is now part of the SDK and there is no need to add separate Compose dependency.\n\nThe steps to add the SDK are [in the Getting Started documentation](GettingStarted.md)\n\n#### Usage\n\n1. At first we will need a nesting composable. You can use any composable layout you want: `Box`, `Column`, `LazyColumn`, etc.\n\n```kotlin\n @Composable\n fun MainScreen() {\n Box() {\n // ...\n }\n }\n```\n\n1. Storyteller Composables\n Now it's time to add the Storyteller Composables to your app. The Storyteller Composables are the building blocks of the Storyteller SDK.\n\n```kotlin\n\n StorytellerStoriesRow(\n modifier = Modifier,\n dataModel = StorytellerStoriesDataModel(categories = emptyList()), // data model with the configuration for your Composables. We will describe how to use StorytellerDataModel below.\n delegate = listViewDelegate, // delegate for the Composables. We will describe how to use StorytellerListViewDelegate below.\n state = rememberStorytellerRowState() // state for the Composables. We will describe how to use StorytellerRowState below.\n )\n```\n\nWe have the following **composable types** available:\n\n- StorytellerStoriesRow\n- StorytellerStoriesGrid\n- StorytellerClipsRow\n- StorytellerClipsGrid\n\n### Animated thumbnails\n\nFrom Android SDK 11.7.0, Story and Clip Compose lists automatically animate thumbnails when the Storyteller response includes an animated asset. No host-app configuration is required.\n\nOnly one eligible visible thumbnail plays at a time across all Story and Clip Compose lists in the foreground app. Rectangular Story tiles and circular Story tiles use separate shape-specific animated assets, while rectangular Clip tiles use the Clip animated asset and circular Clip tiles always retain their static thumbnail. A Story tile does not fall back to the animated asset for a different shape. Playback is silent, and its player resources are released while the owning list is scrolling, when the list or app is no longer visible, or when the system animator duration scale is disabled. Playback resources are recreated automatically when a thumbnail becomes eligible again. The existing static thumbnail remains visible while video loads and is retained when the animated asset for that tile shape is missing or cannot be played.\n\n1. Using the Storyteller Composables\n\n You can always modify your Composables during recomposition.\n\n Reload Data:\n Just like normal composable lists, they have a list state, so as Storyteller lists, they have a state, and we will use it to reload the data. There is an extra parameter `state` in the Composables, which is set by default. Let's see how it's used:\n\n```kotlin\n val state = rememberStorytellerRowState() // or rememberStorytellerGridState() for StorytellerStoriesGrid or StorytellerClipsGrid\n StorytellerStoriesRow(\n // ...\n state = state\n )\n\n // At any point in your code you can call `reloadData` method, maybe after doing PTR or after some event.\n LaunchedEffect(Unit) {\n state.reloadData()\n }\n```\n\n1. Setting categories and collections, applying themes and styles\n\n You can use `StorytellerStoriesDataModel` object for configuring your Stories Composables. It has the following properties:\n\n```kotlin\n StorytellerStoriesDataModel(\n categories = listOf(\"category1\", \"category2\"), // list of stories categories for the Composable. If you want to use all categories, you can pass an empty list.\n theme = UiTheme(), // theme for the Composable. A global theme will be set if no value will be passed here.\n uiStyle = StorytellerListViewStyle.LIGHT, // a style of the Composable. It can be AUTO, LIGHT or DARK. AUTO will represent the theme of the app.\n displayLimit = 6, // a limit of the stories to be displayed in the Composable. Default is Int.MAX_VALUE.\n cellType = StorytellerListViewCellType.SQUARE, // a cell type of the Composable. It can be SQUARE or ROUND.\n visibleTiles = null, // a number of visible tiles for rows. Default is null and turned off. If the value is set for greater than 0, the visible tiles will be turned on. The height of the row will be calculated based on the number of visible tiles and if set to eg. 3.5 it will always show three and a half tile independently of the screen size.\n context = mapOf(\"placementId\" to \"home_stories\", \"location\" to \"Home\"), // Optional analytics context containing integrator-defined key-value pairs. See [Analytics](Analytics.md#context) for more details.\n )\n```\n\n> Note: `StorytellerStoriesGrid` supports only `cellType = StorytellerListViewCellType.SQUARE`.\n\nYou can use `StorytellerClipsDataModel` object for configuring your Clips Composables. It has the following properties:\n\n```kotlin\n StorytellerClipsDataModel(\n collection = \"collection1\", // a string representing your collection of Clips.\n theme = UiTheme(), // theme for the Composable. A global theme will be set if no value will be passed here.\n uiStyle = StorytellerListViewStyle.LIGHT, // a style of the Composable. It can be AUTO, LIGHT or DARK. AUTO will represent the theme of the app.\n displayLimit = 6, // a limit of the clips to be displayed in the Composable. Default is Int.MAX_VALUE.\n cellType = StorytellerListViewCellType.SQUARE, // a cell type of the Composable. It can be SQUARE or ROUND.\n visibleTiles = null, // a number of visible tiles for rows. Default is null and turned off. If the value is set for greater than 0, the visible tiles will be turned on. The height of the row will be calculated based on the number of visible tiles and if set to eg. 3.5 it will always show three and a half tile independently of the screen size.\n adConfiguration = Storyteller.StorytellerClipsAdConfiguration(\n preRollEnabled = false,\n betweenClipsAdProviderOrder = listOf(\n Storyteller.StorytellerAdProvider.VAST,\n Storyteller.StorytellerAdProvider.GAM,\n ),\n frequency = 4,\n initialIndex = 1,\n ), // Per-presentation Clips ad controls. See Ads.md.\n context = mapOf(\"placementId\" to \"home_clips\", \"location\" to \"Home\"), // Optional analytics context containing integrator-defined key-value pairs. See [Analytics](Analytics.md#context) for more details.\n )\n```\n\n> Note: `StorytellerClipsGrid` supports only `cellType = StorytellerListViewCellType.SQUARE`.\n\n`adConfiguration` is passed to the player opened from this row or grid. A null provider order preserves registered module order; an explicit order is an allowlist; an empty order disables standard between-Clip requests. Nullable `frequency` and `initialIndex` values independently inherit remote cadence, while valid supplied values override it only for that player opening. See [Per-presentation Clips Ad Controls](Ads.md#per-presentation-clips-ad-controls).\n\n## Storyteller Compose ListView Delegate\n\nThe `StorytellerListViewDelegate` will provide you necessary callbacks for the state of data inside you Composables.\n\nThis delegate is used to track when data load starts, when it completes, when a tile is tapped, and when player was dismissed by user.\n\nWe recommend using `remember` for the delegate's object to avoid creating new ones during recomposition.\n\n```kotlin\nval listViewDelegate by remember(\"your_item_id\") {\n val value = object : StorytellerListViewDelegate {\n override fun onPlayerDismissed() {\n // handle player dismissed event\n }\n\n override fun onDataLoadStarted() {\n // handle loading started event\n }\n\n override fun onDataLoadComplete(success: Boolean, error: Error?, dataCount: Int) {\n // handle data load complete event\n }\n\n override fun onTileTapped(tileType: StorytellerTileType) {\n // handle tile tap event - called before the player Activity is opened\n // tileType can be either StorytellerTileType.Story or StorytellerTileType.Clip\n }\n }\n mutableStateOf(\n value\n )\n}\n\nStorytellerStoriesRow(\n delegate = listViewDelegate,\n // ...\n)\n```\n\n### StorytellerTileType\n\nThe `StorytellerTileType` is a sealed class that represents the type of tile that was tapped. It contains information about the tapped item and can be one of two types:\n\nThe SDK populates these common properties for both Story and Clip callbacks:\n\n- `id: String` - The unique identifier of the Story or Clip\n- `title: String?` - The title known at tap time\n- `tileIndex: Int?` - The current 1-based position in the displayed row or grid\n- `metadata: Map<String, String>?` - A snapshot of the complete custom content metadata map, including entries such as `publisher`\n\n`title` is `null` if refreshed content can no longer be resolved when the tap is handled, and it can be an empty string when the source supplies no title text. `tileIndex` is `null` if the item is no longer present in the displayed list. `metadata` is `null` when the source omitted metadata or the content can no longer be resolved, and it is empty when the source supplied no entries. The SDK never reuses details from another tile.\n\nThe public Story and Clip subtype constructors remain available for application-created values. Because title, position, and metadata are callback context, values created directly by an application\u2014or created with the data class `copy` method\u2014return `null` for those three common properties. The generated `equals`, `hashCode`, and `toString` functions continue to use only the legacy subtype constructor properties; they do not include callback context.\n\n#### StorytellerTileType.Story\n\nRepresents a Story tile that was tapped.\n\n**Properties:**\n\n- `categories: List<StorytellerCategory>` - List of categories that this story belongs to\n\n#### StorytellerTileType.Clip\n\nRepresents a Clip tile that was tapped.\n\n**Properties:**\n\n- `collectionId: String` - The collection ID that this clip belongs to\n- `categories: List<StorytellerCategory>` - List of categories that this clip belongs to\n\n**Example usage:**\n\n```kotlin\noverride fun onTileTapped(tileType: StorytellerTileType) {\n val title = tileType.title\n val oneBasedIndex = tileType.tileIndex\n val publisher = tileType.metadata?.get(\"publisher\")\n\n when (tileType) {\n is StorytellerTileType.Story -> {\n val storyId = tileType.id\n val categories = tileType.categories\n // Handle story tile tap\n Log.d(\"TileTap\", \"Story tapped: $storyId at $oneBasedIndex, title: $title, publisher: $publisher, categories: $categories\")\n }\n is StorytellerTileType.Clip -> {\n val clipId = tileType.id\n val collectionId = tileType.collectionId\n val categories = tileType.categories\n // Handle clip tile tap\n Log.d(\"TileTap\", \"Clip tapped: $clipId at $oneBasedIndex, title: $title, publisher: $publisher, collection: $collectionId, categories: $categories\")\n }\n }\n}\n```\n\n## StorytellerStoriesGrid\n\nThe `StorytellerStoriesGrid` is a Composable with same functionality as the `StorytellerStoriesGridView` view from the Storyteller SDK.\n\n### Usage in Compose\n\n```kotlin\n//Stories Grid\nStorytellerStoriesGrid(\n modifier = modifier,\n dataModel = StorytellerStoriesDataModel(categories = emptyList()),\n delegate = listViewDelegate,\n state = rememberStorytellerGridState(),\n isEnabled = true\n)\n\n//Stories Scrollable Grid\nStorytellerStoriesGrid(\n modifier = modifier,\n dataModel = StorytellerStoriesDataModel(categories = emptyList()),\n delegate = listViewDelegate,\n state = rememberStorytellerGridState(),\n isScrollable = true,\n isEnabled = true\n)\n```\n\n### Parameters\n\n- `modifier` - a `Modifier` object that will be applied to the `StorytellerStoriesGrid` composable.\n- `delegate` - a `StorytellerListViewDelegate` object that will be used to handle data load events and player dismissed event.\n- `state` - a `StorytellerGridState` object that can be used to reload data and it has a reference to the actual grid state.\n + the `rememberStorytellerGridState()` has two parameters:\n - `lazyGridState` - a `LazyGridState` object that will be used to handle the state of the grid.\n - `enablePullToRefresh` - a `Boolean` flag that indicates if the pull to refresh should be enabled. it's off if the grid is not scrollable. and it's off by default if the grid is scrollable.\n- `isScrollable` - a `Boolean` flag that indicates if the grid is scrollable, if `true` the grid can be put in a non scrollable container. if `false` the grid can be put in a scrollable container, but a display limit should be set wisely as it wil layout all tiles in the grid.\n- `isEnabled` - a `Boolean` flag that indicates if the tile click should be enabled.\n\n## StorytellerStoriesRow\n\nThe `StorytellerStoriesRow` is a Composable with same functionality as the `StorytellerStoriesRowView` view from the Storyteller SDK.\n\n### Usage in Compose\n\n```kotlin\nStorytellerStoriesRow(\n modifier = modifier,\n dataModel = StorytellerStoriesDataModel(categories = emptyList()),\n delegate = listViewDelegate,\n state = rememberStorytellerRowState()\n)\n```\n\n## StorytellerClipsGrid\n\nThe `StorytellerClipsGridView` is a Composable with same functionality as the `StorytellerClipsGridView` view from the Storyteller SDK.\n\n### Usage in Compose\n\n```kotlin\n//Clips Grid\nStorytellerClipsGrid(\n modifier = modifier,\n dataModel = StorytellerClipsDataModel(collection = \"collection\"),\n delegate = listViewDelegate,\n state = rememberStorytellerGridState(),\n isScrollable = false,\n isEnabled = true\n)\n\n//Clips Scrollable Grid\nStorytellerClipsGrid(\n modifier = modifier,\n dataModel = StorytellerClipsDataModel(collection = \"collection\"),\n delegate = listViewDelegate,\n state = rememberStorytellerGridState(),\n isScrollable = true,\n isEnabled = true\n)\n```\n\n### Parameters\n\n- `modifier` - a `Modifier` object that will be applied to the `StorytellerClipsGrid` composable.\n- `delegate` - a `StorytellerListViewDelegate` object that will be used to handle data load events and player dismissed event.\n- `state` - a `StorytellerGridState` object that can be used to reload data and it has a reference to the actual grid state.\n + the `rememberStorytellerGridState()` has two parameters:\n - `lazyGridState` - a `LazyGridState` object that will be used to handle the state of the grid.\n - `enablePullToRefresh` - a `Boolean` flag that indicates if the pull to refresh should be enabled. it's off if the grid is not scrollable. and it's off by default if the grid is scrollable.\n- `isScrollable` - a `Boolean` flag that indicates if the grid is scrollable, if `true` the grid can be put in a non scrollable container. if `false` the grid can be put in a scrollable container, but a display limit should be set wisely as it will layout all tiles in the grid.\n- `isEnabled` - a `Boolean` flag that indicates if the tile click should be enabled.\n- `searchInput` - an optional `SearchInput` object that can be used to filter clips in the grid.\n- `isVisible` - a `Boolean` flag that indicates if the grid should be visible. Defaults to true.\n\n## StorytellerClipsRow\n\nThe `StorytellerClipsRow` is a Composable with same functionality as the `StorytellerClipsRowView` view from the Storyteller SDK.\n\n### Usage in Compose\n\n```kotlin\nStorytellerClipsRow(\n modifier = modifier,\n dataModel = StorytellerClipsDataModel(collection = \"collection\"),\n delegate = listViewDelegate,\n state = rememberStorytellerRowState()\n)\n```\n", "copy_markdown_include_header": false, "base_path": "android", "ai_dir": "ai", "missing_payload_behavior": "empty"}