Skip to content

Content Modeling in Android

Learn how to handle different content-type and nestable blocks, render rich text, and use story references to manage content globally.

In the existing space, create the following blocks:

  • An article content type block with the following fields:
    • title: Text
    • content: Rich text
  • A featured-articles nestable block with the following field:
    • articles: References

Next, create an Articles folder, open it, and create a few stories that use the article content type.

Finally, add the featured-articles block to the body field of the Home story, and select the created articles to feature.

Update the BlockLibrary.kt file to add Component subclasses for the new blocks.

app/src/main/java/com/example/myapplication/BlockLibrary.kt
package com.example.myapplication
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import com.storyblok.cdn.schema.Component
import com.storyblok.cdn.schema.RichText
import com.storyblok.cdn.schema.Story
@Serializable
@SerialName("teaser")
data class Teaser(val headline: String): Component()
@Serializable
@SerialName("article")
data class Article(val title: String, val content: RichText) : Component()
@Serializable
@SerialName("featured-articles")
data class FeaturedArticles(val articles: List<Story<Article>>) : Component()

The fields of the article and featured-articles blocks are defined as follows:

  • Article.content is defined as RichText which can represent any rich text node (a document in this case).
  • FeaturedArticles.articles is defined as List<Story<Article>> which will cause the referenced stories to be resolved and returned. To receive only the UUIDs of the linked stories change the type of the field to List<Uuid>.

Next, add composables for these blocks inside the blockProvider in MainActivity.kt.

app/src/main/java/com/example/myapplication/MainActivity.kt
import androidx.navigation3.ui.NavDisplay
import androidx.navigation3.runtime.entryProvider
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.TextButton
blockProvider = blockProvider(
fallback = { it, _ -> Text("Unknown block type '${it.component}'") }
storyLinkListener = { uuid, _ -> backStack.add(StoryKey(uuid = uuid)) }
) {
block<Page> { page, modifier ->
LazyColumn(modifier) {
items(page.body, key = { it.uid }) { Block(it, Modifier.fillMaxWidth()) }
}
}
block<Feature> { feature, modifier ->
Text(feature.name, modifier.padding(16.dp))
}
block<Teaser> { teaser, modifier ->
Text(teaser.headline, modifier.padding(16.dp), style = typography.headlineMedium)
}
block<Grid> { grid, modifier ->
FlowRow(modifier) { grid.columns.forEach { Block(it) } }
}
block<FeaturedArticles> { featured, modifier ->
Column(modifier.padding(16.dp)) {
Text("Featured Articles", style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.height(8.dp))
featured.articles.forEach { article ->
TextButton(onClick = { backStack.add(StoryKey(story = article)) }) {
Text(article.content.title)
}
}
}
}
block<Article> { article, modifier ->
Column(modifier.padding(16.dp)) {
Text(article.title, style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(16.dp))
RichText(article.content, Modifier.fillMaxWidth())
}
}
}

Composables for FeaturedArticles and Article are registered alongside the existing blocks:

  • The block<FeaturedArticles> composable renders links to the featured articles, which on click construct a StoryKey with the article Story instance for immediate display of the selected article.
  • The block<Article> composable invokes RichText() to render the article’s rich text content.

Also storyLinkListener is set in the blockProvider to handle any story links present in the rich text content.

Run the app and see the home story with the links to the featured articles being rendered. Tapping any link navigates to the article.

Was this page helpful?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.