在Jetpack Compose中显示自定义警告对话框

61

我正在寻找如何在Jetpack Compose中创建自定义对话框。在XML或Material Design中,我们可以轻松地创建自定义对话框,其中可以接受用户输入、单选按钮等等,但是我在Jetpack Compose中找不到类似的东西。


自定义对话框 - Bolt UIX
9个回答

73

M3 1.1.0-alpha04开始,有一个带有content插槽的AlertDialog组合函数。

val openDialog = remember { mutableStateOf(true) }

if (openDialog.value) {
    androidx.compose.material3.AlertDialog(
        onDismissRequest = {
            // Dismiss the dialog when the user clicks outside the dialog or on the back
            // button. If you want to disable that functionality, simply use an empty
            // onDismissRequest.
            openDialog.value = false
        }
    ) {
        Surface(
            modifier = Modifier
                .wrapContentWidth()
                .wrapContentHeight(),
            shape = MaterialTheme.shapes.large
        ) {
            Column(modifier = Modifier.padding(16.dp)) {

                //... AlertDialog content
            }
        }
    }
}

enter image description here


M3 1.1.0-alpha04 之前或使用 M2,您可以使用标准 AlertDialog
texttitlebuttons 参数支持 @Composable 函数,这样您就可以根据自己的喜好自定义对话框。

例如:

val openDialog = remember { mutableStateOf(true) }
var text by remember { mutableStateOf("") }

if (openDialog.value) {
    AlertDialog(
        onDismissRequest = {
            openDialog.value = false
        },
        title = {
            Text(text = "Title")
        },
        text = {
            Column() {
                TextField(
                    value = text,
                    onValueChange = { text = it }
                )
                Text("Custom Text")
                Checkbox(checked = false, onCheckedChange = {})
            }
        },
        buttons = {
            Row(
                modifier = Modifier.padding(all = 8.dp),
                horizontalArrangement = Arrangement.Center
            ) {
                Button(
                    modifier = Modifier.fillMaxWidth(),
                    onClick = { openDialog.value = false }
                ) {
                    Text("Dismiss")
                }
            }
        }
    )
}

enter image description here


我不能在上面显示图片或其他东西吗? - Tanjim ahmed
你要把 TextFieldTextCheckbox 放在 text 参数里面吗?这是不符合直觉的。所以基本上除了确认和取消按钮之外,任何内容都应该放在那里吗? - kc_dev
FYI:您可以在“Column”中放置任何内容,包括“Image”。 - MSpeed
1
@MarkDelphi,规则很简单:如果您使用=声明openDialog,则必须使用.value.。如果代之以by, 则不得使用.value. - MarianD
@MarianD非常感谢您的澄清。有时候谷歌文档很难正确理解。再次感谢。 - Mark Delphi
显示剩余2条评论

38

这个示例演示了如何在安卓Jet Compose中制作自定义对话框。

阅读更多 https://www.boltuix.com/2022/01/ice-cream-app-ui-ux.html

import android.annotation.SuppressLint
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.compose.example.ui.theme.Pink80
import com.compose.example.ui.theme.Purple40
import com.compose.example.ui.theme.Purple80
import com.compose.example.ui.theme.PurpleGrey40

@Composable
fun CustomDialog(openDialogCustom: MutableState<Boolean>) {
    Dialog(onDismissRequest = { openDialogCustom.value = false}) {
        CustomDialogUI(openDialogCustom = openDialogCustom)
    }
}

//Layout
@Composable
fun CustomDialogUI(modifier: Modifier = Modifier, openDialogCustom: MutableState<Boolean>){
    Card(
        //shape = MaterialTheme.shapes.medium,
        shape = RoundedCornerShape(10.dp),
        // modifier = modifier.size(280.dp, 240.dp)
        modifier = Modifier.padding(10.dp,5.dp,10.dp,10.dp),
        elevation = 8.dp
    ) {
        Column(
            modifier
                .background(Color.White)) {

            //.......................................................................
            Image(
                painter = painterResource(id = R.drawable.notification),
                contentDescription = null, // decorative
                contentScale = ContentScale.Fit,
                colorFilter  = ColorFilter.tint(
                    color = Purple40
                ),
                modifier = Modifier
                    .padding(top = 35.dp)
                    .height(70.dp)
                    .fillMaxWidth(),

                )

            Column(modifier = Modifier.padding(16.dp)) {
                androidx.compose.material3.Text(
                    text = "Get Updates",
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .padding(top = 5.dp)
                        .fillMaxWidth(),
                    style = MaterialTheme.typography.labelLarge,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis
                )
                androidx.compose.material3.Text(
                    text = "Allow Permission to send you notifications when new art styles added.",
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .padding(top = 10.dp, start = 25.dp, end = 25.dp)
                        .fillMaxWidth(),
                    style = MaterialTheme.typography.bodyMedium
                )
            }
            //.......................................................................
            Row(
                Modifier
                    .fillMaxWidth()
                    .padding(top = 10.dp)
                    .background(Purple80),
                horizontalArrangement = Arrangement.SpaceAround) {

                androidx.compose.material3.TextButton(onClick = {
                    openDialogCustom.value = false
                }) {

                    androidx.compose.material3.Text(
                        "Not Now",
                        fontWeight = FontWeight.Bold,
                        color = PurpleGrey40,
                        modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
                    )
                }
                androidx.compose.material3.TextButton(onClick = {
                    openDialogCustom.value = false
                }) {
                    androidx.compose.material3.Text(
                        "Allow",
                        fontWeight = FontWeight.ExtraBold,
                        color = Color.Black,
                        modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
                    )
                }
            }
        }
    }
}


@SuppressLint("UnrememberedMutableState")
@Preview (name="Custom Dialog")
@Composable
fun MyDialogUIPreview(){
    CustomDialogUI(openDialogCustom = mutableStateOf(false))
}

这里输入图像描述


1
真是太棒了... 真的太棒了! - Giddy Naya
对我有用,谢谢 - Johnny Antony . P
1
这张截图使用的字体是什么?我真的很喜欢它。 - Lampione
4
@matteo:字体名称是Jost https://fonts.google.com/specimen/Jost - Bolt UIX
1
关闭它必须是:if (openDialogCustom.value){ Dialog(onDismissRequest = { openDialogCustom.value = false}) { CustomDialogUI(openDialogCustom = openDialogCustom) } } - Sadegh J

20

还可以使用 lambda 将对话框中的值返回到任何其他组合中:

@Composable
private fun CustomDialogWithResultExample(
    onDismiss: () -> Unit,
    onNegativeClick: () -> Unit,
    onPositiveClick: (Color) -> Unit
) {
    var red by remember { mutableStateOf(0f) }
    var green by remember { mutableStateOf(0f) }
    var blue by remember { mutableStateOf(0f) }

    val color = Color(
        red = red.toInt(),
        green = green.toInt(),
        blue = blue.toInt(),
        alpha = 255
    )

    Dialog(onDismissRequest = onDismiss) {

        Card(
            elevation = 8.dp,
            shape = RoundedCornerShape(12.dp)
        ) {

            Column(modifier = Modifier.padding(8.dp)) {

                Text(
                    text = "Select Color",
                    fontWeight = FontWeight.Bold,
                    fontSize = 20.sp,
                    modifier = Modifier.padding(8.dp)
                )
                Spacer(modifier = Modifier.height(8.dp))

                // Color Selection
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.Center
                ) {


                    Column {

                        Text(text = "Red ${red.toInt()}")
                        Slider(
                            value = red,
                            onValueChange = { red = it },
                            valueRange = 0f..255f,
                            onValueChangeFinished = {}
                        )
                        Spacer(modifier = Modifier.height(8.dp))

                        Text(text = "Green ${green.toInt()}")
                        Slider(
                            value = green,
                            onValueChange = { green = it },
                            valueRange = 0f..255f,
                            onValueChangeFinished = {}
                        )
                        Spacer(modifier = Modifier.height(8.dp))

                        Text(text = "Blue ${blue.toInt()}")
                        Slider(
                            value = blue,
                            onValueChange = { blue = it },
                            valueRange = 0f..255f,
                            onValueChangeFinished = {}
                        )

                        Spacer(modifier = Modifier.height(8.dp))
                        Surface(
                            border = BorderStroke(1.dp, Color.DarkGray),
                            color = color,
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(40.dp)
                        ) {}
                    }
                }

                // Buttons
                Row(
                    horizontalArrangement = Arrangement.End,
                    modifier = Modifier.fillMaxWidth()
                ) {

                    TextButton(onClick = onNegativeClick) {
                        Text(text = "CANCEL")
                    }
                    Spacer(modifier = Modifier.width(4.dp))
                    TextButton(onClick = {
                        onPositiveClick(color)
                    }) {
                        Text(text = "OK")
                    }
                }
            }
        }
    }
}

并展示它

   var showCustomDialogWithResult by remember { mutableStateOf(false) }


  if (showCustomDialogWithResult) {
        CustomDialogWithResultExample(
            onDismiss = {
                showCustomDialogWithResult = !showCustomDialogWithResult
                Toast.makeText(context, "Dialog dismissed!", Toast.LENGTH_SHORT)
                    .show()
            },
            onNegativeClick = {
                showCustomDialogWithResult = !showCustomDialogWithResult
                Toast.makeText(context, "Negative Button Clicked!", Toast.LENGTH_SHORT)
                    .show()

            },
            onPositiveClick = { color ->
                showCustomDialogWithResult = !showCustomDialogWithResult
                Toast.makeText(context, "Selected color: $color", Toast.LENGTH_SHORT)
                    .show()
            }
        )
    }

结果如下

这里输入图片描述


13

我需要实现类似这样的效果:

custom compose AlertDialog with image on top

将图片放在compose AlertDialog的“title”位置,将文本放在“text”位置,结果如下所示:

enter image description here

因为“title”和“text”被AlertDialogBaselineLayout包裹,导致添加了填充(padding),而我不知道该如何更改。

然而,“buttons”位置没有被包裹,我的解决方案是像以下代码一样(将“title”和“text”位置设置为null,所有对话框内容都放在“buttons”位置):

@Composable
fun AppDialog(
modifier: Modifier = Modifier,
dialogState: Boolean = false,
onDialogPositiveButtonClicked: (() -> Unit)? = null,
onDialogStateChange: ((Boolean) -> Unit)? = null,
onDismissRequest: (() -> Unit)? = null,
) {
    val textPaddingAll = 24.dp
    val buttonPaddingAll = 8.dp
    val dialogShape = RoundedCornerShape(16.dp)

    if (dialogState) {
        AlertDialog(
            onDismissRequest = {
                onDialogStateChange?.invoke(false)
                onDismissRequest?.invoke()
            },
            title = null,
            text = null,
            buttons = {

                Column{
                    Image(
                        painter = painterResource(R.drawable.dialog_top_image),
                        contentDescription = "",
                        contentScale = ContentScale.FillWidth,
                        modifier = Modifier.fillMaxWidth()
                    )
                    Row(Modifier.padding(all = textPaddingAll)){
                        TextWithHTMLSupport(
                            text = stringResource(R.string.gdprText)
                        )
                    }
                    Divider(color = MaterialTheme.colors.onSurface, thickness = 1.dp)

                    Row(
                        modifier = Modifier.padding(all = buttonPaddingAll),
                        horizontalArrangement = Arrangement.Center
                    ) {
                        TextButton(
                            modifier = Modifier.fillMaxWidth(),
                            onClick = {
                                onDialogStateChange?.invoke(false)
                                onDialogPositiveButtonClicked?.invoke()
                            }
                        ) {
                            Text(text = stringResource(R.string.dialog_ok), color = MaterialTheme.colors.onSurface)
                        }
                    }
                }

            },
            properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = false),
            modifier = modifier,
            shape = dialogShape
        )
    }
}

谢谢!如果你有滚动条,这个方法会更好,不像我试过的被接受的答案,虽然它也能用,但是滚动不够流畅。 - Islam Mansour

10

带图片的自定义对话框

要创建像这样的自定义对话框。

  • 首先,创建 Dialog 组合并设置 dismissRequest 和 dialog 属性。
  • 在对话框内创建您自己的视图。
  • 最后,在所需按钮上设置关闭对话框。
@Composable
fun CustomAlertDialog(onDismiss: () -> Unit, onExit: () -> Unit) {

    Dialog(onDismissRequest = { onDismiss() }, properties = DialogProperties(
        dismissOnBackPress = false,dismissOnClickOutside = false
    )) {
        Card(
            //shape = MaterialTheme.shapes.medium,
            shape = RoundedCornerShape(10.dp),
            // modifier = modifier.size(280.dp, 240.dp)
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            elevation = 8.dp
        ) {
            Column(
                Modifier
                    .fillMaxWidth()
                    .background(Color.White)
            ) {


                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(100.dp)
                        .background(Color.Red.copy(alpha = 0.8F)),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.Center,

                    ) {

                    Image(
                        painter = painterResource(id = R.drawable.background_image),
                        contentDescription = "Exit app",
                        modifier = Modifier.fillMaxSize(),
                        contentScale = ContentScale.FillWidth
                    )
                }

                Text(
                    text = "Lorem Ipsum is simply dummy text",
                    modifier = Modifier.padding(8.dp), fontSize = 20.sp
                )

                Text(
                    text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard",
                    modifier = Modifier.padding(8.dp)
                )

                Row(Modifier.padding(top = 10.dp)) {
                    OutlinedButton(
                        onClick = { onDismiss() },
                        Modifier
                            .fillMaxWidth()
                            .padding(8.dp)
                            .weight(1F)
                    ) {
                        Text(text = "Cancel")
                    }


                    Button(
                        onClick = { onExit() },
                        Modifier
                            .fillMaxWidth()
                            .padding(8.dp)
                            .weight(1F)
                    ) {
                        Text(text = "Exit")
                    }
                }


            }
        }
    }
}

现在我们已经创建了自定义对话框,要在按钮单击时显示对话框,您需要创建一个mutableStateOf()变量来维护对话框的显示和取消状态。
此外,创建条件,例如如果变量为true,则调用对话框,否则不调用对话框函数。
@Composable
fun Content() {
    val context = LocalContext.current
    var showCustomDialog by remember {
        mutableStateOf(false)
    }
 
    Column(
        Modifier.fillMaxSize(),
        Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { showCustomDialog = !showCustomDialog }, Modifier.wrapContentSize()) {
            Text(text = "Show Alert Dialog")
        }


    }

    if (showCustomDialog) {
        CustomAlertDialog({
            showCustomDialog = !showCustomDialog
        }, {
            val activity = (context as? Activity)
            activity?.finish()
        })
    }
}

当单击对话框上的取消按钮时,需要更新一个变量为false以隐藏对话框。
代码的最终输出如下:

Custom Dialog with image

带输入框的警示对话框

如上所述,我们需要使用Dialog()可组合函数来创建对话框。但对于输入字段,我们需要创建一个mutableStateOf()变量来保存输入字段的值。

@Composable
fun InputDialogView(onDismiss:() -> Unit) {
    val context = LocalContext.current
    var searchedFood by remember {
        mutableStateOf("")
    }

    Dialog(onDismissRequest = { onDismiss() }) {
        Card(
            //shape = MaterialTheme.shapes.medium,
            shape = RoundedCornerShape(10.dp),
            // modifier = modifier.size(280.dp, 240.dp)
            modifier = Modifier.padding(8.dp),
            elevation = 8.dp
        ) {
            Column(
                Modifier
                    .background(Color.White)
            ) {

                Text(
                    text = "Search your favorite food",
                    modifier = Modifier.padding(8.dp),
                    fontSize = 20.sp
                )

                OutlinedTextField(
                    value = searchedFood,
                    onValueChange = { searchedFood = it }, modifier = Modifier.padding(8.dp),
                    label = { Text("Favorite Food") }
                )

                Row {
                    OutlinedButton(
                        onClick = { onDismiss() },
                        Modifier
                            .fillMaxWidth()
                            .padding(8.dp)
                            .weight(1F)
                    ) {
                        Text(text = "Cancel")
                    }


                    Button(
                        onClick = {
                            Toast.makeText(context, searchedFood, Toast.LENGTH_SHORT).show()
                            onDismiss() },
                        Modifier
                            .fillMaxWidth()
                            .padding(8.dp)
                            .weight(1F)
                    ) {
                        Text(text = "Search")
                    }
                }


            }
        }
    }
}

要显示对话框,您需要按照上述方式操作。通过创建mutableStateOf()变量并使其为true或false来实现。
上述代码的输出是:

Custom dialog with input

加载对话框

对于加载对话框,我们需要使用CircularProgressIndicator()组合函数进行加载动画。除此之外,所有内容与其他自定义对话框相同。

@Composable
fun LoadingView(onDismiss:() -> Unit) {
    Dialog(onDismissRequest = { onDismiss() }) {

        Card(
            shape = RoundedCornerShape(8.dp),
            modifier = Modifier,
            elevation = 8.dp
        ) {
            Column(
                Modifier
                    .background(Color.White)
                    .padding(12.dp)
            ) {
                Text(
                    text = "Loading.. Please wait..",
                    Modifier
                        .padding(8.dp), textAlign = TextAlign.Center
                )

                CircularProgressIndicator(
                    strokeWidth = 4.dp,
                    modifier = Modifier
                        .align(Alignment.CenterHorizontally)
                        .padding(8.dp)
                )
            }
        }
    }
}


关于加载对话框代码的输出,

Custom dialog with loading icon


7
如果您的自定义警告对话框的内容需要滚动(例如:横屏模式或内容变得更长),可以像这样做:
@Composable
fun CustomDialogScrollable(
    onConfirmClicked: () -> Unit,
    onDismiss: () -> Unit,
) {
    Dialog(
        onDismissRequest = onDismiss,
    ) {
        Surface(
            shape = MaterialTheme.shapes.medium,
            color = MaterialTheme.colors.surface,
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                // TITLE
                Text(text = "Title", style = MaterialTheme.typography.subtitle1)

                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .verticalScroll(rememberScrollState())
                        .weight(weight = 1f, fill = false)
                        .padding(vertical = 16.dp)
                ) {
                    Text(
                        text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s",
                        style = MaterialTheme.typography.body2
                    )
                    OutlinedTextField(value = "", onValueChange = {
                    }, Modifier.padding(top = 8.dp), label = { Text(text = "Email") })

                    // other content can go here
                }

                // BUTTONS
                Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
                    TextButton(onClick = onDismiss) {
                        Text(text = "Cancel")
                    }
                    TextButton(onClick = onConfirmClicked) {
                        Text(text = "OK")
                    }
                }
            }
        }
    }
}

使用
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
    CustomDialog({
        // confirm clicked
    }, {
        openDialog.value = false
    })
}

5

在Jetpack Compose中创建自定义对话框很容易。

这是一个询问启用两步验证的对话框。我还添加了点击事件。

输出:

enter image description here

代码:

查看更多带源代码的设计,请访问Jetpack Compose Samples

import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog

/*
For more designs with source code,
visit: https://semicolonspace.com/jetpack-compose-samples/
 */
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BlogPostsTheme(darkTheme = false) {
                Column(
                    modifier = Modifier.fillMaxSize(),
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.Center
                ) {
                    Column(
                        modifier = Modifier
                            .fillMaxSize()
                            .background(color = MaterialTheme.colors.background),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {

                        var openDialog by remember {
                            mutableStateOf(false) // Initially dialog is closed
                        }

                        ButtonClick(buttonText = "Open Dialog") {
                            openDialog = true
                        }

                        if (openDialog) {
                            DialogBox2FA {
                                openDialog = false
                            }
                        }

                    }
                }
            }
        }
    }
}

@Composable
fun DialogBox2FA(onDismiss: () -> Unit) {
    val contextForToast = LocalContext.current.applicationContext

    Dialog(
        onDismissRequest = {
            onDismiss()
        }
    ) {
        Surface(
            modifier = Modifier
                .fillMaxWidth(),
            elevation = 4.dp
        ) {
            Column(
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {

                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(150.dp)
                        .background(color = Color(0xFF35898f)),
                    contentAlignment = Alignment.Center
                ) {
                    Image(
                        modifier = Modifier
                            .padding(top = 16.dp, bottom = 16.dp),
                        painter = painterResource(id = R.drawable.image_security),
                        contentDescription = "2-Step Verification",
                        alignment = Alignment.Center
                    )
                }

                Text(
                    modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
                    text = "2-Step Verification",
                    textAlign = TextAlign.Center,
                    style = TextStyle(
                        fontFamily = FontFamily(Font(R.font.roboto_bold, FontWeight.Bold)),
                        fontSize = 20.sp
                    )
                )

                Text(
                    modifier = Modifier.padding(start = 12.dp, end = 12.dp),
                    text = "Setup 2-Step Verification to add additional layer of security to your account.",
                    textAlign = TextAlign.Center,
                    style = TextStyle(
                        fontFamily = FontFamily(Font(R.font.roboto_regular, FontWeight.Normal)),
                        fontSize = 14.sp
                    )
                )

                Button(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 36.dp, start = 36.dp, end = 36.dp, bottom = 8.dp),
                    colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF35898f)),
                    onClick = {
                        onDismiss()
                        Toast.makeText(
                            contextForToast,
                            "Click: Setup Now",
                            Toast.LENGTH_SHORT
                        ).show()
                    }) {
                    Text(
                        text = "Setup Now",
                        color = Color.White,
                        style = TextStyle(
                            fontFamily = FontFamily(
                                Font(
                                    R.font.roboto_medium,
                                    FontWeight.Medium
                                )
                            ),
                            fontSize = 16.sp
                        )
                    )
                }

                TextButton(
                    onClick = {
                        onDismiss()
                        Toast.makeText(
                            contextForToast,
                            "Click: I'll Do It Later",
                            Toast.LENGTH_SHORT
                        ).show()
                    }) {
                    Text(
                        text = "I'll Do It Later",
                        color = Color(0xFF35898f),
                        style = TextStyle(
                            fontFamily = FontFamily(
                                Font(
                                    R.font.roboto_regular,
                                    FontWeight.Normal
                                )
                            ),
                            fontSize = 14.sp
                        )
                    )
                }
            }
        }
    }
}

@Composable
fun ButtonClick(
    buttonText: String,
    onButtonClick: () -> Unit
) {
    Button(
        shape = RoundedCornerShape(5.dp),
        colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.primary),
        onClick = {
            onButtonClick()
        }) {
        Text(
            text = buttonText,
            fontSize = 16.sp,
            color = Color.White
        )
    }
}

0

谁想要使用像一样的自定义对话框。

enter image description here

@Composable
fun CustomAlertDialog(onDismiss: () -> Unit, onExit: () -> Unit) {
Dialog(
    onDismissRequest = { onDismiss() }, properties = DialogProperties(
        dismissOnBackPress = false, dismissOnClickOutside = false
    )
) {
    Card(
        shape = RoundedCornerShape(10.dp),
        modifier = Modifier
            .fillMaxWidth().padding(0.dp).height(IntrinsicSize.Min),
        elevation = 0.dp
    ) {
        Column(
            Modifier
                .fillMaxWidth()
                .background(Color.White)
        ) {
            Text(
                text = "Logout",
                modifier = Modifier.padding(8.dp, 16.dp, 8.dp, 2.dp)
                    .align(Alignment.CenterHorizontally).fillMaxWidth(), fontSize = 20.sp,
                fontWeight = FontWeight.Bold,
                textAlign = TextAlign.Center
            )
            Text(
                text = "Are you sure you want to logout?",
                modifier = Modifier.padding(8.dp, 2.dp, 8.dp, 16.dp)
                    .align(Alignment.CenterHorizontally).fillMaxWidth(),
                textAlign = TextAlign.Center
            )
            Divider(color = Color.Gray, modifier = Modifier.fillMaxWidth().width(1.dp))
            Row(Modifier.padding(top = 0.dp)) {
                CompositionLocalProvider(
                    LocalMinimumTouchTargetEnforcement provides false,
                ) {
                    TextButton(
                        onClick = { onDismiss() },
                        Modifier
                            .fillMaxWidth()
                            .padding(0.dp)
                            .weight(1F)
                            .border(0.dp, Color.Transparent)
                            .height(48.dp),
                        elevation = ButtonDefaults.elevation(0.dp, 0.dp),
                        shape = RoundedCornerShape(0.dp),
                        contentPadding = PaddingValues(0.dp)
                    ) {
                        Text(text = "Cancel", color = Color.Blue)
                    }
                }
                Divider(color = Color.Gray, modifier = 
                Modifier.fillMaxHeight().width(1.dp))
                CompositionLocalProvider(
                    LocalMinimumTouchTargetEnforcement provides false,
                ) {
                    TextButton(
                        onClick = {
                         onExit.invoke()
                        },
                        Modifier
                            .fillMaxWidth()
                            .padding(0.dp)
                            .weight(1F)
                            .border(0.dp, color = Color.Transparent)
                            .height(48.dp),
                        elevation = ButtonDefaults.elevation(0.dp, 0.dp),
                        shape = RoundedCornerShape(0.dp),
                        contentPadding = PaddingValues()
                    ) {
                        Text(text = "Logout", color = Color.Red)
                    }
                }
            }
        }
    }
}
}

并且可以在任何地方组合使用!

CustomAlertDialog(
        onDismiss = {
            /*do something*/
        },
        onExit = {
            /*do something*/
        })

0
如果你像我一样在Compose中苦于对话框的工作原理以及如何与View Model一起使用,让我来解释一下。
1. 实际上,在哪里放置显示对话框的逻辑? 答案是,只要在代码中可以访问到它,它放在哪里都无关紧要,你就可以看到你的对话框。
2. 它会是一个弹出窗口,还是我需要手动将其覆盖在现有的界面上? 答案是,它将是一个对话框,不是你现有界面的一部分。
3. 如何在ViewModel中管理状态? 先生,你来对地方了,请看下面的示例:
使用ViewModel的示例
活动(Activity)

class MainActivity : ComponentActivity() {

    private val viewModel by lazy { MainViewModel() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourAppTheme {
                HomeScreen(viewModel)
            }
        }
    }
}

主屏幕界面


@Composable
fun HomeScreen(signUpViewModel: SignUpViewModel) {


    Scaffold(
    ) { pd ->
       //ignore
        pd.calculateBottomPadding() 
        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(BackgroundColor)
                .wrapContentSize(Alignment.Center)
                .padding(20.dp)
        ) {

            Spacer(modifier = Modifier.weight(1.0f)) // fill height with spacer
            Text(
                text = "HomeUI",
                fontSize = 20.sp,
                fontWeight = FontWeight(700),
                color = Color(0xFF344054),
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .align(alignment = CenterHorizontally)
                    .padding(0.dp)
            )
            Spacer(modifier = Modifier.fillMaxHeight(.1f))
            
            CommonButton(buttonLabel = "Capture") {
                signUpViewModel.showDialog.value = !signUpViewModel.showDialog.value
                Log.e("error", "showDialog.value ${signUpViewModel.showDialog.value}")
            }
            if(signUpViewModel.showDialog.collectAsState().value)
                CustomDialog(signUpViewModel = signUpViewModel)

            Spacer(modifier = Modifier.weight(1.0f)) // fill height with spacer

        }
    }
}


对话界面: 毫不羞耻地从这里复制:SO答案


@Composable
fun CustomDialog(signUpViewModel: SignUpViewModel) {
    Dialog(onDismissRequest = { signUpViewModel.showDialog.value = false }) {
        CustomDialogUI(signUpViewModel = signUpViewModel)
    }
}

//Layout
@Composable
fun CustomDialogUI(modifier: Modifier = Modifier, signUpViewModel: SignUpViewModel) {
    Card(
        //shape = MaterialTheme.shapes.medium,
        shape = RoundedCornerShape(10.dp),
        // modifier = modifier.size(280.dp, 240.dp)
        modifier = Modifier.padding(10.dp, 5.dp, 10.dp, 10.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 20.dp)
//        elevation = Shadow
    ) {
        Column(
            modifier.background(Color.White)
        ) {

            //.......................................................................
            Image(
                painter = painterResource(id = R.drawable.ic_home),
                contentDescription = null, // decorative
                contentScale = ContentScale.Fit,
                colorFilter = ColorFilter.tint(
                    color = PrimaryGreen
                ),
                modifier = Modifier
                    .padding(top = 35.dp)
                    .height(70.dp)
                    .fillMaxWidth(),

                )

            Column(modifier = Modifier.padding(16.dp)) {
                androidx.compose.material3.Text(
                    text = "Get Updates",
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .padding(top = 5.dp)
                        .fillMaxWidth(),
                    style = MaterialTheme.typography.labelLarge,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis
                )
                androidx.compose.material3.Text(
                    text = "Allow Permission to send you notifications when new art styles added.",
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .padding(top = 10.dp, start = 25.dp, end = 25.dp)
                        .fillMaxWidth(),
                    style = MaterialTheme.typography.bodyMedium
                )
            }
            //.......................................................................
            Row(
                Modifier
                    .fillMaxWidth()
                    .padding(top = 10.dp)
                    .background(PrimaryGreen),
                horizontalArrangement = Arrangement.SpaceAround
            ) {

                androidx.compose.material3.TextButton(onClick = {
                    signUpViewModel.showDialog.value = false
                }) {

                    androidx.compose.material3.Text(
                        "Not Now",
                        fontWeight = FontWeight.Bold,
                        color = PrimaryGreen,
                        modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
                    )
                }
                androidx.compose.material3.TextButton(onClick = {
                    signUpViewModel.showDialog.value = false
                }) {
                    androidx.compose.material3.Text(
                        "Allow",
                        fontWeight = FontWeight.ExtraBold,
                        color = Color.Black,
                        modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
                    )
                }
            }
        }
    }
}

ViewModel:

class MainViewModel : ViewModel() { }
var showDialog  = MutableStateFlow(false)

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接