Skip to content

Screen UI Providers

Andrés Pedraza Míguez edited this page Dec 6, 2025 · 4 revisions

Screen UI Providers

The ScreenUiProvider pattern is a mechanism used to decouple the "Chrome" of a screen (TopAppBar, FloatingActionButton) from the screen's content and the main navigation host.

The Problem

In a modular app, the MainScreen (which holds the Scaffold) doesn't know about specific screens inside feature modules. We wanted to avoid a monolithic when(route) statement in MainScreen to determine which Title or FAB to show.

The Solution

We define an interface ScreenUiProvider in :core:ui. Each Feature module implements this interface for its screens if they need a TopBar or FAB.

1. The Interface

interface ScreenUiProvider {
    val route: String
    val topBar: (@Composable () -> Unit)? get() = null
    val fab: (@Composable () -> Unit)? get() = null
}

2. Implementation (Feature Module)

In the feature module (e.g., Settings), we create an implementation binding it to a specific route:

class DefaultCurrencyScreenUiProviderImpl(
    override val route: String = Routes.SETTINGS_DEFAULT_CURRENCY
) : ScreenUiProvider {
    override val topBar: @Composable () -> Unit = {
        TopAppBar(title = { Text("Currency") })
    }
}

3. Dependency Injection

We register it in Koin as a ScreenUiProvider:

single { DefaultCurrencyScreenUiProviderImpl() } bind ScreenUiProvider::class

4. Consumption (The "Magic")

In MainScreen (or FeatureScaffold), we inject all providers:

val providers: List<ScreenUiProvider> = getKoin().getAll()
val currentProvider = providers.find { it.route == currentRoute }

Scaffold(
    topBar = { currentProvider?.topBar?.invoke() }
) { content() }

This makes the architecture extensible. Adding a new screen with a toolbar requires zero changes in the MainScreen code.

Clone this wiki locally