How to make dialog components reusable in Jetpack Compose

Viewed 243

Can a dialog be altered to make its components reusable without having to type duplicate lines of code for multiple items. My list has more than one item. When I click the first list item (Starters), I want Starters as the dialog title and the Screen.Starters.route navigation route to be used, whereas when I click the other list item (Desserts), I want Desserts as the dialog title Screen.Starters.route navigation route to be used.

enum class Items(@StringRes val textResId: Int) {
    Starters(R.string.starters),
    Desserts(R.string.desserts)
}    

@Composable
fun MainScreen(navController: NavController, dialogTitle: String) {
val openDialog = remember { mutableStateOf(false) }
val dialogTitle: String
if (openDialog.value) {
    AlertDialog(
        onDismissRequest = {
            openDialog.value = false
        },
        title = {Text(dialogTitle)},
        text = {Text("Ready to order?")},
        confirmButton = {
            Button(onClick = {navController.navigate(route = Screen.Starters.route), openDialog.value = false}) {Text("Order food")}
        },
        dismissButton = {
            OutlinedButton(onClick = {openDialog.value = false}) {Text("Cancel")}
        }
    )
}

val myListener: (Items) -> Unit = { item ->
    when (item) {
        Items.Starters -> { openDialog.value = true }
        Items.Desserts -> { openDialog.value = true }
    }
}


Scaffold(
    topBar = {...},
    content = { it ->
        LazyColumn(modifier = Modifier.padding(it),
          lazyItems = arrayOf(Items.ItemStaters, Items.ItemDesserts),
          onClickItem = myListener) {
          Text(text = stringResource(id = it.textResId))
        }
    },
    containerColor = ...val
)

Update

    val myListener: (Items) -> Unit = { item ->
        when (item) {
            Items.Starters -> {
                MyDialog(
                    onDismissRequest = { /*TODO*/ },
                    title = { /*TODO*/ },
                    text = { /*TODO*/ },
                    dismissButton = { /*TODO*/ },
                    onConfirm = { /*TODO*/ }
                )
            }
        }
    }
1 Answers

In Jetpack Compose you can make anything reusable using slot api as default Compososables do.

@Composable
fun MyDialog(
    onDismissRequest: () -> Unit,
    title: @Composable () -> Unit,
    text: @Composable () -> Unit,
    dismissButton: @Composable () -> Unit,
    onConfirm: (String)->Unit
) {
    AlertDialog(
        onDismissRequest = onDismissRequest,
        title =title,
        text = text,
        dismissButton = dismissButton,
        confirmButton = {
            Button(onClick = { 
                onConfirm("You picked....")
            }) {
                Text("Button")
            }
        }
    )
}

Using this pattern which lets you pass your own Composables or lambdas you can customize your Dialog or Composable as you see fit and make it highly reusable.

If you wish to open such a dialog on a click from an Item with desired parameters you can add callback to items and set a state with correct value.

To open a dialog based on your Item and navigation i would change from a binary:

val openDialog = remember { mutableStateOf(false) }

to class that contains item details such as

// This is your data class, it's a simplification for demonstration

data class Dessert(val id: Int)

@Composable
private fun MyDessertRow(
    dessert: Dessert, onClick: () -> Unit
) {
    // Some Row, Column, Etc
}

sealed class DialogOption {
    object None : DialogOption()
    class NewDialog(val item: Dessert) : DialogOption()
}

And add it as:

val desserts = listOf(Dessert(1), Dessert(2))

var showDialogOption:DialogOption by remember {
    mutableStateOf(DialogOption.None)
}
LazyColumn() {
    items(desserts) { dessert ->
        MyDessertRow(dessert = dessert,
            onClick = {
                showDialogOption = DialogOption.NewDialog(dessert)
            }
        )
    }
}

if (showDialogOption != DialogOption.None) {

    val options = showDialogOption as DialogOption.NewDialog

    val dessert = options.item
    MyDialog(
        onDismissRequest = { /*TODO*/ },
        title = { /*TODO*/ },
        text = { /*TODO*/ },
        dismissButton = { /*TODO*/ },
        onConfirm = {
                // Navigation here
        }
    )
}
Related