Consistent labeled checkbox composable

This commit is contained in:
arkon 2023-10-21 09:42:12 -04:00
parent 8626a55fe4
commit c53172265b
8 changed files with 151 additions and 216 deletions

View file

@ -1,12 +1,8 @@
package eu.kanade.presentation.history.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -14,12 +10,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
@ -34,28 +29,16 @@ fun HistoryDeleteDialog(
Text(text = stringResource(R.string.action_remove))
},
text = {
Column {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(text = stringResource(R.string.dialog_with_checkbox_remove_description))
Row(
modifier = Modifier
.padding(top = 16.dp)
.toggleable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
value = removeEverything,
onValueChange = { removeEverything = it },
),
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(
checked = removeEverything,
onCheckedChange = null,
)
Text(
modifier = Modifier.padding(start = 4.dp),
text = stringResource(R.string.dialog_with_checkbox_reset),
)
}
LabeledCheckbox(
label = stringResource(R.string.dialog_with_checkbox_reset),
checked = removeEverything,
onCheckedChange = { removeEverything = it },
)
}
},
onDismissRequest = onDismissRequest,

View file

@ -1,11 +1,7 @@
package eu.kanade.presentation.library
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -13,11 +9,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.tachiyomi.R
import tachiyomi.core.preference.CheckboxState
import tachiyomi.presentation.core.components.LabeledCheckbox
@Composable
fun DeleteLibraryMangaDialog(
@ -62,27 +57,18 @@ fun DeleteLibraryMangaDialog(
text = {
Column {
list.forEach { state ->
val onCheck = {
val index = list.indexOf(state)
if (index != -1) {
val mutableList = list.toMutableList()
mutableList[index] = state.next() as CheckboxState.State<Int>
list = mutableList.toList()
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onCheck() },
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(
checked = state.isChecked,
onCheckedChange = { onCheck() },
)
Text(text = stringResource(state.value))
}
LabeledCheckbox(
label = stringResource(state.value),
checked = state.isChecked,
onCheckedChange = {
val index = list.indexOf(state)
if (index != -1) {
val mutableList = list.toMutableList()
mutableList[index] = state.next() as CheckboxState.State<Int>
list = mutableList.toList()
}
},
)
}
}
},

View file

@ -1,16 +1,12 @@
package eu.kanade.presentation.manga
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -19,7 +15,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -30,6 +25,7 @@ import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.tachiyomi.R
import tachiyomi.core.preference.TriState
import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.RadioItem
import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TriStateItem
@ -172,6 +168,7 @@ private fun SetAsDefaultDialog(
onConfirmed: (optionalChecked: Boolean) -> Unit,
) {
var optionalChecked by rememberSaveable { mutableStateOf(false) }
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(text = stringResource(R.string.chapter_settings)) },
@ -181,20 +178,11 @@ private fun SetAsDefaultDialog(
) {
Text(text = stringResource(R.string.confirm_set_chapter_settings))
Row(
modifier = Modifier
.clickable { optionalChecked = !optionalChecked }
.padding(vertical = 8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(
checked = optionalChecked,
onCheckedChange = null,
)
Text(text = stringResource(R.string.also_set_chapter_settings_for_library))
}
LabeledCheckbox(
label = stringResource(R.string.also_set_chapter_settings_for_library),
checked = optionalChecked,
onCheckedChange = { optionalChecked = it },
)
}
},
dismissButton = {

View file

@ -8,20 +8,13 @@ import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -38,7 +31,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import com.hippo.unifile.UniFile
import eu.kanade.presentation.extensions.RequestStoragePermission
@ -55,6 +47,7 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.launch
import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.util.collectAsState
import tachiyomi.presentation.core.util.isScrolledToEnd
@ -160,22 +153,23 @@ object SettingsBackupScreen : SearchableSettings {
val state = rememberLazyListState()
ScrollbarLazyColumn(state = state) {
item {
CreateBackupDialogItem(
isSelected = true,
title = stringResource(R.string.manga),
LabeledCheckbox(
label = stringResource(R.string.manga),
checked = true,
onCheckedChange = {},
)
}
choices.forEach { (k, v) ->
item {
val isSelected = flags.contains(k)
CreateBackupDialogItem(
isSelected = isSelected,
title = stringResource(v),
modifier = Modifier.clickable {
if (isSelected) {
flags.remove(k)
} else {
LabeledCheckbox(
label = stringResource(v),
checked = isSelected,
onCheckedChange = {
if (it) {
flags.add(k)
} else {
flags.remove(k)
}
},
)
@ -204,29 +198,6 @@ object SettingsBackupScreen : SearchableSettings {
)
}
@Composable
private fun CreateBackupDialogItem(
modifier: Modifier = Modifier,
isSelected: Boolean,
title: String,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier.fillMaxWidth(),
) {
Checkbox(
modifier = Modifier.heightIn(min = 48.dp),
checked = isSelected,
onCheckedChange = null,
)
Text(
text = title,
style = MaterialTheme.typography.bodyMedium.merge(),
modifier = Modifier.padding(start = 24.dp),
)
}
}
@Composable
private fun getRestoreBackupPref(): Preference.PreferenceItem.TextPreference {
val context = LocalContext.current

View file

@ -1,30 +1,20 @@
package eu.kanade.presentation.more.settings.widget
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.selection.selectable
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.LabeledCheckbox
@Composable
fun MultiSelectListPreferenceWidget(
@ -55,33 +45,17 @@ fun MultiSelectListPreferenceWidget(
preference.entries.forEach { current ->
item {
val isSelected = selected.contains(current.key)
val onSelectionChanged = {
when (!isSelected) {
true -> selected.add(current.key)
false -> selected.remove(current.key)
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(MaterialTheme.shapes.small)
.selectable(
selected = isSelected,
onClick = { onSelectionChanged() },
)
.minimumInteractiveComponentSize()
.fillMaxWidth(),
) {
Checkbox(
checked = isSelected,
onCheckedChange = null,
)
Text(
text = current.value,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(start = 24.dp),
)
}
LabeledCheckbox(
label = current.value,
checked = isSelected,
onCheckedChange = {
if (it) {
selected.add(current.key)
} else {
selected.remove(current.key)
}
},
)
}
}
}

View file

@ -1,17 +1,13 @@
package eu.kanade.tachiyomi.ui.browse.migration.search
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -22,7 +18,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@ -55,6 +50,7 @@ import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.screens.LoadingScreen
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -92,16 +88,11 @@ internal fun MigrateDialog(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
flags.forEachIndexed { index, flag ->
val onChange = { selectedFlags[index] = !selectedFlags[index] }
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onChange),
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(checked = selectedFlags[index], onCheckedChange = { onChange() })
Text(text = context.getString(flag.titleId))
}
LabeledCheckbox(
label = stringResource(flag.titleId),
checked = selectedFlags[index],
onCheckedChange = { selectedFlags[index] = it },
)
}
}
},

View file

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.app.Application
import android.content.Context
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -13,7 +12,6 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@ -33,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
@ -75,6 +74,7 @@ import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.DeleteTrack
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.model.Track
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.padding
import uy.kohesive.injekt.Injekt
@ -94,10 +94,10 @@ data class TrackInfoDialogHomeScreen(
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val context = LocalContext.current
val sm = rememberScreenModel { Model(mangaId, sourceId) }
val screenModel = rememberScreenModel { Model(mangaId, sourceId) }
val dateFormat = remember { UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()) }
val state by sm.state.collectAsState()
val state by screenModel.state.collectAsState()
TrackInfoDialogHome(
trackItems = state.trackItems,
@ -146,7 +146,7 @@ data class TrackInfoDialogHomeScreen(
},
onNewSearch = {
if (it.tracker is EnhancedTracker) {
sm.registerEnhancedTracking(it)
screenModel.registerEnhancedTracking(it)
} else {
navigator.push(
TrackerSearchScreen(
@ -261,19 +261,19 @@ private data class TrackStatusSelectorScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
track = track,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
val state by screenModel.state.collectAsState()
TrackStatusSelector(
selection = state.selection,
onSelectionChange = sm::setSelection,
selections = remember { sm.getSelections() },
onSelectionChange = screenModel::setSelection,
selections = remember { screenModel.getSelections() },
onConfirm = {
sm.setStatus()
screenModel.setStatus()
navigator.pop()
},
onDismissRequest = navigator::pop,
@ -314,20 +314,20 @@ private data class TrackChapterSelectorScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
track = track,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
val state by screenModel.state.collectAsState()
TrackChapterSelector(
selection = state.selection,
onSelectionChange = sm::setSelection,
range = remember { sm.getRange() },
onSelectionChange = screenModel::setSelection,
range = remember { screenModel.getRange() },
onConfirm = {
sm.setChapter()
screenModel.setChapter()
navigator.pop()
},
onDismissRequest = navigator::pop,
@ -373,20 +373,20 @@ private data class TrackScoreSelectorScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
track = track,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
val state by screenModel.state.collectAsState()
TrackScoreSelector(
selection = state.selection,
onSelectionChange = sm::setSelection,
selections = remember { sm.getSelections() },
onSelectionChange = screenModel::setSelection,
selections = remember { screenModel.getSelections() },
onConfirm = {
sm.setScore()
screenModel.setScore()
navigator.pop()
},
onDismissRequest = navigator::pop,
@ -484,7 +484,7 @@ private data class TrackDateSelectorScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
track = track,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
@ -503,13 +503,13 @@ private data class TrackDateSelectorScreen(
} else {
stringResource(R.string.track_finished_reading_date)
},
initialSelectedDateMillis = sm.initialSelection,
initialSelectedDateMillis = screenModel.initialSelection,
selectableDates = selectableDates,
onConfirm = {
sm.setDate(it)
screenModel.setDate(it)
navigator.pop()
},
onRemove = { sm.confirmRemoveDate(navigator) }.takeIf { canRemove },
onRemove = { screenModel.confirmRemoveDate(navigator) }.takeIf { canRemove },
onDismissRequest = navigator::pop,
)
}
@ -557,7 +557,7 @@ private data class TrackDateRemoverScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
track = track,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
@ -579,7 +579,7 @@ private data class TrackDateRemoverScreen(
)
},
text = {
val serviceName = sm.getServiceName()
val serviceName = screenModel.getServiceName()
Text(
text = if (start) {
stringResource(R.string.track_remove_start_date_conf_text, serviceName)
@ -598,7 +598,7 @@ private data class TrackDateRemoverScreen(
}
FilledTonalButton(
onClick = {
sm.removeDate()
screenModel.removeDate()
navigator.popUntil { it is TrackInfoDialogHomeScreen }
},
colors = ButtonDefaults.filledTonalButtonColors(
@ -643,7 +643,7 @@ data class TrackerSearchScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
mangaId = mangaId,
currentUrl = currentUrl,
@ -652,18 +652,18 @@ data class TrackerSearchScreen(
)
}
val state by sm.state.collectAsState()
val state by screenModel.state.collectAsState()
var textFieldValue by remember { mutableStateOf(TextFieldValue(initialQuery)) }
TrackerSearch(
query = textFieldValue,
onQueryChange = { textFieldValue = it },
onDispatchQuery = { sm.trackingSearch(textFieldValue.text) },
onDispatchQuery = { screenModel.trackingSearch(textFieldValue.text) },
queryResult = state.queryResult,
selected = state.selected,
onSelectedChange = sm::updateSelection,
onSelectedChange = screenModel::updateSelection,
onConfirmSelection = {
sm.registerTracking(state.selected!!)
screenModel.registerTracking(state.selected!!)
navigator.pop()
},
onDismissRequest = navigator::pop,
@ -731,14 +731,14 @@ private data class TrackerRemoveScreen(
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val sm = rememberScreenModel {
val screenModel = rememberScreenModel {
Model(
mangaId = mangaId,
track = track,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val serviceName = sm.getName()
val serviceName = screenModel.getName()
var removeRemoteTrack by remember { mutableStateOf(false) }
AlertDialogContent(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@ -755,21 +755,19 @@ private data class TrackerRemoveScreen(
)
},
text = {
Column {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = stringResource(R.string.track_delete_text, serviceName),
)
if (sm.isDeletable()) {
val onChange = { removeRemoteTrack = !removeRemoteTrack }
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onChange),
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(checked = removeRemoteTrack, onCheckedChange = { onChange() })
Text(text = stringResource(R.string.track_delete_remote_text, serviceName))
}
if (screenModel.isDeletable()) {
LabeledCheckbox(
label = stringResource(R.string.track_delete_remote_text, serviceName),
checked = removeRemoteTrack,
onCheckedChange = { removeRemoteTrack = it },
)
}
}
},
@ -786,8 +784,8 @@ private data class TrackerRemoveScreen(
}
FilledTonalButton(
onClick = {
sm.unregisterTracking(serviceId)
if (removeRemoteTrack) sm.deleteMangaFromService()
screenModel.unregisterTracking(serviceId)
if (removeRemoteTrack) screenModel.deleteMangaFromService()
navigator.pop()
},
colors = ButtonDefaults.filledTonalButtonColors(

View file

@ -0,0 +1,44 @@
package tachiyomi.presentation.core.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
@Composable
fun LabeledCheckbox(
modifier: Modifier = Modifier,
label: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
) {
Row(
modifier = modifier
.clip(MaterialTheme.shapes.small)
.fillMaxWidth()
.heightIn(min = 48.dp)
.clickable(
role = Role.Checkbox,
onClick = { onCheckedChange(!checked) },
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Checkbox(
checked = checked,
onCheckedChange = null,
)
Text(text = label)
}
}