OiO.lk Blog Android How to fix onBackPressed for an expanded material3 SearchBar?
Android

How to fix onBackPressed for an expanded material3 SearchBar?


I am using androidx.compose.material3.SearchBar in my Android project. Here is the structure:

SearchActivity.kt:

class SearchActivity {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_search)
        if (savedInstanceState == null) {
            addFragment(R.id.container, SearchFragment(), SearchFragment.FRAGMENT_TAG)
        }
    }
}

layout/activity_search.xml:

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:context=".search.SearchActivity"
        tools:ignore="MergeRootFrame"
        tools:layout="@layout/fragment_search" />

</androidx.constraintlayout.widget.ConstraintLayout>

SearchFragment.kt:

class SearchFragment : Fragment() {

    companion object {
        const val FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG"

        fun replaceAtBackStack(fragmentManager: FragmentManager, @IdRes containerViewId: Int) {
            fragmentManager.commit {
                fragmentManager.popBackStack(FRAGMENT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
                fragmentManager.replaceFragment(containerViewId, SearchFragment(), FRAGMENT_TAG, FRAGMENT_TAG)
            }
        }
    }

    private val viewModelFactory by lazy { SearchViewModelFactory(/*...*/) }
    private val viewModel: SearchViewModel by viewModels { viewModelFactory }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = 
      inflater.inflate(R.layout.fragment_search, container, false).apply {
        findViewById<ComposeView>(R.id.search_view).apply {
            setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                SearchScreen(
                    searchQuery = viewModel.searchQuery,
                    state = viewModel.searchResultsState.collectAsState().value,
                    onViewEvent = viewModel::onViewEvent,
                )
            }
            isClickable = true
        }
    }

}

layout/fragment_search.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/search_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

SearchScreen.kt:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchScreen(
    searchQuery: String,
    state: SearchResultState,
    onViewEvent: (SearchViewEvent) -> Unit,
) {
    MyTheme {
        Scaffold { contentPadding ->
            Box(
                Modifier
                    .padding(contentPadding)
            ) {
                var text by rememberSaveable { mutableStateOf(searchQuery) }
                val expanded = true
                SearchBar(
                    modifier = Modifier
                        .align(TopCenter)
                        .semantics { traversalIndex = 0f },
                    inputField = {
                        SearchBarDefaults.InputField(
                            query = text,
                            onQueryChange = {
                                text = it
                                onViewEvent(OnSearchQueryChange(it))
                            },
                            onSearch = { },
                            expanded = expanded,
                            onExpandedChange = { },
                            placeholder = { Text(stringResource(R.string.search_query_hint)) },
                            leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
                        )
                    },
                    expanded = expanded,
                    onExpandedChange = { }
                ) {
                    when (state) {
                        is Loading -> Loading()
                        is Success -> {
                            val sessions = state.parameters
                            if (sessions.isEmpty()) {
                                NoSearchResult()
                            } else {
                                SearchResultList(
                                    parameters = sessions,
                                    onViewEvent = onViewEvent,
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

For some reason, the SearchBar consumes the back gesture. This prevents the user from leaving the screen at all. How can this be fixed?



You need to sign in to view this answers

Exit mobile version