mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 08:45:27 +02:00
Collapsable top bar for SecondScreen.kt
This commit is contained in:
parent
90780d66ef
commit
a37babb63d
@ -1,15 +1,17 @@
|
||||
package com.sadellie.unitto.screens.second
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.sadellie.unitto.R
|
||||
import com.sadellie.unitto.data.units.ALL_UNIT_GROUPS
|
||||
@ -38,53 +40,60 @@ fun SecondScreen(
|
||||
val chipsRowLazyListState = rememberLazyListState()
|
||||
val currentUnit = if (leftSide) viewModel.unitFrom else viewModel.unitTo
|
||||
var chosenUnitGroup: UnitGroup? by rememberSaveable { mutableStateOf(currentUnit.group) }
|
||||
val scrollBehavior: TopAppBarScrollBehavior = remember {
|
||||
TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
}
|
||||
|
||||
Column {
|
||||
SearchBar(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
title = stringResource(id = if (leftSide) R.string.units_screen_from else R.string.units_screen_to),
|
||||
value = searchQuery,
|
||||
onValueChange = {
|
||||
searchQuery = it
|
||||
viewModel.loadUnitsToShow(searchQuery, chosenUnitGroup, leftSide)
|
||||
},
|
||||
favoritesOnly = favoritesOnly,
|
||||
favoriteAction = {
|
||||
viewModel.toggleFavoritesOnly()
|
||||
viewModel.loadUnitsToShow(searchQuery, chosenUnitGroup, leftSide)
|
||||
},
|
||||
navigateUpAction = navigateUp,
|
||||
focusManager = focusManager
|
||||
)
|
||||
|
||||
if (leftSide) {
|
||||
ChipsRow(
|
||||
lazyListState = chipsRowLazyListState,
|
||||
items = ALL_UNIT_GROUPS,
|
||||
chosenUnitGroup = chosenUnitGroup,
|
||||
selectAction = {
|
||||
chosenUnitGroup = if (it == chosenUnitGroup) null else it
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
SearchBar(
|
||||
title = stringResource(id = if (leftSide) R.string.units_screen_from else R.string.units_screen_to),
|
||||
value = searchQuery,
|
||||
onValueChange = {
|
||||
searchQuery = it
|
||||
viewModel.loadUnitsToShow(searchQuery, chosenUnitGroup, leftSide)
|
||||
}
|
||||
)
|
||||
UnitsList(
|
||||
groupedUnits = unitsList,
|
||||
changeAction = { viewModel.changeUnitFrom(it); focusManager.clearFocus(true); navigateUp() },
|
||||
favoriteAction = { viewModel.favoriteUnit(it) },
|
||||
currentUnit = viewModel.unitFrom,
|
||||
)
|
||||
} else {
|
||||
UnitsList(
|
||||
groupedUnits = unitsList,
|
||||
changeAction = { viewModel.changeUnitTo(it); focusManager.clearFocus(true); navigateUp() },
|
||||
favoriteAction = { viewModel.favoriteUnit(it) },
|
||||
currentUnit = viewModel.unitTo,
|
||||
inputValue = viewModel.mainUIState.inputValue.toBigDecimal(),
|
||||
unitFrom = viewModel.unitFrom
|
||||
},
|
||||
favoritesOnly = favoritesOnly,
|
||||
favoriteAction = {
|
||||
viewModel.toggleFavoritesOnly()
|
||||
viewModel.loadUnitsToShow(searchQuery, chosenUnitGroup, leftSide)
|
||||
},
|
||||
navigateUpAction = navigateUp,
|
||||
focusManager = focusManager,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(modifier = Modifier.padding(paddingValues)) {
|
||||
if (leftSide) {
|
||||
ChipsRow(
|
||||
lazyListState = chipsRowLazyListState,
|
||||
items = ALL_UNIT_GROUPS,
|
||||
chosenUnitGroup = chosenUnitGroup,
|
||||
selectAction = {
|
||||
chosenUnitGroup = if (it == chosenUnitGroup) null else it
|
||||
viewModel.loadUnitsToShow(searchQuery, chosenUnitGroup, leftSide)
|
||||
}
|
||||
)
|
||||
UnitsList(
|
||||
groupedUnits = unitsList,
|
||||
changeAction = { viewModel.changeUnitFrom(it); focusManager.clearFocus(true); navigateUp() },
|
||||
favoriteAction = { viewModel.favoriteUnit(it) },
|
||||
currentUnit = viewModel.unitFrom,
|
||||
)
|
||||
} else {
|
||||
UnitsList(
|
||||
groupedUnits = unitsList,
|
||||
changeAction = { viewModel.changeUnitTo(it); focusManager.clearFocus(true); navigateUp() },
|
||||
favoriteAction = { viewModel.favoriteUnit(it) },
|
||||
currentUnit = viewModel.unitTo,
|
||||
inputValue = viewModel.mainUIState.inputValue.toBigDecimal(),
|
||||
unitFrom = viewModel.unitFrom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This block is called only once on initial composition
|
||||
|
@ -8,15 +8,12 @@ import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Clear
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
@ -33,7 +30,6 @@ import com.sadellie.unitto.R
|
||||
/**
|
||||
* Search bar on the Second screen. Controls what will be shown in the list above this component
|
||||
*
|
||||
* @param modifier Modifier to be applied to the Row that contains search bar elements
|
||||
* @param title Search bar title
|
||||
* @param value Current query
|
||||
* @param onValueChange Action to perform when search query changes
|
||||
@ -41,139 +37,149 @@ import com.sadellie.unitto.R
|
||||
* @param favoriteAction Function to toggle favorite filter
|
||||
* @param navigateUpAction Function to navigate to previous screen
|
||||
* @param focusManager Used to hide keyboard when leaving unit selection screen
|
||||
* @param scrollBehavior [TopAppBarScrollBehavior] that is used for collapsing and container color
|
||||
*/
|
||||
@Composable
|
||||
fun SearchBar(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String = String(),
|
||||
value: String = String(),
|
||||
onValueChange: (String) -> Unit = {},
|
||||
favoritesOnly: Boolean,
|
||||
favoriteAction: () -> Unit,
|
||||
navigateUpAction: () -> Unit = {},
|
||||
focusManager: FocusManager
|
||||
focusManager: FocusManager,
|
||||
scrollBehavior: TopAppBarScrollBehavior
|
||||
) {
|
||||
var showSearch by rememberSaveable { mutableStateOf(false) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
Crossfade(targetState = showSearch) { textFieldShown ->
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
when (textFieldShown) {
|
||||
// No search text field
|
||||
false -> {
|
||||
IconButton(onClick = navigateUpAction) {
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
// Search button
|
||||
IconButton(onClick = { onValueChange(""); showSearch = true }) {
|
||||
Icon(
|
||||
Icons.Default.Search,
|
||||
contentDescription = stringResource(id = R.string.search_button_description)
|
||||
)
|
||||
}
|
||||
// Favorites button
|
||||
IconButton(onClick = favoriteAction) {
|
||||
AnimatedContent(
|
||||
targetState = favoritesOnly,
|
||||
transitionSpec = {
|
||||
(scaleIn() with scaleOut()).using(SizeTransform(clip = false))
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
if (favoritesOnly) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
|
||||
contentDescription = stringResource(id = R.string.favorite_button_description)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// With text field
|
||||
true -> {
|
||||
IconButton(
|
||||
onClick = {
|
||||
// Resetting query
|
||||
onValueChange("")
|
||||
focusManager.clearFocus()
|
||||
showSearch = false
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||
)
|
||||
}
|
||||
BasicTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.focusRequester(focusRequester),
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
singleLine = true,
|
||||
textStyle = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
keyboardActions = KeyboardActions(onSearch = {
|
||||
// Close searchbar if there is nothing in search query and user
|
||||
// clicks search button on his keyboard
|
||||
if (value.isEmpty()) {
|
||||
showSearch = false
|
||||
} else {
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
}),
|
||||
decorationBox = { innerTextField ->
|
||||
// Showing placeholder only when there is query is empty
|
||||
if (value.isEmpty()) {
|
||||
innerTextField()
|
||||
Text(
|
||||
modifier = Modifier.alpha(0.7f),
|
||||
text = stringResource(id = R.string.search_bar_placeholder),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
} else {
|
||||
innerTextField()
|
||||
}
|
||||
}
|
||||
)
|
||||
// Clear button
|
||||
IconButton(
|
||||
modifier = Modifier.alpha(if (value != String()) 1f else 0f),
|
||||
onClick = { onValueChange("") }
|
||||
) {
|
||||
Icon(
|
||||
Icons.Outlined.Clear,
|
||||
contentDescription = stringResource(id = R.string.clear_input_description)
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BackHandler {
|
||||
if (textFieldShown) {
|
||||
// Search text field is open, need to close it and clear search query
|
||||
onValueChange("")
|
||||
showSearch = false
|
||||
} else {
|
||||
// No MyTextField shown, can go back as usual
|
||||
navigateUpAction()
|
||||
}
|
||||
fun stagedNavigateUp() {
|
||||
if (showSearch) {
|
||||
// Search text field is open, need to close it and clear search query
|
||||
onValueChange("")
|
||||
focusManager.clearFocus()
|
||||
showSearch = false
|
||||
} else {
|
||||
// No search text field is shown, can go back as usual
|
||||
navigateUpAction()
|
||||
}
|
||||
}
|
||||
|
||||
SmallTopAppBar(
|
||||
title = {
|
||||
Crossfade(targetState = showSearch) { textFieldShown ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
when (textFieldShown) {
|
||||
// No search text field
|
||||
false -> {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
}
|
||||
// With text field
|
||||
true -> {
|
||||
BasicTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.focusRequester(focusRequester),
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
singleLine = true,
|
||||
textStyle = MaterialTheme.typography.titleLarge.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
keyboardActions = KeyboardActions(onSearch = {
|
||||
// Close searchbar if there is nothing in search query and user
|
||||
// clicks search button on his keyboard
|
||||
if (value.isEmpty()) {
|
||||
showSearch = false
|
||||
} else {
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
}),
|
||||
decorationBox = { innerTextField ->
|
||||
// Showing placeholder only when there is query is empty
|
||||
if (value.isEmpty()) {
|
||||
innerTextField()
|
||||
Text(
|
||||
modifier = Modifier.alpha(0.7f),
|
||||
text = stringResource(id = R.string.search_bar_placeholder),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
} else {
|
||||
innerTextField()
|
||||
}
|
||||
}
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
Crossfade(targetState = showSearch) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
when (it) {
|
||||
false -> {
|
||||
// Search button
|
||||
IconButton(onClick = { onValueChange(""); showSearch = true }) {
|
||||
Icon(
|
||||
Icons.Default.Search,
|
||||
contentDescription = stringResource(id = R.string.search_button_description)
|
||||
)
|
||||
}
|
||||
// Favorites button
|
||||
IconButton(onClick = favoriteAction) {
|
||||
AnimatedContent(
|
||||
targetState = favoritesOnly,
|
||||
transitionSpec = {
|
||||
(scaleIn() with scaleOut()).using(SizeTransform(clip = false))
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
if (favoritesOnly) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
|
||||
contentDescription = stringResource(id = R.string.favorite_button_description)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
true -> {
|
||||
// Clear button
|
||||
IconButton(onClick = { onValueChange("") }) {
|
||||
Icon(
|
||||
modifier = Modifier.alpha(if (value.isBlank()) 0f else 1f),
|
||||
imageVector = Icons.Outlined.Clear,
|
||||
contentDescription = stringResource(id = R.string.clear_input_description)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { stagedNavigateUp() }) {
|
||||
Icon(
|
||||
Icons.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.navigate_up_description)
|
||||
)
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
|
||||
BackHandler { stagedNavigateUp() }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user