← Docs
Recipe

Kotlin Android Patterns

Production-grade Kotlin idioms for Android — sealed hierarchies, delegate properties, and coroutine scoping that ship.

Sealed class result handling

sealed class ApiResult<out T> {
    data class Success<T>(val data: T) : ApiResult<T>()
    data class Error(val code: Int, val message: String) : ApiResult<Nothing>()
    object Loading : ApiResult<Nothing>()
}

fun handle(result: ApiResult<User>) = when (result) {
    is ApiResult.Success -> showProfile(result.data)
    is ApiResult.Error -> showSnackbar(result.message)
    ApiResult.Loading -> showSpinner()
}

ViewModel delegate

class ProfileViewModel : ViewModel() {
    private val _state = MutableStateFlow(ProfileState())
    val state: StateFlow<ProfileState> = _state.asStateFlow()

    fun load(id: String) {
        viewModelScope.launch {
            _state.update { it.copy(loading = true) }
            repo.fetch(id).collect { result ->
                _state.update { it.copy(loading = false, user = result) }
            }
        }
    }
}

Coroutine scope lifecycle

class MainActivity : AppCompatActivity() {
    private val scope = MainScope()

    override fun onDestroy() {
        scope.cancel()
        super.onDestroy()
    }

    private fun observe() {
        scope.launch(Dispatchers.Main) {
            viewModel.events.collect { event ->
                when (event) {
                    is NavEvent.Go -> findNavController().navigate(event.dest)
                }
            }
        }
    }
}

Lazy injection

class App : Application() {
    val repo: UserRepository by lazy {
        UserRepository(
            api = RetrofitClient.instance,
            cache = Room.databaseBuilder(this, AppDb::class.java, "app.db").build()
        )
    }
}

These patterns are extracted from Meridian's own Android SDK. Every snippet compiles against Kotlin 1.9+ and AndroidX.

Meridian — getnimbus.net