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.