Top-level windows management
Edit pageLast modified: 01 October 2024Compose Multiplatform for desktop provides various features for managing windows. You can hide windows in the tray, make them draggable, adapt size, change position, and so on.
Open and close windows
You can use the Window()
function to create a regular window. To put it in a composable scope, use Window()
in the application
entry point:
application { Window(onCloseRequest = ::exitApplication)
{...}
As a composable function, Window()
allows you to change its properties declaratively. For example, you can open a window with one title and change the title later:
Window(onCloseRequest = ::exitApplication, title =
{...}

Add conditions
You can also open and close windows using simple if
conditions. In the following code sample, the application window is automatically closed after completing a task:
if (isPerformingTask) { Window(onCloseRequest = ::exitApplication,
{...}

If you want to use custom logic on application exit, such as showing a dialog, you can override the close action using the onCloseRequest
callback. In the following code sample, instead of an imperative approach (window.close()
), we use a declarative approach and close the window in response to the state change (isOpen = false
).
Window(onCloseRequest = { isAskingToClose = true }
{...}

Work with multiple windows
If an application has multiple windows, you can create a separate class for the application state and open or close windows in response to the mutableStateListOf
changes:
MyApplicationState { val windows = mutableStateListOf<MyWindowState>()
{...}

For a more complex example, see the notepad sample.
Minimize a window to the system tray
To hide the window instead of closing it, you can change the windowState.isVisible
state:
Window(onCloseRequest = { isVisible = false },
{...}

singleWindowApplication() function
You can create a single window application by calling the singleWindowApplication()
function.
The singleWindowApplication()
function is easier to use but has the following limitations:
The application can have only one window.
You cannot add custom closing logic.
You cannot change the attributes of the window in runtime.
import androidx.compose.ui.window.singleWindowApplication
fun main() = singleWindowApplication {
// Content of the window
}
As an alternative, you can use the Window()
composable in the application
entry point.
Adaptive window size
When you don't know the size of the expected content and cannot specify the optimal window dimensions in advance, you can set one or both dimensions of WindowSize
to Dp.Unspecified
. Compose Multiplatform for desktop will automatically adjust the initial size of your window to fit the content:
state = rememberWindowState(width = Dp.Unspecified, height = Dp.Unspecified)
{...}

Changing the window state
WindowState
is a separate API class for the window placement, current position, and size. The placement attribute allows you to specify how the window is placed on the screen: floating, maximized/minimized, or fullscreen. Any change of the state triggers automatic recomposition. To change the window state, use callbacks or observe it in composables:
val state = rememberWindowState(placement = WindowPlacement.Maximized)
{...}

Listen to the window state
If you need to react to the state changes and send a value to another non-composable application level, for example, write it to the database, you can use the snapshotFlow()
function. This function captures the current value of a composable's state.
LaunchedEffect(state) { snapshotFlow { state.size } .onEach(::onWindowResize)
{...}
Dialogs
You can use the Window()
composable to create a regular window and the DialogWindow()
composable for a modal window that locks its parent until the user closes the modal window.
The following code sample demonstrates how to use these composables to combine regular and modal windows:
if (isDialogOpen) { DialogWindow(onCloseRequest = { isDialogOpen = false },
{...}
Draggable window area
To add a custom draggable title bar to the undecorated window or make the whole window draggable, you can use the WindowDraggableArea()
composable:
WindowDraggableArea { Box(Modifier.fillMaxWidth().height(48.dp).background(Color.DarkGray))}
{...}
WindowDraggableArea()
can be used inside the singleWindowApplication()
, Window()
, and DialogWindow()
composables only. To call it in another composable function, use a WindowScope
as a receiver scope:
private fun WindowScope.AppWindowTitleBar() = WindowDraggableArea {
{...}

Transparent windows and other customizations
To create a transparent window, pass two parameters to the Window()
function: transparent=true
and undecorated=true
. The window must be undecorated because it is impossible to decorate a transparent window.
The following code sample demonstrates how to combine composables to create a transparent window with rounded corners:
Modifier.fillMaxSize().padding(5.dp).shadow(3.dp, RoundedCornerShape(20.dp))
{...}
Swing interoperability
Compose Multiplatform for desktop uses Swing under the hood, so you can create a window using Swing directly:
SwingUtilities.invokeLater { ComposeWindow().apply {
{...}
You can also use the scope of a Window()
composable. In the following code sample, window
is a ComposeWindow
created inside Window()
:
LaunchedEffect(Unit) { window.dropTarget = DropTarget().apply
{...}
If you need to use a dialog implemented in Swing, you can wrap it into a composable function:
@Composable private fun FileDialog( parent: Frame? = null,
{...}
What's next
Explore the tutorials about other desktop components.
Thanks for your feedback!