Replace two-button image picker with bottom sheet chooser
Single "Add photo" button opens a ModalBottomSheet with camera and gallery options, matching standard mobile UX patterns. Switches to material-icons-extended for CameraAlt/PhotoLibrary icons. Button label changes to "Change photo" once an image is selected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9f660d50bc
commit
19d8899057
2 changed files with 57 additions and 6 deletions
|
|
@ -44,7 +44,7 @@ dependencies {
|
|||
implementation platform('androidx.compose:compose-bom:2025.05.01')
|
||||
implementation 'androidx.activity:activity-compose:1.10.1'
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation 'androidx.compose.material:material-icons-core'
|
||||
implementation 'androidx.compose.material:material-icons-extended'
|
||||
implementation 'androidx.compose.ui:ui'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
package com.danvics.calorieai.ui
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CameraAlt
|
||||
import androidx.compose.material.icons.filled.PhotoLibrary
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.danvics.calorieai.data.MealEntry
|
||||
|
|
@ -13,6 +18,7 @@ import java.time.format.DateTimeFormatter
|
|||
|
||||
private val MEAL_TYPES = listOf("Breakfast", "Lunch", "Dinner", "Snack", "Other")
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LogMealScreen(
|
||||
editing: MealEntry?,
|
||||
|
|
@ -31,6 +37,36 @@ fun LogMealScreen(
|
|||
var description by remember(editing?.id) { mutableStateOf(editing?.description ?: "") }
|
||||
var measure by remember(editing?.id) { mutableStateOf(editing?.measure ?: "") }
|
||||
var estimate by remember(editing?.id) { mutableStateOf(editing?.estimate ?: NutritionEstimate()) }
|
||||
var showImageSheet by remember { mutableStateOf(false) }
|
||||
|
||||
if (showImageSheet) {
|
||||
ModalBottomSheet(onDismissRequest = { showImageSheet = false }) {
|
||||
Column(Modifier.padding(bottom = 32.dp)) {
|
||||
Text(
|
||||
"Add photo",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
)
|
||||
HorizontalDivider()
|
||||
ListItem(
|
||||
headlineContent = { Text("Take photo") },
|
||||
leadingContent = { Icon(Icons.Default.CameraAlt, contentDescription = null) },
|
||||
modifier = Modifier.clickable {
|
||||
showImageSheet = false
|
||||
onTakePhoto()
|
||||
}
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text("Choose from gallery") },
|
||||
leadingContent = { Icon(Icons.Default.PhotoLibrary, contentDescription = null) },
|
||||
modifier = Modifier.clickable {
|
||||
showImageSheet = false
|
||||
onPickImage()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SectionCard("Meal details") {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
|
|
@ -40,12 +76,27 @@ fun LogMealScreen(
|
|||
DropdownField("Meal type", MEAL_TYPES, mealType, { mealType = it })
|
||||
Field("Description", description, { description = it }, singleLine = false, placeholder = "e.g. grilled salmon, rice, broccoli")
|
||||
Field("Portion or measure", measure, { measure = it }, placeholder = "e.g. one plate, 450g")
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
|
||||
OutlinedButton(onClick = onTakePhoto, modifier = Modifier.weight(1f)) { Text("Take photo") }
|
||||
OutlinedButton(onClick = onPickImage, modifier = Modifier.weight(1f)) { Text("Choose image") }
|
||||
OutlinedButton(
|
||||
onClick = { showImageSheet = true },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
if (selectedImageName.isNotBlank()) Icons.Default.CameraAlt else Icons.Default.PhotoLibrary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp).padding(end = 0.dp)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(if (selectedImageName.isNotBlank()) "Change photo" else "Add photo")
|
||||
}
|
||||
if (selectedImageName.isNotBlank()) {
|
||||
Text("Image: $selectedImageName", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
selectedImageName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
enabled = !busy && (description.isNotBlank() || selectedImageName.isNotBlank()),
|
||||
|
|
@ -56,7 +107,7 @@ fun LogMealScreen(
|
|||
Text(
|
||||
status,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = if (status.startsWith("Analysis failed") || status.startsWith("Set API")) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary
|
||||
color = if (status.startsWith("Analysis failed") || status.startsWith("No AI")) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue