# How the GUI Is Structured Using the Iced Framework in UAD-ng

> Discover how UAD-ng structures its GUI with the Iced framework. Explore its declarative, message-driven approach and centralized update loop for seamless Android device operations.

- Repository: [Universal-Debloater-Alliance/universal-android-debloater-next-generation](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation)
- Tags: architecture
- Published: 2026-06-18

---

**UAD-ng implements a declarative, message-driven GUI by defining a single `UadGui` struct that implements Iced’s `Application` trait, coordinating stateful views and asynchronous Android device operations through a centralized update loop.**

Universal Android Debloater Next Generation (UAD-ng) delivers its cross-platform interface through the **Iced** GUI library, employing an Elm-inspired architecture that separates state management from view rendering. The application centers on a central `UadGui` struct that manages all UI transitions and background tasks. Understanding how the GUI is structured using the Iced framework in UAD-ng reveals a modular Rust codebase where navigation, device handling, and async ADB operations remain cleanly decoupled.

## Application Core: The `UadGui` Struct

The heart of the interface resides in [`src/gui.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/gui.rs), where the **`UadGui`** struct implements the **`Application`** trait required by Iced. This struct stores all global UI state and implements the five mandatory lifecycle methods: `new`, `update`, `view`, `subscription`, and `theme`.

The `UadGui` struct contains:

- **`view`** – An enum (`View::List`, `View::About`, or `View::Settings`) that determines which screen is currently active.
- **Sub-views** – Instantiated structs for `apps_view` (package list), `about_view` (version information), and `settings_view` (configuration options).
- **Device state** – `devices_list` and `selected_device` for managing Android device connections.
- **Update tracking** – An `UpdateState` struct monitoring self-update status and UAD-list synchronization.

The entry point in [`src/main.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/main.rs) bootstraps the application by calling `UadGui::start()`, which hands control to Iced’s event loop:

```rust
fn main() -> iced::Result {
    setup_logger().expect("setup logging");
    UadGui::start()
}

```

## Message-Driven Update Architecture

All user interactions and background events in UAD-ng flow through a centralized **`Message`** enum defined in [`src/gui.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/gui.rs). This architecture ensures that every button click, device detection, or async callback translates into a discrete message handled by the `UadGui::update` method.

Common message variants include:

- **Navigation actions** – `AppsPress`, `AboutPressed`, and `SettingsPressed` switch between views.
- **Device handling** – `DeviceSelected`, `LoadDevices`, and `ADBStateUpdate` manage ADB connections.
- **Async completions** – `FontLoaded`, `GetLatestRelease`, and package refresh callbacks update the UI after background work finishes.

When `update` receives a message, it either mutates internal state or spawns new asynchronous tasks using **`Task::perform`**. For example, navigating to the About screen triggers a fresh update check:

```rust
Message::AboutPressed => {
    self.view = View::About;
    self.update_state.self_update = SelfUpdateState::default();
    Task::perform(
        async move { get_latest_release() },
        Message::GetLatestRelease,
    )
}

```

## Composable Views and Navigation

The UI is assembled from modular view components located in `src/views/` and reusable widgets. Rather than monolithic rendering logic, each view module implements its own `view` and `update` methods, which `UadGui` orchestrates based on the current `View` state.

**Key view modules:**

- **[`src/views/list.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/views/list.rs)** – Renders the package list, loading states, and debloating actions for the selected device.
- **[`src/views/about.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/views/about.rs)** – Displays version information, update status, and project links.
- **[`src/views/settings.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/views/settings.rs)** – Provides per-device configuration options and multi-user toggles.

The top navigation bar lives in [`src/widgets/navigation_menu.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/widgets/navigation_menu.rs). The `nav_menu` function builds a dynamic horizontal row containing the device picker, refresh/reboot buttons, and view-switching shortcuts. Inside `UadGui::view`, the navigation container combines with the active sub-view to form the final layout:

```rust
fn view(&self) -> Element<'_, Message, Theme> {
    let navigation_container = nav_menu(
        &self.devices_list,
        self.selected_device.clone(),
        &self.apps_view,
        &self.update_state.self_update,
    );

    let selected_device = self.selected_device.clone().unwrap_or_default();
    let main_container = match self.view {
        View::List => self.apps_view.view(&self.settings_view, &selected_device)
                                 .map(Message::AppsAction),
        View::About => self.about_view.view(&self.update_state).map(Message::AboutAction),
        View::Settings => self.settings_view.view(&selected_device, &self.apps_view)
                                            .map(Message::SettingsAction),
    };

    column![navigation_container, main_container]
        .width(Length::Fill)
        .align_x(Alignment::Center)
        .into()
}

```

## Initialization and Async Lifecycle

When Iced instantiates the application, `UadGui::new` returns both the initial state and a batch of asynchronous tasks. This pattern ensures the GUI renders immediately while background initialization proceeds concurrently.

The startup sequence in [`src/gui.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/gui.rs) uses **`Task::batch`** to parallelize:

1. **Font loading** – Embeds and loads `icons.ttf` for custom interface icons.
2. **ADB initialization** – Validates the Android Debug Bridge connection.
3. **Device discovery** – Scans for connected devices via `get_devices_list()`.
4. **Update checks** – Fetches the latest release information via `get_latest_release()`.

```rust
fn new() -> (Self, Task<Message>) {
    (
        Self::default(),
        Task::batch([
            font::load(include_bytes!("../../../resources/assets/icons.ttf").as_slice())
                .map(Message::FontLoaded),
            Task::perform(async { initial_load() }, Message::ADBSatisfied),
            Task::perform(async { get_devices_list() }, Message::LoadDevices),
            Task::perform(async move { get_latest_release() }, Message::GetLatestRelease),
        ]),
    )
}

```

## Theming and Custom Widgets

Visual consistency is enforced through [`src/theme.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/theme.rs) and [`src/style.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/style.rs). The `UadGui::theme` method maps user preferences to Iced’s `Theme` enum, while helper functions like `button_primary` and `text` centralize font handling and color schemes.

This styling layer allows UAD-ng to support light and dark modes seamlessly, with the `Theme` selection propagating automatically through Iced’s rendering pipeline whenever the user changes settings.

## Summary

- **Centralized Architecture** – The `UadGui` struct in [`src/gui.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/gui.rs) implements Iced’s `Application` trait, serving as the single source of truth for UI state.
- **Message Passing** – All interactions flow through the `Message` enum, processed by `UadGui::update` to maintain predictable state transitions.
- **Modular Views** – Separate modules under `src/views/` handle specific screens (List, About, Settings), keeping the codebase maintainable.
- **Async-First Design** – Background operations like ADB queries and update checks use `Task::perform`, preventing the interface from blocking during device communication.
- **Composable Navigation** – The `nav_menu` helper in [`src/widgets/navigation_menu.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/widgets/navigation_menu.rs) dynamically renders the top bar based on current device and view state.

## Frequently Asked Questions

### What is the Iced framework and why does UAD-ng use it?

Iced is a cross-platform GUI library for Rust inspired by the Elm Architecture, emphasizing type safety and reactive programming. UAD-ng uses Iced because it provides a declarative API where UI elements are pure functions of state, making it easier to manage complex asynchronous workflows like ADB device communication without data races or callback hell.

### How does UAD-ng handle asynchronous operations without freezing the interface?

The application leverages Iced’s **`Task`** system. When `UadGui::update` needs to perform long-running work—such as scanning devices with `get_devices_list()` or downloading updates—it returns a `Task::perform` instead of blocking. Iced executes this task on a separate thread and delivers the result back as a new `Message`, keeping the main event loop responsive.

### Where is the main entry point for the GUI application located?

The program starts in **[`src/main.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/main.rs)**, which calls `UadGui::start()` to bootstrap the Iced runtime. The `UadGui` struct itself is defined in **[`src/gui.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/src/gui.rs)**, where it implements the `Application` trait and manages the entire lifecycle from initialization to shutdown.

### How does view navigation work between the package list, settings, and about screens?

Navigation works through a `View` enum stored in `UadGui`. When users click navigation buttons, the `update` method changes `self.view` to `View::List`, `View::About`, or `View::Settings`. The `view` method then pattern-matches on this enum to render the appropriate sub-view component, mapping child messages back to the parent `Message` type using `.map()` transformations.