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
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
)
}
- 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")
}
}
}
}
- 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")
}
}
}
}
- 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)
}
}
}
- 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()
}
}
}
π Running the Application
Run the following command to start the application:
gradle run
- 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.
Now, you can expand this template with more features and additional screens as needed! π
Top comments (0)