# How to Create Custom UI Screens in Unciv: A Complete Developer Guide

> Learn to create custom UI screens in Unciv. Extend BaseScreen, use Stage and skin, and push your screen with UncivGame.pushScreen(). A complete developer guide.

- Repository: [Yair Morgenstern/Unciv](https://github.com/yairm210/Unciv)
- Tags: how-to-guide
- Published: 2026-06-18

---

**Extend the abstract `BaseScreen` class, compose widgets on the provided `Stage` using the shared `skin`, and push your screen onto the stack via `UncivGame.pushScreen()` to create fully functional custom interfaces in Unciv.**

Unciv's user interface is built on LibGDX's Scene2D framework, making it straightforward to create custom UI screens by following the established inheritance pattern found throughout the codebase. Whether you are building a new mod configuration panel or a statistics dashboard, understanding how to properly extend `BaseScreen` and integrate with the screen management system is essential for any contributor or modder working with the yairm210/Unciv repository.

## Understanding the BaseScreen Architecture

Every full-screen interface in Unciv inherits from `BaseScreen`, defined in [`core/src/com/unciv/ui/screens/basescreen/BaseScreen.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/screens/basescreen/BaseScreen.kt). This abstract class provides the foundational lifecycle methods—`render()`, `resize()`, `show()`, `hide()`, and `dispose()`—along with a pre-configured `Stage` and a shared `Skin` instance. The `Stage` handles input processing and actor rendering, while the `Skin` ensures visual consistency with the game's default font styles and drawable assets.

Screen navigation is managed by `UncivGame` in [`core/src/com/unciv/UncivGame.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/UncivGame.kt). This singleton maintains a screen stack and exposes `pushScreen()`, `popScreen()`, and `replaceCurrentScreen()` to transition between views. When you create a custom screen, you instantiate it with a reference to the `UncivGame` instance and push it onto this stack to display it.

## Step-by-Step Implementation

### 1. Subclass BaseScreen

Create a new Kotlin file in the appropriate package under `core/src/com/unciv/ui/screens/`. Your class must extend `BaseScreen` and accept the game instance as a constructor parameter.

```kotlin
package com.unciv.ui.screens.custom

import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.UncivGame
import com.unciv.ui.components.extensions.onClick

class MyCustomScreen(val game: UncivGame) : BaseScreen() {

    init {
        // Configure the root table to fill the viewport
        val root = Table()
        root.setFillParent(true)
        stage.addActor(root)

        // Add UI elements using the shared skin
        val title = Label("My Custom Screen", BaseScreen.skin)
        root.add(title).padBottom(20f).row()

        val closeButton = TextButton("Close", BaseScreen.skin)
        closeButton.onClick { game.popScreen() }
        root.add(closeButton)
    }
}

```

### 2. Compose the UI Hierarchy

Use `Table` containers to layout your widgets. The `stage` field provided by `BaseScreen` is where all actors must be added. Always reference `BaseScreen.skin` for constructors of `Label`, `TextButton`, `SelectBox`, and other Scene2D widgets to maintain the game's visual theme. For complex layouts involving side panels or scrollable lists, study [`ModManagementScreen.kt`](https://github.com/yairm210/Unciv/blob/main/ModManagementScreen.kt) in [`core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt), which demonstrates split-pane designs and list handling.

### 3. Register with the Screen Stack

To display your screen, call `pushScreen()` from the `UncivGame` instance. This adds your screen to the top of the stack and pauses the previous screen.

```kotlin
// Example: Opening the custom screen from an existing button
val openButton = TextButton("Open Custom View", BaseScreen.skin)
openButton.onClick {
    game.pushScreen(MyCustomScreen(game))
}

```

To close your screen and return to the previous view, invoke `game.popScreen()`. This triggers the `hide()` lifecycle method on your screen and `show()` on the restored screen.

### 4. Handle Resources and Lifecycle

If your screen loads custom textures, fonts, or other disposable resources, override the `dispose()` method to release them and prevent memory leaks. The base implementation handles disposal of the `Stage`, but you must explicitly dispose any assets loaded outside the shared skin.

```kotlin
override fun dispose() {
    // Dispose custom textures here if applicable
    super.dispose()
}

```

## Key Reference Implementations

Study these existing screens to understand different UI patterns:

- **`WorldScreen`** ([`core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt)): Demonstrates a complex, stateful screen with map rendering, unit controls, and persistent overlays. Reference this for handling input multiplexing and continuous rendering.
- **`ModManagementScreen`** ([`core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt)): Shows how to implement side panels, scrollable lists, and asynchronous loading indicators within the BaseScreen framework.
- **`OptionsPopup`** ([`core/src/com/unciv/ui/popups/options/OptionsPopup.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/popups/options/OptionsPopup.kt)): While technically a popup rather than a screen, this file illustrates how to compose reusable UI components and handle settings persistence, which is applicable to screen-based configurations.
- **`UiElementDocsWriter`** ([`desktop/src/com/unciv/app/desktop/UiElementDocsWriter.kt`](https://github.com/yairm210/Unciv/blob/main/desktop/src/com/unciv/app/desktop/UiElementDocsWriter.kt)): Use this utility to generate documentation for any custom widgets you create, ensuring they remain documented for other modders.

## Best Practices for Custom Screens

- **Reuse the shared skin**: Instantiate all widgets with `BaseScreen.skin` to ensure your UI matches Unciv's theme and supports user-defined mods that override default styles.
- **Separate concerns**: Keep game logic inside `game.gameInfo` or dedicated controller classes. The UI layer should only handle presentation and fire events; avoid heavy calculations on the main thread.
- **Test cross-platform**: UI scaling differs between desktop and Android. Verify that your `Table` layouts adapt correctly by testing on multiple resolutions, and use `BaseScreen.stage.viewport.update(width, height, true)` in `resize()` if you require custom viewport handling.
- **Follow naming conventions**: Place screen classes in `core/src/com/unciv/ui/screens/yourpackage/` and name files ending in [`Screen.kt`](https://github.com/yairm210/Unciv/blob/main/Screen.kt) for consistency with the existing codebase.

## Summary

- Extend `BaseScreen` from [`core/src/com/unciv/ui/screens/basescreen/BaseScreen.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/screens/basescreen/BaseScreen.kt) to inherit stage management and lifecycle handling.
- Add actors to the `stage` field and use `BaseScreen.skin` for consistent widget styling.
- Navigate to your screen using `UncivGame.pushScreen()` and close it with `popScreen()` as implemented in [`core/src/com/unciv/UncivGame.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/UncivGame.kt).
- Reference [`WorldScreen.kt`](https://github.com/yairm210/Unciv/blob/main/WorldScreen.kt) and [`ModManagementScreen.kt`](https://github.com/yairm210/Unciv/blob/main/ModManagementScreen.kt) for patterns on complex layouts and state management.
- Dispose custom resources in `dispose()` to prevent memory leaks.

## Frequently Asked Questions

### What is the difference between a Screen and a Popup in Unciv?

A **Screen** is a full-viewport interface that occupies the entire display and is managed by the screen stack in `UncivGame`. A **Popup** is a modal overlay that appears above the current screen, typically dimming the background and capturing input until dismissed. Screens extend `BaseScreen`, while popups usually extend `Popup` or `BasePopup` and are added directly to a screen's stage.

### How do I pass data between custom screens?

Pass data through constructor parameters when instantiating your screen class. For example, `MyCustomScreen(game, gameInfo, selectedCivilization)` allows you to carry state from the calling context. Avoid static singletons for screen-specific data to prevent memory leaks and threading issues.

### Can I use custom fonts or textures in my UI screen?

Yes. Load custom assets in your screen's `init` block or `show()` method using LibGDX's `AssetManager` or direct texture loading. You must override `dispose()` to call `texture.dispose()` or `font.dispose()` on these custom assets, as `BaseScreen` only manages the default skin and stage resources.

### Where should I add a button to open my custom screen?

Add entry points in existing UI hierarchies such as the main menu, the options menu ([`core/src/com/unciv/ui/popups/options/OptionsPopup.kt`](https://github.com/yairm210/Unciv/blob/main/core/src/com/unciv/ui/popups/options/OptionsPopup.kt)), or the mod manager. Create a `TextButton` with an `onClick` listener that calls `game.pushScreen(MyCustomScreen(game))`. Ensure you import your new screen class at the top of the file where you add the entry point.