Compose Material 3のDatePickerを使ってみる
Compose Material 3のv1.1.0から導入されたDatePickerとDatePickerDialogを使う機会があったのでそれのまとめです。
導入
Gradleファイルに以下を追加します。
dependencies {
implementation("androidx.compose.material3:material3:1.1.0")
}
DatePicker
実際にDatePickerを使っていきます。
DatePickerは以下のような実装になっていました。
@ExperimentalMaterial3Api
@Composable
fun DatePicker(
state: DatePickerState,
modifier: Modifier = Modifier,
dateFormatter: DatePickerFormatter = remember { DatePickerFormatter() },
dateValidator: (Long) -> Boolean = { true },
title: (@Composable () -> Unit)? = {
DatePickerDefaults.DatePickerTitle(
state,
modifier = Modifier.padding(DatePickerTitlePadding)
)
},
headline: (@Composable () -> Unit)? = {
DatePickerDefaults.DatePickerHeadline(
state,
dateFormatter,
modifier = Modifier.padding(DatePickerHeadlinePadding)
)
},
showModeToggle: Boolean = true,
colors: DatePickerColors = DatePickerDefaults.colors()
) {
DateEntryContainer(
modifier = modifier,
title = title,
headline = headline,
modeToggleButton = if (showModeToggle) {
{
DisplayModeToggleButton(
modifier = Modifier.padding(DatePickerModeTogglePadding),
displayMode = state.displayMode,
onDisplayModeChange = { displayMode ->
state.stateData.switchDisplayMode(
displayMode
)
},
)
}
} else {
null
},
headlineTextStyle = MaterialTheme.typography.fromToken(
DatePickerModalTokens.HeaderHeadlineFont
),
headerMinHeight = DatePickerModalTokens.HeaderContainerHeight,
colors = colors
) {
SwitchableDateEntryContent(
state = state,
dateFormatter = dateFormatter,
dateValidator = dateValidator,
colors = colors
)
}
}
DatePickerStateはmustで渡さないといけないようになっており、このDatePickerStateで選択した日付の状態を持つことになります。
実際に呼び出すときは以下のような感じで呼び出して使用します。
@Composable
fun Material3DatePickerComponent() {
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = Instant.now().toEpochMilli()
)
DatePicker(state = datePickerState)
}
rememberDatePickerStateには、初期値を渡すことができるようになっています。
ここでは、現在の時間を渡すようにしています。
rememberDatePickerの内部を見てみると、rememberSaveableが使われており、画面回転などにも対応できるようになっているようです。
@Composable
@ExperimentalMaterial3Api
fun rememberDatePickerState(
@Suppress("AutoBoxing") initialSelectedDateMillis: Long? = null,
@Suppress("AutoBoxing") initialDisplayedMonthMillis: Long? = initialSelectedDateMillis,
yearRange: IntRange = DatePickerDefaults.YearRange,
initialDisplayMode: DisplayMode = DisplayMode.Picker
): DatePickerState = rememberSaveable(
saver = DatePickerState.Saver()
) {
DatePickerState(
initialSelectedDateMillis = initialSelectedDateMillis,
initialDisplayedMonthMillis = initialDisplayedMonthMillis,
yearRange = yearRange,
initialDisplayMode = initialDisplayMode
)
}
DatePickerDialog
次にDatePickerDialogを使ってみます。
DatePickerDialogは以下のような感じになっています。
@ExperimentalMaterial3Api
@Composable
fun DatePickerDialog(
onDismissRequest: () -> Unit,
confirmButton: @Composable () -> Unit,
modifier: Modifier = Modifier,
dismissButton: @Composable (() -> Unit)? = null,
shape: Shape = DatePickerDefaults.shape,
tonalElevation: Dp = DatePickerDefaults.TonalElevation,
colors: DatePickerColors = DatePickerDefaults.colors(),
properties: DialogProperties = DialogProperties(usePlatformDefaultWidth = false),
content: @Composable ColumnScope.() -> Unit
) {
AlertDialog(
onDismissRequest = onDismissRequest,
modifier = modifier.wrapContentHeight(),
properties = properties
) {
Surface(
modifier = Modifier
.requiredWidth(DatePickerModalTokens.ContainerWidth)
.heightIn(max = DatePickerModalTokens.ContainerHeight),
shape = shape,
color = colors.containerColor,
tonalElevation = tonalElevation,
) {
Column(verticalArrangement = Arrangement.SpaceBetween) {
content()
// Buttons
Box(
modifier = Modifier
.align(Alignment.End)
.padding(DialogButtonsPadding)
) {
CompositionLocalProvider(
LocalContentColor provides DialogTokens.ActionLabelTextColor.toColor()
) {
val textStyle =
MaterialTheme.typography.fromToken(DialogTokens.ActionLabelTextFont)
ProvideTextStyle(value = textStyle) {
AlertDialogFlowRow(
mainAxisSpacing = DialogButtonsMainAxisSpacing,
crossAxisSpacing = DialogButtonsCrossAxisSpacing
) {
dismissButton?.invoke()
confirmButton()
}
}
}
}
}
}
}
}
DatePickerDialogでは、onDismissRequest・confirmButtonの2つを渡す必要があります。
onDismissRequestは、DatePickerDialog外をタップした時に、どういう挙動をするのかを定義します。
confirmButtonは、DatePickerDialogを表示したときに、ダイアログの下の部分に表示するボタンの挙動を定義します。
実際に使うときは以下のように呼び出します。
@Composable
fun DatePickerComponent() {
var showPicker by remember { mutableStateOf(false) }
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = Instant.now().toEpochMilli()
)
if (showPicker) {
Material3DatePickerDialogComponent(
datePickerState = datePickerState,
closePicker = { showPicker = false },
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Material3DatePickerDialogComponent(
datePickerState: DatePickerState,
date: Instant?,
closePicker: () -> Unit,
setDate: (Instant) -> Unit,
modifier: Modifier = Modifier,
) {
DatePickerDialog(
onDismissRequest = {
closePicker()
},
confirmButton = {
TextButton(
onClick = {
datePickerState.setSelection(datePickerState.selectedDateMillis)
closePicker()
}
) {
Text(text = "OK")
}
},
dismissButton = {
TextButton(
onClick = {
closePicker()
}
) {
Text(text = "CANCEL")
}
},
modifier = modifier,
) {
DatePicker(state = datePickerState)
}
}
showPickerでDatePickerDialogを表示するかどうかを判断しています。
confirmButtonでは、datePickerState.setSelection(datePickerState.selectedDateMillis)を使って、選択した日付の状態を保存しています。
参考資料
Discussion