👀
【超雑記】キーボードに対していい感じに動作するダイアログを作成する
普通にダイアログを使うと隠れてしまう・・・
こんな感じに動作させる
実装
コード
@Composable
fun GrowDialog(
expanded: Boolean,
onDismissRequest: () -> Unit,
properties: GrowDialogProperties = GrowDialogProperties(),
content: @Composable () -> Unit,
) {
if (expanded) {
Dialog(
onDismissRequest = onDismissRequest,
properties = properties.properties
) {
Column(
modifier = Modifier
.fillMaxHeight()
.then(
if (properties.dismissOnClickOutside) {
Modifier.clickable(
onClick = onDismissRequest,
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
} else Modifier
),
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally
) {
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
var bottomSpaceHeight by remember { mutableStateOf(screenHeight / 3) }
val imeHeight = WindowInsets.ime.asPaddingValues().calculateBottomPadding()
if (imeHeight > bottomSpaceHeight) { // imeHeight > screenHeight / 3 とすると高さは記憶されない
bottomSpaceHeight = imeHeight
}
Surface(
modifier = Modifier
.heightIn(max = screenHeight - bottomSpaceHeight)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { /* do nothing */ },
),
content = content
)
Spacer(modifier = Modifier.height(bottomSpaceHeight))
}
}
}
}
/*
* : DialogProperties by DialogProperties(decorFitsSystemWindows: Boolean = false)
*/
class GrowDialogProperties(
dismissOnBackPress: Boolean = true,
dismissOnClickOutside: Boolean = true,
securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
usePlatformDefaultWidth: Boolean = true,
decorFitsSystemWindows: Boolean = false
) {
private val dialogProperties = DialogProperties(
dismissOnBackPress = dismissOnBackPress,
dismissOnClickOutside = dismissOnClickOutside,
securePolicy = securePolicy,
usePlatformDefaultWidth = usePlatformDefaultWidth,
decorFitsSystemWindows = decorFitsSystemWindows
)
val properties: DialogProperties get() = dialogProperties
val dismissOnBackPress: Boolean get() = dialogProperties.dismissOnBackPress
val dismissOnClickOutside: Boolean get() = dialogProperties.dismissOnClickOutside
val securePolicy: SecureFlagPolicy get() = dialogProperties.securePolicy
val usePlatformDefaultWidth: Boolean get() = dialogProperties.usePlatformDefaultWidth
val decorFitsSystemWindows: Boolean get() = dialogProperties.decorFitsSystemWindows
}
// 呼び出し側
@Composable
fun GrowDialogSample() {
var shown by remember { mutableStateOf(true) }
GrowDialog(
expanded = shown,
onDismissRequest = { shown = false },
) {
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = it },
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
)
}
内容
- 透明なダイアログを画面全体に広げ、キーボードに合わせて
Spacer
とheightIn
によってレイアウトを制御 -
decorFitsSystemWindows: Boolean = false
とする必要があるため、DialogProperties
をラップしたGrowDialogProperties
を用意 - その他、クリックイベントなどのあれこれを対策
おまけ
TextFieldの上下に他の要素を付けたい場合
weight
で制約をかければ良い
@Composable
fun GrowDialogSample() {
GrowDialog(
expanded = shown,
onDismissRequest = { shown = false }
) {
Column(modifier = Modifier.fillMaxWidth()) {
var text by remember { mutableStateOf("") }
Text(
text = "Keyboard Height Test",
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier
.height(80.dp)
)
OutlinedTextField(
value = text,
onValueChange = {
text = it
},
placeholder = { Text("入力してください") },
modifier = Modifier
.fillMaxWidth()
.weight(weight = 1f, fill = false)
.padding(8.dp),
minLines = 2,
maxLines = Int.MAX_VALUE
)
TextButton(
onClick = { shown = false },
modifier = Modifier
.align(Alignment.End)
.background(MaterialTheme.colorScheme.background),
) {
Text(text = "閉じる")
}
}
}
}
キーボードに関係なく画面からはみ出なくしたい場合
screenHeightDp
で制限をかければよい
@Composable
fun BasicDialog(
expanded: Boolean,
onDismissRequest: () -> Unit,
properties: DialogProperties = DialogProperties(),
content: @Composable () -> Unit,
) {
if (expanded) {
Dialog(
onDismissRequest = onDismissRequest,
properties = properties
) {
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
Surface(
modifier = Modifier
.heightIn(max = screenHeight)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { /* do nothing */ },
),
content = content
)
}
}
}
Discussion