Over the past couple of years switching to Kotlin full-time, I’ve accumulated a small library of extension functions that I copy into every new project. These aren’t fancy - they’re just helpers that come up constantly and make the code noticeably easier to read.
1. View Visibility Shortcuts
Chaining visibility changes becomes much cleaner.
fun View.show() { visibility = View.VISIBLE }
fun View.hide() { visibility = View.GONE }
fun View.invisible() { visibility = View.INVISIBLE }
// Usage: textView.hide()
2. Safe String Conversions
Eliminate repetitive try-catch blocks when parsing user input.
fun String?.toIntOrDefault(default: Int = 0): Int {
return this?.toIntOrNull() ?: default
}
// Usage: val age = editText.text.toString().toIntOrDefault(18)
3. Toast Shorthand
fun Context.toast(message: String, length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, length).show()
}
// Usage: toast("Saved")
4. Date Formatting
Turn timestamps into readable dates inline.
fun Long.toDateString(pattern: String = "yyyy-MM-dd"): String {
val formatter = SimpleDateFormat(pattern, Locale.getDefault())
return formatter.format(Date(this))
}
// Usage: val dateStr = task.updatedAt.toDateString("MMM dd, yyyy")
5. Lifecycle-Aware Coroutine Launch
Now that viewModelScope is available in the stable lifecycle extensions, this helper wraps it with automatic error handling so I don’t have to repeat the try-catch in every ViewModel.
fun ViewModel.launchSafe(block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(Dispatchers.Main) {
try {
block()
} catch (e: Exception) {
Log.e("ViewModel", "Coroutine error: ${e.message}")
}
}
}
When NOT to Use Extensions
- Core logic: If a function modifies the internal state of a class, it belongs inside that class.
- Private members: Extensions cannot see private APIs.
- One-off functions: If you only use it once, a standard function is simpler.
I organise extensions by context in separate files - ViewExt.kt, DateExt.kt - so they’re easy to find and easy to copy across projects.