Creating reusable AlertDialogs with Custom Views in Anko

One of the features Anko is most notable for is simplifying the process of creating layouts programmatically. Anko also offers some variations of some views such as the AlertDialog. The indeterminateProgressDialog() displays an AlertDialog with a progress dialog while a selector() displays an AlertDialog with a list of text items. Even with all these, sometimes we still need to create dialogs with custom views, and we might need to use those dialogs in multiple screens in our app.

There will be a lot of code duplication if we keep replicating the same Anko DSL whenever we need it. In this article, we’ll first explore creating an AlertDialog with a default layout with Anko. Next, we will learn how to create a reusable AlertDialog with a Custom View, still with Anko. We’ll extract all the related code to a separate file and keep our views clean.

AlertDialog with default layout

We need to import the Anko Commons library in the app level build.gradle file.

implementation "org.jetbrains.anko:anko-commons:$anko_version"

Here is a typical dialog with a title resource, a message resource, a positive and a negative button which dismisses the dialog when clicked. We can replace the titleResource key with title and that will take in a string. But, as good Android citizens, and also because of internationalization, we want to have all our strings in the strings.xml file.

alert {
titleResource = R.string.title
messageResource = R.string.message
positiveButton(R.string.ok, { dialog ->
dialog.dismiss()
})
negativeButton(R.string.cancel, { dialog ->
dialog.dismiss()
})
show()
}

Reusable AlertDialog with a Custom View

Let’s say we need to display a dialog to collect customer feedback, and we need to do that in multiple places in our app. While we can always create the layout in our code every single time, we will end up repeating ourselves and violating the DRY principle.

Image for post
Image for post
Sample dialog

In order to create the sample dialog above, we first need to include these additional imports in our app level build.gradle file.

implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
implementation "org.jetbrains.anko:anko-sdk25:$anko_version"

Next, we create a new class that takes in an AnkoContext. We use an AnkoContext because it uses a weak reference under the hood. Then, we flesh out the views and call the dialog’s show() method.

class FeedbackDialog(ui: AnkoContext<View>) {

lateinit var dialog: DialogInterface
lateinit var feedbackText: TextInputEditText
lateinit var cancelButton: TextView
lateinit var okButton: TextView

init {
with(ui) {
dialog = alert {
customView {
verticalLayout {
padding = dip(16)

textView(R.string.some_title) { ... }

textView(R.string.some_message) { ... }

textInputLayout {
hint = context.getString(R.string.hint)
feedbackText = textInputEditText {
textSize = 16f
}
}

// More DSL goes here
}
}
}
.show()
}
}
}

We have now created our dialog with a custom view. We have to be able to call this from any View/Activity/Fragment. We’ll need to reference whatever view we are in from AnkoContext’s create() method. In the code below, we pass in the ContentView from an Activity. In a Fragment, we can use the View returned from the onCreateView() method.

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

private fun displayFeedbackDialog(view: View) {
val feedbackDialogUi by lazy {
contentView
?.let {
FeedbackDialog
(AnkoContext.create(ctx, it))
}
}

feedbackDialogUi?.okButton?.setOnClickListener {
val text = feedbackDialogUi.feedbackText.text.toString()
toast(getString(R.string.confirmation, text))

feedbackDialogUi.dialog.dismiss()
}

feedbackDialogUi?.cancelButton?.setOnClickListener {
feedbackDialogUi.dialog.dismiss()
}
}
}

Conclusion

This process is quite different from creating MainActivity’s entire layout with Anko. In this case, the MainActivity doesn’t need to extend or implement any additional classes. The FeedbackDialog class can be called from multiple views with the buttons set to perform different actions, all without editing the DSL.

All the code in this article can be found on Github. Please leave a comment if you have any feedback.

Written by

Android Dev, Marathoner

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store