Collapsable top bar for SecondScreen.kt

This commit is contained in:
Sad Ellie 2022-05-28 23:46:56 +03:00
parent 90780d66ef
commit a37babb63d
2 changed files with 186 additions and 171 deletions

View File

@ -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,12 +40,15 @@ 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(
Scaffold(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
SearchBar(
title = stringResource(id = if (leftSide) R.string.units_screen_from else R.string.units_screen_to),
value = searchQuery,
onValueChange = {
@ -56,9 +61,12 @@ fun SecondScreen(
viewModel.loadUnitsToShow(searchQuery, chosenUnitGroup, leftSide)
},
navigateUpAction = navigateUp,
focusManager = focusManager
focusManager = focusManager,
scrollBehavior = scrollBehavior
)
}
) { paddingValues ->
Column(modifier = Modifier.padding(paddingValues)) {
if (leftSide) {
ChipsRow(
lazyListState = chipsRowLazyListState,
@ -86,6 +94,7 @@ fun SecondScreen(
)
}
}
}
// This block is called only once on initial composition
LaunchedEffect(Unit) {

View File

@ -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,35 +37,43 @@ 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() }
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(
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()
@ -77,43 +81,9 @@ fun SearchBar(
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()
@ -149,31 +119,67 @@ fun SearchBar(
}
}
)
// 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()
}
},
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() }
}