DEV Community

Chanbong Park
Chanbong Park

Posted on

Compose for Desktop - Project UI Structure and Theme Application

In this post, we will explore how to structure the UI in a Compose for Desktop project, apply dark mode themes, and implement navigation functionality.
Additionally, we will introduce reusable UI components (Components) and demonstrate how to utilize them effectively.

Project Structure

For this example, we will organize the project as follows:

πŸ“‚ src/jvmMain/kotlin/

πŸ“ ui/
   β”œβ”€β”€ πŸ“„ HomeScreen.kt         # Home screen
   β”œβ”€β”€ πŸ“„ SettingsScreen.kt     # Settings screen
   β”œβ”€β”€ πŸ“„ Navigation.kt         # Screen navigation management
   β”œβ”€β”€ πŸ“„ Theme.kt              # Light/Dark theme management
   β”œβ”€β”€ πŸ“„ Components.kt         # Reusable UI components
πŸ“„ Main.kt                      # Application entry point
Enter fullscreen mode Exit fullscreen mode

Theme.kt (Dark/Light Theme Application)

To support theme switching, we use a global ThemeController to manage theme state.

package ui

import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue

private val DarkColorPalette = darkColors(
    primary = androidx.compose.ui.graphics.Color(0xFFBB86FC),
    primaryVariant = androidx.compose.ui.graphics.Color(0xFF3700B3),
    secondary = androidx.compose.ui.graphics.Color(0xFF03DAC5)
)

private val LightColorPalette = lightColors(
    primary = androidx.compose.ui.graphics.Color(0xFF6200EE),
    primaryVariant = androidx.compose.ui.graphics.Color(0xFF3700B3),
    secondary = androidx.compose.ui.graphics.Color(0xFF03DAC5)
)

object ThemeController {
    var isDarkMode by mutableStateOf(false)
}

@Composable
fun AppTheme(content: @Composable () -> Unit) {
    val colors = if (ThemeController.isDarkMode) DarkColorPalette else LightColorPalette

    MaterialTheme(
        colors = colors,
        typography = Typography(),
        shapes = Shapes(),
        content = content
    )
}
Enter fullscreen mode Exit fullscreen mode
  • ThemeController.isDarkMode manages the global theme state.
  • AppTheme applies the dark or light theme dynamically based on the theme state.

HomeScreen.kt (Home Screen)

The home screen displays a welcome card using a reusable UI component (SimpleCard).

package ui.screens

import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ui.Screen

@Composable
fun HomeScreen(onNavigate: (Screen) -> Unit) {
    Scaffold(
        topBar = { TopAppBar(title = { Text("Home") }) }
    ) {
        Column(
            modifier = Modifier.fillMaxSize().padding(16.dp),
            verticalArrangement = Arrangement.Center
        ) {
            Text("Welcome to Compose for Desktop!", style = MaterialTheme.typography.h5)
            Spacer(modifier = Modifier.height(20.dp))
            Button(onClick = { onNavigate(Screen.SETTINGS) }) {
                Text("Go to Settings")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Uses SimpleCard for consistent UI elements.
  • Clicking the button triggers onNavigate(Screen.SETTINGS), navigating to the Settings screen.

SettingsScreen.kt (Settings Screen)

The settings screen features a dark mode toggle switch.

package ui.screens

import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ui.Screen
import ui.ThemeController
import ui.components.SimpleCard

@Composable
fun SettingsScreen(onNavigate: (Screen) -> Unit) {
    Scaffold(
        topBar = { TopAppBar(title = { Text("Settings") }) }
    ) {
        Column(
            modifier = Modifier.fillMaxSize().padding(16.dp),
            verticalArrangement = Arrangement.Center
        ) {
            SimpleCard(title = "Settings", content = "Customize your app preferences.")
            Spacer(modifier = Modifier.height(10.dp))

            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Text("Dark Mode")
                Switch(
                    checked = ThemeController.isDarkMode,
                    onCheckedChange = { ThemeController.isDarkMode = it }
                )
            }
            Spacer(modifier = Modifier.height(20.dp))
            Button(onClick = { onNavigate(Screen.HOME) }) {
                Text("Back to Home")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • The Switch button toggles dark mode on/off.
  • Updating ThemeController.isDarkMode instantly changes the theme across the app.

Components.kt (Reusable UI Components)

We define reusable UI elements such as SimpleCard.

package ui.components

import androidx.compose.foundation.layout.*
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun SimpleCard(title: String, content: String) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(8.dp),
        elevation = 4.dp
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = title, style = MaterialTheme.typography.h6)
            Spacer(modifier = Modifier.height(8.dp))
            Text(text = content, style = MaterialTheme.typography.body2)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • SimpleCard provides a consistent UI structure.
  • Used in both HomeScreen and SettingsScreen for uniform styling.

Main.kt (Application Entry Point)

Finally, we wrap everything inside AppTheme and launch NavigationController.

import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import ui.AppTheme
import ui.NavigationController

fun main() = application {
    Window(onCloseRequest = ::exitApplication, title = "Compose Desktop App") {
        AppTheme {
            NavigationController()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸš€ Running the Application

Run the following command to start the application:

gradle run
Enter fullscreen mode Exit fullscreen mode
  • The Home Screen is displayed by default.
  • Clicking β€œGo to Settings” navigates to the Settings screen.
  • Toggling the Dark Mode switch updates the entire app’s theme instantly.

Home Screen

Settings Screen

Settings Screen with Dark Theme Applied

Now, you can expand this template with more features and additional screens as needed! πŸ˜ƒ

Top comments (0)