Use TextFieldState

This commit is contained in:
Phil Oliver 2025-11-18 03:26:25 -05:00
parent 92deae785e
commit 9b6cb4ee85
3 changed files with 18 additions and 35 deletions

View file

@ -28,8 +28,10 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Sort
import androidx.compose.material.icons.filled.Clear
@ -37,6 +39,7 @@ import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -53,7 +56,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
@ -82,7 +84,6 @@ import org.meshtastic.feature.node.list.NodeFilterState
fun NodeFilterTextField(
modifier: Modifier = Modifier,
filterState: NodeFilterState,
onTextChange: (String) -> Unit,
currentSortOption: NodeSortOption,
onSortSelect: (NodeSortOption) -> Unit,
onToggleIncludeUnknown: () -> Unit,
@ -94,11 +95,7 @@ fun NodeFilterTextField(
) {
Column(modifier = modifier.background(MaterialTheme.colorScheme.background)) {
Row {
NodeFilterTextField(
filterText = filterState.filterText,
onTextChange = onTextChange,
modifier = Modifier.weight(1f),
)
NodeFilterTextField(textState = filterState.filterText, modifier = Modifier.weight(1f))
NodeSortButton(
modifier = Modifier.align(Alignment.CenterVertically),
@ -140,14 +137,14 @@ fun NodeFilterTextField(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun NodeFilterTextField(filterText: String, onTextChange: (String) -> Unit, modifier: Modifier = Modifier) {
val focusManager = LocalFocusManager.current
private fun NodeFilterTextField(textState: TextFieldState, modifier: Modifier = Modifier) {
var isFocused by remember { mutableStateOf(false) }
OutlinedTextField(
modifier = modifier.defaultMinSize(minHeight = 48.dp).onFocusEvent { isFocused = it.isFocused },
value = filterText,
state = textState,
placeholder = {
Text(
text = stringResource(Res.string.node_filter_placeholder),
@ -158,24 +155,16 @@ private fun NodeFilterTextField(filterText: String, onTextChange: (String) -> Un
leadingIcon = {
Icon(Icons.Default.Search, contentDescription = stringResource(Res.string.node_filter_placeholder))
},
onValueChange = onTextChange,
trailingIcon = {
if (filterText.isNotEmpty() || isFocused) {
Icon(
Icons.Default.Clear,
contentDescription = stringResource(Res.string.desc_node_filter_clear),
modifier =
Modifier.clickable {
onTextChange("")
focusManager.clearFocus()
},
)
if (textState.text.isNotEmpty() || isFocused) {
IconButton(onClick = { textState.clearText() }) {
Icon(Icons.Default.Clear, contentDescription = stringResource(Res.string.desc_node_filter_clear))
}
}
},
textStyle = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onBackground),
maxLines = 1,
lineLimits = TextFieldLineLimits.SingleLine,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
)
}
@ -304,7 +293,6 @@ private fun NodeFilterTextFieldPreview() {
AppTheme {
NodeFilterTextField(
filterState = NodeFilterState(),
onTextChange = {},
currentSortOption = NodeSortOption.LAST_HEARD,
onSortSelect = {},
onToggleIncludeUnknown = {},

View file

@ -154,7 +154,6 @@ fun NodeListScreen(
.background(MaterialTheme.colorScheme.surfaceDim)
.padding(8.dp),
filterState = state.filter,
onTextChange = { viewModel.setFilterText(it) },
currentSortOption = state.sort,
onSortSelect = viewModel::setSortOption,
onToggleIncludeUnknown = { viewModel.nodeFilterPreferences.toggleIncludeUnknown() },

View file

@ -17,6 +17,7 @@
package org.meshtastic.feature.node.list
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -61,7 +62,7 @@ constructor(
private val _sharedContactRequested: MutableStateFlow<AdminProtos.SharedContact?> = MutableStateFlow(null)
val sharedContactRequested = _sharedContactRequested.asStateFlow()
private val filterText = savedStateHandle.getStateFlow(KEY_FILTER_TEXT, "")
private val filterText = mutableStateOf(TextFieldState())
private val moleculeScope = CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)
val uiState: StateFlow<NodesUiState> by
@ -71,7 +72,6 @@ constructor(
val onlineNodeCount by nodeRepository.onlineNodeCount.collectAsState(0)
val totalNodeCount by nodeRepository.totalNodeCount.collectAsState(0)
val connectionState by serviceRepository.connectionState.collectAsState()
val filterText by filterText
val includeUnknown by nodeFilterPreferences.includeUnknown.collectAsState()
val excludeInfrastructure by nodeFilterPreferences.excludeInfrastructure.collectAsState()
val onlyOnline by nodeFilterPreferences.onlyOnline.collectAsState()
@ -81,7 +81,7 @@ constructor(
val filter =
NodeFilterState(
filterText = filterText,
filterText = filterText.value,
includeUnknown = includeUnknown,
excludeInfrastructure = excludeInfrastructure,
onlyOnline = onlyOnline,
@ -97,7 +97,7 @@ constructor(
nodeRepository
.getNodes(
sort = sort,
filter = filter.filterText,
filter = filter.filterText.text.toString(),
includeUnknown = filter.includeUnknown,
onlyOnline = filter.onlyOnline,
onlyDirect = filter.onlyDirect,
@ -138,10 +138,6 @@ constructor(
}
}
fun setFilterText(filterText: String) {
savedStateHandle[KEY_FILTER_TEXT] = value
}
fun setSortOption(sort: NodeSortOption) {
nodeFilterPreferences.setNodeSort(sort)
}
@ -175,7 +171,7 @@ data class NodesUiState(
)
data class NodeFilterState(
val filterText: String = "",
val filterText: TextFieldState = TextFieldState(),
val includeUnknown: Boolean = false,
val excludeInfrastructure: Boolean = false,
val onlyOnline: Boolean = false,