This writing is continuation from Building Portfolio Website using compose multiplatform(2). Please refer to Part 1 & Part 2 for setup & creating the apps menu. On this part, we will focus on displaying text on the content area, whenever user click or hover on the text menu.

First, we will provide the content for About, Experience and Projects in stringResources. In compose, all static asset such as Strings, Fonts and Images are keep in one Resources folder.

Ensure that the view panel (on the left side Android Studio) showing Project mode. Find composeResources folder inside commonMain. Create another folder values and new file strings.xml.
Lets put all the data on this file. I am copying directly from Linkedin for About, Experience and Projects content. The file is using XML marking language. To put data create <resources></resources> tag, each string data are to be put in between <string></string> tag.
<resources>
<string name="about_us">
Hello my name is Andrea. I am passionate to create beautiful and practical application. I had 4 years of experience with Native Android App Development Kotlin/Java. One of my app was an Android part of reporting system, which is now widely used for one of the Police Force Division at one of the country.
\nSince 2024, I also working on Kotlin Multiplatform, and still eagerly looking for more Kotlin Multiplatform projects.
\nIn my spare time, I love to spend time with my family and baking delicious bread.</string>
<string name="experience">
Freelance Android Developer
Nov 2019 - Present
Working in various project across the world as Junior Android Developer (part of Remote Team) or Main Developer (sole Developer).
...
</string>
<string name="projects">
KMP Compose Weather
Jun 2024 - Jul 2024
This is a Weather App build with Kotlin Multiplatform project targeting Android, iOS, Desktop.
Tech stack :
🧩 Compose Multiplatform; for shared UI
🌐 Ktor; for networking
🗃️ KStore; for storage
📦 Kotlinx Serialization; for content negotiation
🚏 Decompose + Router; for navigation
🧪 Molecule; for modeling state
🔖 KMLogging; for logging
...
</string>
</resources>
Great. Now we will create space to display the content. We will create container with Text wrapped in Box. Create this function inside App.kt
@Composable
fun ContentBox(modifier: Modifier, content: String){
val scrollState = rememberScrollState()
Box(modifier.padding(end = 24.dp)
.sizeIn(maxWidth = 200.dp)
.verticalScroll(scrollState)){
Text(
text = content,
modifier = Modifier.padding(),
style = MaterialTheme.typography.body1.copy(color = silverTextTitle),
textAlign = TextAlign.Start)
}
}
I’d like the content to be displayed in Row if the screen is in Landscape orientation. Vice versa it will be displayed in Column if the screen is in Portrait orientation.
To determine what is the screen orientation, we will put this variabel inside App()
val screenHeight = LocalWindowInfo.current.containerSize.height
val screenWidth = LocalWindowInfo.current.containerSize.width
val isPortrait = screenWidth < screenHeight
Thus, we can arrange the layout based on the orientation input
MaterialTheme {
if(isPortrait){
Column(Modifier.fillMaxSize()
.background(navyBluePrimary)
.padding(24.dp)) {
MenuColumn(Modifier.weight(1f))
ContentBox(Modifier.weight(1f), "")
}
} else {
Row (Modifier.fillMaxSize()
.background(navyBluePrimary)
.padding(24.dp)) {
MenuColumn(Modifier.weight(4f))
ContentBox(Modifier.weight(5f), "")
}
}
}
If we run this code, the layout looks good, but nothing displayed on the content box yet. We will need to create function so that it will give feedback which menu on clicked.

Create this variabel inside App(). It will give input which menu are on clicked.
val currentContent = remember { mutableStateOf(0) }
On the MenuColumn() we will add callback whenever user change menu selection
@Composable
fun MenuColumn(modifier: Modifier, currentContent:(Int)-> Unit){
Column(modifier.sizeIn(maxWidth = 200.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)) {
DescriptionText()
MenuTextButton(currentContent)
IconButtons({})
}
}
@Composable
fun MenuTextButton(callback:(Int) -> Unit) {
val focusTextStyle = MaterialTheme.typography.subtitle1.copy(silverTextTitle)
val unfocusTextStyle = MaterialTheme.typography.body1.copy(color = grayText)
val textFocus = remember { mutableStateOf(0) }
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
Text("- About",
style = if(textFocus.value == 0 ) focusTextStyle else unfocusTextStyle,
modifier = Modifier.clickable { textFocus.value = 0
callback(0)}
.hoverableExt { textFocus.value = 0
callback(0)})
Text(
"- Experience",
style = if(textFocus.value == 1 ) focusTextStyle else unfocusTextStyle,
modifier = Modifier.clickable { textFocus.value = 1
callback(1)}
.hoverableExt { textFocus.value = 1
callback(1)}
)
Text(
"- Projects",
style = if(textFocus.value == 2 ) focusTextStyle else unfocusTextStyle,
modifier = Modifier.clickable { textFocus.value = 2
callback(2)}
.hoverableExt { textFocus.value = 2
callback(2)}
)
}
}
Finally we will update currentContent variable from the callback function.
fun App() {
val screenHeight = LocalWindowInfo.current.containerSize.height
val screenWidth = LocalWindowInfo.current.containerSize.width
val isPortrait = screenWidth < screenHeight
val currentContent = remember { mutableStateOf(0) }
MaterialTheme {
if(isPortrait){
Column(Modifier.fillMaxSize()
.background(navyBluePrimary)
.padding(24.dp)) {
MenuColumn(Modifier.weight(1f), {currentContent.value = it})
ContentBox(Modifier.weight(1f), "")
}
} else {
Row (Modifier.fillMaxSize()
.background(navyBluePrimary)
.padding(24.dp)) {
MenuColumn(Modifier.weight(4f), {currentContent.value = it})
ContentBox(Modifier.weight(5f), "")
}
}
}
}
On the final step, we will called appropriate String data based on the currentContent input.
fun App() {
val screenHeight = LocalWindowInfo.current.containerSize.height
val screenWidth = LocalWindowInfo.current.containerSize.width
val isPortrait = screenWidth < screenHeight
val currentContent = remember { mutableStateOf(0) }
MaterialTheme {
if(isPortrait){
Column(Modifier.fillMaxSize()
.background(navyBluePrimary)
.padding(24.dp)) {
MenuColumn(Modifier.weight(1f), {currentContent.value = it})
ContentBox(Modifier.weight(1f), getContent(currentContent.value))
}
} else {
Row (Modifier.fillMaxSize()
.background(navyBluePrimary)
.padding(24.dp)) {
MenuColumn(Modifier.weight(4f), {currentContent.value = it})
ContentBox(Modifier.weight(5f), getContent(currentContent.value))
}
}
}
}
@Composable
fun getContent(code:Int): String {
return when(code)
{ 0 -> stringResource(Res.string.about_us)
1 -> stringResource(Res.string.experience)
2 -> stringResource(Res.string.projects)
else -> ""
}
}
Hit the Run button, the screen will show :

Complete code for this part is here. For the next part, we will take care the icon buttons on the bottom part of the Menu area.