Android Development
Custom Dialog
Android UI
Mobile App Design
Android Programming

How to create a Custom Dialog box in android?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A custom dialog is useful when a standard alert with a title, message, and two buttons is not enough for your flow. On modern Android, the safest implementation is usually a DialogFragment with a custom XML layout, because it handles lifecycle changes more cleanly than a raw Dialog.

Why DialogFragment Is the Better Starting Point

You can create dialogs directly with Dialog, but that approach is easy to misuse when activities rotate, fragments detach, or the process is backgrounded. DialogFragment integrates with the fragment lifecycle and makes showing, recreating, and dismissing dialogs more predictable.

That matters for real apps because dialogs are often shown during configuration changes, navigation transitions, or async callbacks.

Step 1: Define a Custom Layout

Create a layout file in res/layout/dialog_confirm_delete.xml:

xml
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3    android:layout_width="match_parent"
4    android:layout_height="wrap_content"
5    android:orientation="vertical"
6    android:padding="20dp">
7
8    <TextView
9        android:layout_width="match_parent"
10        android:layout_height="wrap_content"
11        android:text="Optional reason"
12        android:textStyle="bold" />
13
14    <EditText
15        android:id="@+id/reasonInput"
16        android:layout_width="match_parent"
17        android:layout_height="wrap_content"
18        android:hint="Type a reason" />
19</LinearLayout>

This layout becomes the content view inside the dialog. Keep it focused and compact, because dialogs should support small screens and font scaling gracefully.

Step 2: Build the Dialog in a DialogFragment

Inflate the custom layout and attach it to an AlertDialog or MaterialAlertDialogBuilder.

kotlin
1import android.app.Dialog
2import android.os.Bundle
3import android.view.LayoutInflater
4import android.widget.EditText
5import androidx.appcompat.app.AlertDialog
6import androidx.core.os.bundleOf
7import androidx.fragment.app.DialogFragment
8
9class ConfirmDeleteDialog : DialogFragment() {
10    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
11        val view = LayoutInflater.from(requireContext())
12            .inflate(R.layout.dialog_confirm_delete, null, false)
13
14        val reasonInput = view.findViewById<EditText>(R.id.reasonInput)
15
16        return AlertDialog.Builder(requireContext())
17            .setTitle("Delete item")
18            .setView(view)
19            .setPositiveButton("Delete") { _, _ ->
20                parentFragmentManager.setFragmentResult(
21                    "delete_result",
22                    bundleOf(
23                        "confirmed" to true,
24                        "reason" to reasonInput.text.toString()
25                    )
26                )
27            }
28            .setNegativeButton("Cancel") { _, _ ->
29                parentFragmentManager.setFragmentResult(
30                    "delete_result",
31                    bundleOf("confirmed" to false)
32                )
33            }
34            .create()
35    }
36}

This approach keeps the dialog self-contained and avoids tight coupling to a specific activity method.

Step 3: Show the Dialog and Receive the Result

From a fragment, register a result listener and show the dialog with the fragment manager.

kotlin
1import android.os.Bundle
2import android.view.View
3import android.widget.Button
4import androidx.fragment.app.Fragment
5
6class ItemsFragment : Fragment(R.layout.fragment_items) {
7    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
8        childFragmentManager.setFragmentResultListener(
9            "delete_result",
10            viewLifecycleOwner
11        ) { _, result ->
12            val confirmed = result.getBoolean("confirmed")
13            val reason = result.getString("reason", "")
14            if (confirmed) {
15                performDelete(reason)
16            }
17        }
18
19        view.findViewById<Button>(R.id.deleteButton).setOnClickListener {
20            ConfirmDeleteDialog().show(childFragmentManager, "confirm_delete")
21        }
22    }
23
24    private fun performDelete(reason: String) {
25        println("Deleting with reason: $reason")
26    }
27}

Using the Fragment Result API keeps communication explicit and avoids deprecated callback patterns.

Input Validation Before Dismissal

If the positive action requires valid input, do not let the dialog dismiss immediately. Intercept the button click in onStart and only dismiss after validation succeeds.

kotlin
1override fun onStart() {
2    super.onStart()
3    val alertDialog = dialog as AlertDialog
4    val positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
5
6    positive.setOnClickListener {
7        val input = alertDialog.findViewById<EditText>(R.id.reasonInput)
8            ?.text
9            ?.toString()
10            .orEmpty()
11
12        if (input.length >= 3) {
13            parentFragmentManager.setFragmentResult(
14                "delete_result",
15                bundleOf("confirmed" to true, "reason" to input)
16            )
17            dismiss()
18        }
19    }
20}

This pattern is especially useful for forms, confirmations, and destructive actions.

Common Pitfalls

The most common mistake is showing a raw Dialog tied directly to an activity context and then hitting lifecycle problems during rotation or navigation.

Another issue is doing real work inside the dialog callback on the main thread. Button handlers should dispatch business logic, not perform long-running work directly.

Developers also forget string resources, accessibility labels, and input validation. A custom dialog is still part of your app UI, so it should meet the same quality bar as the rest of the interface.

Finally, avoid tightly coupling the dialog to one host activity through casts or hand-written interfaces when fragment results or a shared view model already solve the communication cleanly.

Summary

  • Prefer DialogFragment over raw Dialog for lifecycle-safe custom dialogs.
  • Build the UI in a dedicated XML layout and inflate it inside the dialog.
  • Return results with the Fragment Result API to keep components decoupled.
  • Validate input before dismissing when the dialog contains form fields.
  • Treat custom dialogs like normal UI: theme them well, use string resources, and test on small screens.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.