Skip to content

Commit e273661

Browse files
authored
Date pickers (#302)
* Reworking the time picker examples to be more friendly to copy-pasting * Apply Spotless * Updating the time picker examples to be more copy-pastable * Change in-use tag name back to the current form. * Apply Spotless * Renaming region tags * Renaming region tags * Adding date picker snippets * Apply Spotless * fixing region tags * Adding additional range tags --------- Co-authored-by: jakeroseman <[email protected]>
1 parent 1a2f369 commit e273661

File tree

3 files changed

+318
-0
lines changed

3 files changed

+318
-0
lines changed

compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import com.example.compose.snippets.components.ButtonExamples
3434
import com.example.compose.snippets.components.CheckboxExamples
3535
import com.example.compose.snippets.components.ChipExamples
3636
import com.example.compose.snippets.components.ComponentsScreen
37+
import com.example.compose.snippets.components.DatePickerExamples
3738
import com.example.compose.snippets.components.DialogExamples
3839
import com.example.compose.snippets.components.DividerExamples
3940
import com.example.compose.snippets.components.FloatingActionButtonExamples
@@ -105,6 +106,7 @@ class SnippetsActivity : ComponentActivity() {
105106
TopComponentsDestination.BadgeExamples -> BadgeExamples()
106107
TopComponentsDestination.PartialBottomSheet -> PartialBottomSheet()
107108
TopComponentsDestination.TimePickerExamples -> TimePickerExamples()
109+
TopComponentsDestination.DatePickerExamples -> DatePickerExamples()
108110
}
109111
}
110112
}
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.compose.snippets.components
18+
19+
import androidx.compose.foundation.background
20+
import androidx.compose.foundation.layout.Arrangement
21+
import androidx.compose.foundation.layout.Box
22+
import androidx.compose.foundation.layout.Column
23+
import androidx.compose.foundation.layout.fillMaxSize
24+
import androidx.compose.foundation.layout.fillMaxWidth
25+
import androidx.compose.foundation.layout.height
26+
import androidx.compose.foundation.layout.offset
27+
import androidx.compose.foundation.layout.padding
28+
import androidx.compose.material.icons.Icons
29+
import androidx.compose.material.icons.filled.DateRange
30+
import androidx.compose.material3.Button
31+
import androidx.compose.material3.DatePicker
32+
import androidx.compose.material3.DatePickerDialog
33+
import androidx.compose.material3.DateRangePicker
34+
import androidx.compose.material3.DisplayMode
35+
import androidx.compose.material3.ExperimentalMaterial3Api
36+
import androidx.compose.material3.Icon
37+
import androidx.compose.material3.IconButton
38+
import androidx.compose.material3.MaterialTheme
39+
import androidx.compose.material3.OutlinedTextField
40+
import androidx.compose.material3.Text
41+
import androidx.compose.material3.TextButton
42+
import androidx.compose.material3.rememberDatePickerState
43+
import androidx.compose.material3.rememberDateRangePickerState
44+
import androidx.compose.runtime.Composable
45+
import androidx.compose.runtime.getValue
46+
import androidx.compose.runtime.mutableStateOf
47+
import androidx.compose.runtime.remember
48+
import androidx.compose.runtime.setValue
49+
import androidx.compose.ui.Alignment
50+
import androidx.compose.ui.Modifier
51+
import androidx.compose.ui.draw.shadow
52+
import androidx.compose.ui.unit.dp
53+
import androidx.compose.ui.window.Popup
54+
import java.text.SimpleDateFormat
55+
import java.util.Date
56+
import java.util.Locale
57+
58+
// [START android_compose_components_datepicker_examples]
59+
// [START_EXCLUDE]
60+
@Composable
61+
fun DatePickerExamples() {
62+
var showModal by remember { mutableStateOf(false) }
63+
var showModalInput by remember { mutableStateOf(false) }
64+
var showRangeModal by remember { mutableStateOf(false) }
65+
// [END_EXCLUDE]
66+
var selectedDate by remember { mutableStateOf<Long?>(null) }
67+
// [START_EXCLUDE]
68+
var selectedDateRange by remember { mutableStateOf<Pair<Long?, Long?>>(null to null) }
69+
70+
Column(
71+
modifier = Modifier
72+
.fillMaxSize()
73+
.padding(16.dp),
74+
horizontalAlignment = Alignment.CenterHorizontally,
75+
verticalArrangement = Arrangement.spacedBy(16.dp)
76+
) {
77+
Text("Docked date picker:")
78+
DatePickerDocked()
79+
80+
Text("Modal date pickers:")
81+
Button(onClick = { showModal = true }) {
82+
Text("Show Modal Date Picker")
83+
}
84+
Button(onClick = { showModalInput = true }) {
85+
Text("Show Modal Input Date Picker")
86+
}
87+
// [END_EXCLUDE]
88+
if (selectedDate != null) {
89+
val date = Date(selectedDate!!)
90+
val formattedDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(date)
91+
Text("Selected date: $formattedDate")
92+
} else {
93+
Text("No date selected")
94+
}
95+
// [START_EXCLUDE]
96+
97+
Text("Date range pickers:")
98+
99+
Button(onClick = { showRangeModal = true }) {
100+
Text("Show Modal Date Range Picker")
101+
}
102+
103+
if (selectedDateRange.first != null && selectedDateRange.second != null) {
104+
val startDate = Date(selectedDateRange.first!!)
105+
val endDate = Date(selectedDateRange.second!!)
106+
val formattedStartDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(startDate)
107+
val formattedEndDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(endDate)
108+
Text("Selected date range: $formattedStartDate - $formattedEndDate")
109+
} else {
110+
Text("No date range selected")
111+
}
112+
}
113+
114+
if (showModal) {
115+
// [END_EXCLUDE]
116+
DatePickerModal(
117+
onDateSelected = {
118+
selectedDate = it
119+
showModal = false
120+
},
121+
onDismiss = { showModal = false }
122+
)
123+
}
124+
// [START_EXCLUDE]
125+
126+
if (showModalInput) {
127+
DatePickerModalInput(
128+
onDateSelected = {
129+
selectedDate = it
130+
showModalInput = false
131+
},
132+
onDismiss = { showModalInput = false }
133+
)
134+
}
135+
136+
if (showRangeModal) {
137+
DateRangePickerModal(
138+
onDateRangeSelected = {
139+
selectedDateRange = it
140+
showRangeModal = false
141+
},
142+
onDismiss = { showRangeModal = false }
143+
)
144+
}
145+
}
146+
// [END android_compose_components_datepicker_examples]
147+
148+
@OptIn(ExperimentalMaterial3Api::class)
149+
// [START android_compose_components_datepicker_modal]
150+
@Composable
151+
fun DatePickerModal(
152+
onDateSelected: (Long?) -> Unit,
153+
onDismiss: () -> Unit
154+
) {
155+
val datePickerState = rememberDatePickerState()
156+
157+
DatePickerDialog(
158+
onDismissRequest = onDismiss,
159+
confirmButton = {
160+
TextButton(onClick = {
161+
onDateSelected(datePickerState.selectedDateMillis)
162+
onDismiss()
163+
}) {
164+
Text("OK")
165+
}
166+
},
167+
dismissButton = {
168+
TextButton(onClick = onDismiss) {
169+
Text("Cancel")
170+
}
171+
}
172+
) {
173+
DatePicker(state = datePickerState)
174+
}
175+
}
176+
// [END android_compose_components_datepicker_modal]
177+
178+
@OptIn(ExperimentalMaterial3Api::class)
179+
// [START android_compose_components_datepicker_inputmodal]
180+
@Composable
181+
fun DatePickerModalInput(
182+
onDateSelected: (Long?) -> Unit,
183+
onDismiss: () -> Unit
184+
) {
185+
val datePickerState = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
186+
187+
DatePickerDialog(
188+
onDismissRequest = onDismiss,
189+
confirmButton = {
190+
TextButton(onClick = {
191+
onDateSelected(datePickerState.selectedDateMillis)
192+
onDismiss()
193+
}) {
194+
Text("OK")
195+
}
196+
},
197+
dismissButton = {
198+
TextButton(onClick = onDismiss) {
199+
Text("Cancel")
200+
}
201+
}
202+
) {
203+
DatePicker(state = datePickerState)
204+
}
205+
}
206+
// [END android_compose_components_datepicker_inputmodal]
207+
208+
@OptIn(ExperimentalMaterial3Api::class)
209+
// [START android_compose_components_datepicker_docked]
210+
@Composable
211+
fun DatePickerDocked() {
212+
var showDatePicker by remember { mutableStateOf(false) }
213+
val datePickerState = rememberDatePickerState()
214+
val selectedDate = datePickerState.selectedDateMillis?.let {
215+
convertMillisToDate(it)
216+
} ?: ""
217+
218+
Box(
219+
modifier = Modifier.fillMaxWidth()
220+
) {
221+
OutlinedTextField(
222+
value = selectedDate,
223+
onValueChange = { },
224+
label = { Text("DOB") },
225+
readOnly = true,
226+
trailingIcon = {
227+
IconButton(onClick = { showDatePicker = !showDatePicker }) {
228+
Icon(
229+
imageVector = Icons.Default.DateRange,
230+
contentDescription = "Select date"
231+
)
232+
}
233+
},
234+
modifier = Modifier
235+
.fillMaxWidth()
236+
.height(64.dp)
237+
)
238+
239+
if (showDatePicker) {
240+
Popup(
241+
onDismissRequest = { showDatePicker = false },
242+
alignment = Alignment.TopStart
243+
) {
244+
Box(
245+
modifier = Modifier
246+
.fillMaxWidth()
247+
.offset(y = 64.dp)
248+
.shadow(elevation = 4.dp)
249+
.background(MaterialTheme.colorScheme.surface)
250+
.padding(16.dp)
251+
) {
252+
DatePicker(
253+
state = datePickerState,
254+
showModeToggle = false
255+
)
256+
}
257+
}
258+
}
259+
}
260+
}
261+
262+
fun convertMillisToDate(millis: Long): String {
263+
val formatter = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault())
264+
return formatter.format(Date(millis))
265+
}
266+
// [END android_compose_components_datepicker_docked]
267+
268+
@OptIn(ExperimentalMaterial3Api::class)
269+
// [START android_compose_components_datepicker_range]
270+
@Composable
271+
fun DateRangePickerModal(
272+
onDateRangeSelected: (Pair<Long?, Long?>) -> Unit,
273+
onDismiss: () -> Unit
274+
) {
275+
val dateRangePickerState = rememberDateRangePickerState()
276+
277+
DatePickerDialog(
278+
onDismissRequest = onDismiss,
279+
confirmButton = {
280+
TextButton(
281+
onClick = {
282+
onDateRangeSelected(
283+
Pair(
284+
dateRangePickerState.selectedStartDateMillis,
285+
dateRangePickerState.selectedEndDateMillis
286+
)
287+
)
288+
onDismiss()
289+
}
290+
) {
291+
Text("OK")
292+
}
293+
},
294+
dismissButton = {
295+
TextButton(onClick = onDismiss) {
296+
Text("Cancel")
297+
}
298+
}
299+
) {
300+
DateRangePicker(
301+
state = dateRangePickerState,
302+
title = {
303+
Text(
304+
text = "Select date range"
305+
)
306+
},
307+
showModeToggle = false,
308+
modifier = Modifier
309+
.fillMaxWidth()
310+
.height(500.dp)
311+
.padding(16.dp)
312+
)
313+
}
314+
}
315+
// [END android_compose_components_datepicker_range]

compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ enum class TopComponentsDestination(val route: String, val title: String) {
4242
BadgeExamples("badgeExamples", "Badges"),
4343
PartialBottomSheet("partialBottomSheets", "Partial Bottom Sheet"),
4444
TimePickerExamples("timePickerExamples", "Time Pickers"),
45+
DatePickerExamples("datePickerExamples", "Date Pickers"),
4546
}

0 commit comments

Comments
 (0)