Skip to content

Commit

Permalink
feat: Request focus on search bar and consume back press to close bef…
Browse files Browse the repository at this point in the history
…ore exit (#124)

Signed-off-by: starry-shivam <[email protected]>
  • Loading branch information
starry-shivam authored May 9, 2024
1 parent 76e474d commit a8434ad
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import javax.inject.Inject

enum class SearchWidgetState { OPENED, CLOSED }
enum class SearchBarState { OPENED, CLOSED }
enum class FilterField { Title, Amount, Priority }
enum class FilterSortType(val value: Int) { Ascending(1), Descending(2) }
enum class GoalCardStyle { Classic, Compact }
Expand Down Expand Up @@ -97,9 +97,9 @@ class HomeViewModel @Inject constructor(
}
val goalsList = goalsListFlow.asLiveData()

private val _searchWidgetState: MutableState<SearchWidgetState> =
mutableStateOf(value = SearchWidgetState.CLOSED)
val searchWidgetState: State<SearchWidgetState> = _searchWidgetState
private val _searchBarState: MutableState<SearchBarState> =
mutableStateOf(value = SearchBarState.CLOSED)
val searchBarState: State<SearchBarState> = _searchBarState

private val _searchTextState: MutableState<String> = mutableStateOf(value = "")
val searchTextState: State<String> = _searchTextState
Expand All @@ -112,8 +112,8 @@ class HomeViewModel @Inject constructor(
)
val showOnboardingTapTargets: State<Boolean> = _showOnboardingTapTargets

fun updateSearchWidgetState(newValue: SearchWidgetState) {
_searchWidgetState.value = newValue
fun updateSearchWidgetState(newValue: SearchBarState) {
_searchBarState.value = newValue
}

fun updateSearchTextState(newValue: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalView
Expand All @@ -60,43 +65,48 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.starry.greenstash.R
import com.starry.greenstash.ui.screens.home.SearchWidgetState
import com.starry.greenstash.ui.screens.home.SearchBarState
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.utils.weakHapticFeedback


@Composable
fun HomeAppBar(
searchBarState: SearchBarState,
searchTextState: String,
consumeBackPress: MutableState<Boolean>,
onMenuClicked: () -> Unit,
onFilterClicked: () -> Unit,
onSearchClicked: () -> Unit,
searchWidgetState: SearchWidgetState,
searchTextState: String,
onSearchTextChange: (String) -> Unit,
onSearchCloseClicked: () -> Unit,
onSearchImeAction: (String) -> Unit,
) {
Crossfade(
targetState = searchWidgetState,
targetState = searchBarState,
animationSpec = tween(durationMillis = 300),
label = "searchbar cross-fade"
) {
when (it) {
SearchWidgetState.CLOSED -> {
SearchBarState.CLOSED -> {
DefaultAppBar(
onMenuClicked = onMenuClicked,
onFilterClicked = onFilterClicked,
onSearchClicked = onSearchClicked
)
consumeBackPress.value = false
}

SearchWidgetState.OPENED -> {
SearchBarState.OPENED -> {
SearchAppBar(
text = searchTextState,
onTextChange = onSearchTextChange,
onCloseClicked = onSearchCloseClicked,
onSearchClicked = onSearchImeAction
)
// Consume the system back button press when the search bar is open
// So we can close the search bar instead of navigating back.
consumeBackPress.value = true
}
}
}
Expand Down Expand Up @@ -163,10 +173,12 @@ private fun SearchAppBar(
onCloseClicked: () -> Unit,
onSearchClicked: (String) -> Unit,
) {
val focusRequester = remember { FocusRequester() }
Surface(
modifier = Modifier
.fillMaxWidth()
.windowInsetsPadding(insets = WindowInsets.statusBars),
.windowInsetsPadding(insets = WindowInsets.statusBars)
.focusRequester(focusRequester),
color = MaterialTheme.colorScheme.surface
) {
OutlinedTextField(
Expand Down Expand Up @@ -223,5 +235,8 @@ private fun SearchAppBar(
),
shape = RoundedCornerShape(24.dp)
)

// Request focus on the search bar when it is opened
LaunchedEffect(Unit) { focusRequester.requestFocus() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

package com.starry.greenstash.ui.screens.home.composables

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.fadeIn
Expand Down Expand Up @@ -106,7 +107,7 @@ import com.starry.greenstash.ui.navigation.Screens
import com.starry.greenstash.ui.screens.home.FilterField
import com.starry.greenstash.ui.screens.home.FilterSortType
import com.starry.greenstash.ui.screens.home.HomeViewModel
import com.starry.greenstash.ui.screens.home.SearchWidgetState
import com.starry.greenstash.ui.screens.home.SearchBarState
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.utils.getActivity
import com.starry.greenstash.utils.isScrollingUp
Expand All @@ -129,13 +130,25 @@ fun HomeScreen(navController: NavController) {
val filterSheetState = rememberModalBottomSheetState()
val drawerState = rememberDrawerState(DrawerValue.Closed)

val searchWidgetState by viewModel.searchWidgetState
val searchBarState by viewModel.searchBarState
val searchTextState by viewModel.searchTextState

val lazyListState = rememberLazyListState()
val snackBarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()

// If search bar is open, then consume back button press to close it.
val consumeBackPress = remember { mutableStateOf(false) }
BackHandler(enabled = consumeBackPress.value) {
if (viewModel.searchBarState.value == SearchBarState.OPENED) {
if (searchTextState.isNotBlank()) {
viewModel.updateSearchTextState(newValue = "")
} else {
viewModel.updateSearchWidgetState(SearchBarState.CLOSED)
}
}
}

if (showFilterSheet.value) {
ModalBottomSheet(
onDismissRequest = {
Expand Down Expand Up @@ -175,17 +188,14 @@ fun HomeScreen(navController: NavController) {
snackbarHost = { SnackbarHost(snackBarHostState) },
topBar = {
HomeAppBar(
onMenuClicked = { coroutineScope.launch { drawerState.open() } },
onFilterClicked = {

showFilterSheet.value = true

},
onSearchClicked = { viewModel.updateSearchWidgetState(newValue = SearchWidgetState.OPENED) },
searchWidgetState = searchWidgetState,
searchBarState = searchBarState,
searchTextState = searchTextState,
consumeBackPress = consumeBackPress,
onMenuClicked = { coroutineScope.launch { drawerState.open() } },
onFilterClicked = { showFilterSheet.value = true },
onSearchClicked = { viewModel.updateSearchWidgetState(newValue = SearchBarState.OPENED) },
onSearchTextChange = { viewModel.updateSearchTextState(newValue = it) },
onSearchCloseClicked = { viewModel.updateSearchWidgetState(newValue = SearchWidgetState.CLOSED) },
onSearchCloseClicked = { viewModel.updateSearchWidgetState(newValue = SearchBarState.CLOSED) },
onSearchImeAction = { println("Meow >~< | $it") },
)
},
Expand Down

0 comments on commit a8434ad

Please sign in to comment.