Question

Kotlin EditText addTextChangedListener Lambda Guide

kotlinandroidtextviewanko

Question

How can I write a Kotlin lambda for an Android EditText text-change listener? The following code produces an error:

passwordEditText.addTextChangedListener { charSequence ->
    try {
        password = charSequence.toString()
    } catch (error: Throwable) {
        raise(error)
    }
}

What is the correct way to listen for text changes and assign the current text to password?

Short Answer

You will learn how Kotlin lambdas work with Android text-change listeners, why addTextChangedListener may require an AndroidX KTX import, and how to handle the nullable CharSequence value safely.

Concept

EditText notifies your code whenever its contents change. Android's original listener interface, TextWatcher, has three callback methods:

  • beforeTextChanged — runs before the text is modified.
  • onTextChanged — runs while the text changes.
  • afterTextChanged — runs after the change is complete.

Writing an entire TextWatcher is verbose when you only need one callback. AndroidX Core KTX provides an extension function named addTextChangedListener that accepts lambdas for these callbacks.

The first trailing lambda is the onTextChanged callback. Its first parameter is a nullable CharSequence?, because Android does not guarantee a non-null value in the API contract. Calling toString() on it is safe: a nullable value can be converted to a string, although null.toString() becomes the literal string "null". For input fields, orEmpty() is usually clearer because it converts null to an empty string.

The try/catch block is unnecessary for a simple assignment. Also, Kotlin uses throw error, not raise(error).

Mental Model

Think of an EditText as a doorbell. A listener is the instruction you give the doorbell: “When someone presses you, run this code.”

A lambda is a short way to write that instruction. Instead of creating a full TextWatcher object with three methods, you provide only the action you care about:

{ text -> password = text.orEmpty() }

Here, text is the message delivered by the doorbell: the current contents of the field.

Take Quiz

Syntax and Examples

Use the AndroidX Core KTX extension import:

import androidx.core.widget.addTextChangedListener

Then attach a listener to an EditText:

passwordEditText.addTextChangedListener { text ->
    password = text.orEmpty()
}

text has the type CharSequence?. orEmpty() produces a non-null CharSequence, which is assigned to password if password is a CharSequence.

If password is a String, convert the value explicitly:

passwordEditText.addTextChangedListener { text ->
    password = text?.toString().orEmpty()
}

You can name the callback parameters when you need information about the edit position:

passwordEditText.addTextChangedListener { text, start, before, count ->
    val currentPassword = text?.toString().orEmpty()
    println("Changed at $start: removed $before characters and added $count")
    password = currentPassword
}

For password fields, avoid logging the actual password in production code.

Step by Step Execution

Consider this code:

var password = ""

passwordEditText.addTextChangedListener { text ->
    password = text?.toString().orEmpty()
}

Suppose the user types cat.

  1. The user enters c.
  2. The EditText changes its contents to "c".
  3. Android calls the lambda and passes "c" as text.
  4. text?.toString().orEmpty() evaluates to "c".
  5. password becomes "c".
  6. The same sequence repeats when the user types a and then t.
  7. After the final edit, password contains "cat".

The listener runs for every edit, including typing, deleting, pasting, and changes made programmatically with setText().

Real World Use Cases

Text-change listeners are useful when an app must react immediately to input:

  • Form validation: Enable a Submit button only when an email address and password are valid.
  • Password requirements: Show whether a password meets minimum length or character rules.
  • Search-as-you-type: Filter a list as the user enters a search query.
  • Character counters: Display remaining characters for a bio or message field.
  • Formatting: Add separators to a phone number or payment-card number carefully.
  • API queries: Start a search request after the user stops typing, usually with debouncing to avoid excessive requests.
Take Quiz

Real Codebase Usage

In production Android code, keep text listeners small and move business logic into a function, ViewModel, or state holder.

passwordEditText.addTextChangedListener { text ->
    viewModel.onPasswordChanged(text?.toString().orEmpty())
}

This keeps UI code responsible for reading input while the ViewModel handles validation and screen state.

A common validation pattern is to derive UI state from the latest text:

emailEditText.addTextChangedListener { text ->
    val email = text?.toString().orEmpty()
    submitButton.isEnabled = email.contains('@')
}

Use guard clauses when input is not ready:

searchEditText.addTextChangedListener { text ->
    val query = text?.toString().orEmpty().trim()
    if (query.length < 2) {
        showEmptyResults()
        return@addTextChangedListener
    }

    search(query)
}

For expensive work such as network search, do not call the API on every keystroke. Debounce the input and cancel outdated requests.

Common Mistakes

Using raise(error)

raise is not Kotlin syntax. To rethrow an exception, use throw:

try {
    password = text?.toString().orEmpty()
} catch (error: Throwable) {
    throw error
}

However, this try/catch adds no value, so remove it entirely.

Missing the AndroidX KTX import

The lambda-friendly overload comes from AndroidX Core KTX:

import androidx.core.widget.addTextChangedListener

If it is unavailable, ensure your project includes the AndroidX Core KTX dependency:

dependencies {
    implementation("androidx.core:core-ktx:<version>")
}

Use the version managed by your project's dependency setup.

Assuming text cannot be null

This can cause a compile error if password is a non-null String:

// Incorrect when password is a String
password = text.toString()

Prefer:

password = text?.toString().orEmpty()

Confusing addTextChangedListener with addTextChangeListener

The AndroidX extension is named addTextChangedListener with Changed.

Performing expensive work for every character

This is risky:

searchEditText.addTextChangedListener { text ->
    fetchResults(text?.toString().orEmpty())
}

Typing quickly can trigger many requests. Debounce the input or wait for an explicit search action.

Accidentally triggering a listener again

Calling setText() inside a listener can cause another callback. Check whether the value actually needs to change, or temporarily remove the listener when implementing formatting logic.

Comparisons

ApproachBest whenNotes
addTextChangedListener { ... }You only need onTextChangedConcise AndroidX KTX lambda syntax.
addTextChangedListener(afterTextChanged = { ... })You need the final editable textReceives an Editable? after the change.
TextWatcher objectYou need multiple callbacks or reusable complex logicMore verbose but available through the platform API.
setOnEditorActionListenerYou need to react to the keyboard's Done/Search actionIt does not run on every character.

To use the afterTextChanged callback explicitly:

passwordEditText.addTextChangedListener(
    afterTextChanged = { editable ->
        password = editable?.toString().orEmpty()
    }
)

Choose onTextChanged for immediate updates such as button state. Choose afterTextChanged when you need to work with the final editable contents, such as carefully applying formatting.

Take Quiz

Cheat Sheet

import androidx.core.widget.addTextChangedListener
// Store text in a String safely
editText.addTextChangedListener { text ->
    value = text?.toString().orEmpty()
}
// Use all onTextChanged parameters
editText.addTextChangedListener { text, start, before, count ->
    // text: current CharSequence?
    // start: index where the change started
    // before: number of removed characters
    // count: number of added characters
}
// React after the edit finishes
editText.addTextChangedListener(
    afterTextChanged = { editable ->
        value = editable?.toString().orEmpty()
    }
)
  • Import androidx.core.widget.addTextChangedListener for lambda syntax.
  • CharSequence? can be null; use ?.toString().orEmpty() for a non-null String.
  • Kotlin throws exceptions with throw, not raise.
  • The callback runs for typing, deletion, paste, and setText().
  • Avoid logging passwords or sending a network request on every keystroke.

FAQ

Why does addTextChangedListener not accept my lambda?

The lambda overload is an AndroidX Core KTX extension. Add import androidx.core.widget.addTextChangedListener and ensure Core KTX is included in your dependencies.

What type is the text parameter in addTextChangedListener?

For the onTextChanged lambda, it is CharSequence?. Convert it with text?.toString().orEmpty() when you need a non-null String.

Why is text.toString() not always the best choice?

It technically works for a nullable value, but a null value becomes the string "null". text?.toString().orEmpty() instead produces an empty string.

Should I use onTextChanged or afterTextChanged?

Use onTextChanged for ordinary immediate reactions. Use afterTextChanged when you need the final Editable content, particularly for formatting or post-edit validation.

Is a try/catch needed when reading EditText text?

No. Assigning the text normally does not need exception handling. Remove the unnecessary try/catch.

How do I rethrow an error in Kotlin?

Use throw error. There is no built-in Kotlin raise(error) function.

Does setting text in code trigger the listener?

Yes. editText.setText(...) generally triggers text-change callbacks, so take care to avoid unintended update loops.

Take Quiz

Mini Project

Description

Build a small password-input validator that listens to an EditText and updates a status label as the user types. This mirrors a common registration-screen feature while keeping password contents out of logs.

Goal

Show whether the entered password has at least eight characters and enable a Continue button only when it does.

Requirements

Requirement 1 Requirement 2 Requirement 3

Take Quiz

Keep learning

Related questions

View more