SearchBar | Item Selected Change App Bar | Jetpack Compose
Tempo de leitura: 3 minutes
- Barra de pesquisa padrão
- Item selecionado Barra de pesquisa Ocultar mostrar Barra de seleção
O Jetpack Compose é uma biblioteca moderna de IU para desenvolvimento Android que permite criar interfaces de usuário de maneira declarativa e programática. Algumas informações relevantes sobre Jetpack Compose incluem:
- Funções Compostas: O Jetpack Compose utiliza funções que podem ser compostas, permitindo definir a interface do usuário (IU) do aplicativo de maneira programática.
- Estado em Compose: O estado em um aplicativo é qualquer valor que pode mudar ao longo do tempo. Isso pode variar desde um banco de dados do Room até informações na interface do usuário, permitindo interações dinâmicas.
- Bottom App Bar: Em contextos de IU, como o Bottom App Bar, é possível criar um recurso de menu com os itens desejados e configurar a ActionBar utilizando este recurso.
- SearchBar com Jetpack Compose: Para criar uma barra de pesquisa, pode-se utilizar o composável
SearchBar
oferecido pelo Material 3 do Jetpack Compose. Essa barra flutuante permite que os usuários insiram palavras-chave ou frases para obter informações relevantes. - App Bars em Jetpack Compose: As App Bars podem conter um título, itens de ação principais e determinados itens de navegação. É possível personalizá-la conforme as necessidades do aplicativo, incluindo a alteração com base em seleções específicas.
Abaixo você terá um exemplo pratico disto.
//SearchBar.kt @Composable fun SearchBar( searchBarHeight: Dp, searchBarOffsetHeightPx: Float, isVisible: Boolean, value: String, onChange: (newValue: String) -> Unit ) { val searchBarFloatHeight = with(LocalDensity.current) { searchBarHeight.roundToPx().toFloat() } AnimatedVisibility( isVisible, enter = slideIn(tween(200, easing = LinearOutSlowInEasing)) { IntOffset(0, -searchBarFloatHeight.roundToInt()) }, exit = slideOut(tween(200, easing = FastOutSlowInEasing)) { IntOffset(0, -searchBarFloatHeight.roundToInt()) }, ) { Box( modifier = Modifier.height(searchBarHeight).padding(20.dp, 10.dp, 20.dp, 0.dp) .offset { IntOffset(x = 0, y = searchBarOffsetHeightPx.roundToInt()) }, ) { //HERE SEARCH BAR COMPOSE ADD } } }
@OptIn(ExperimentalFoundationApi::class) @Composable fun NoteSelectionScreen() { val searchBarHeight = 60.dp val searchBarHeightPx = with(LocalDensity.current) { searchBarHeight.roundToPx().toFloat() } val searchBarOffsetHeightPx = remember { mutableFloatStateOf(0f) } val lazyGridNotes = rememberLazyStaggeredGridState() val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y val newOffset = searchBarOffsetHeightPx.floatValue + delta if (lazyGridNotes.canScrollForward) searchBarOffsetHeightPx.floatValue = newOffset.coerceIn(-searchBarHeightPx, 0f) return Offset.Zero } } } Box(Modifier.fillMaxSize().nestedScroll(nestedScrollConnection)) { ScreenContent(lazyGridNotes, notesViewModel) Box(modifier = Modifier) { SearchBar( searchBarHeight, searchBarOffsetHeightPx.floatValue, true, //searchText, //changeSearchText ) ActionBar(actionBarHeight, offsetX) } Box(modifier = Modifier.align(Alignment.BottomEnd).padding(35.dp)) { AnimatedVisibility( notesViewModel.screenState.value.selectedNoteId != null, enter = fadeIn(tween(200, easing = LinearOutSlowInEasing)), exit = fadeOut(tween(200, easing = FastOutSlowInEasing)), ) { //FloatingActionButton ADD } } } } @Composable private fun ScreenContent( lazyGridNotes: LazyStaggeredGridState ) { LazyGridNotes( modifier = Modifier.fillMaxSize().testTag("notes_list"), gridState = lazyGridNotes ) { //ADD COMPOSE } } //val selectedNotes: Set<ObjectId> = emptySet() @Composable private fun ActionBar( actionBarHeight: Dp, offsetX: Animatable<Float, AnimationVector1D> ) { val systemUiController = rememberSystemUiController() val backgroundColor by animateColorAsState( if (selectedNotes.isNotEmpty()) CustomTheme.colors.secondaryBackground else CustomTheme.colors.mainBackground, animationSpec = tween(200), label = "", ) SideEffect { systemUiController.setStatusBarColor(backgroundColor) } val topAppBarElevation = if (offsetX.value.roundToInt() < -actionBarHeight.value.roundToInt()) 0.dp else 2.dp Box( modifier = Modifier.height(actionBarHeight).offset { IntOffset(x = 0, y = offsetX.value.roundToInt()) }, ) { CustomTopAppBar( modifier = Modifier.fillMaxWidth(), elevation = topAppBarElevation, backgroundColor = CustomTheme.colors.secondaryBackground, text = selectedNotes.count().toString(), navigation = TopAppBarItem( isActive = false, icon = Icons.Default.Close, iconDescription = R.string.GoBack, onClick = {} ), items = listOf( TopAppBarItem( isActive = false, icon = Icons.Outlined.PushPin, iconDescription = R.string.AttachNote, onClick = {} ), TopAppBarItem( isActive = false, icon = Icons.Outlined.NotificationAdd, iconDescription = R.string.AddToNotification, onClick = {} ), TopAppBarItem( isActive = false, icon = Icons.Outlined.Archive, iconDescription = R.string.AddToArchive, onClick = {} ), TopAppBarItem( isActive = false, icon = Icons.Outlined.Delete, iconDescription = R.string.AddToBasket, onClick = {} ), ) ) } }
//TopActionBar.kt data class TopAppBarItem( val isActive: Boolean = false, val icon: ImageVector, @StringRes val iconDescription: Int, val modifier: Modifier = Modifier, val onClick: () -> Unit ) @Composable fun CustomTopAppBar( modifier: Modifier, elevation: Dp = 0.dp, text: String = "", backgroundColor: Color = CustomTheme.colors.secondaryBackground, contentColor: Color = CustomTheme.colors.textSecondary, navigation: TopAppBarItem, items: List<TopAppBarItem> ) { TopAppBar( elevation = elevation, contentColor = contentColor, backgroundColor = backgroundColor, modifier = modifier, title = { Spacer(modifier = Modifier.padding(6.dp, 0.dp, 0.dp, 0.dp)) Text( text, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.body1.copy(fontSize = 18.sp), color = CustomTheme.colors.textSecondary, maxLines = 1, ) }, navigationIcon = { Spacer(modifier = Modifier.padding(6.dp, 0.dp, 0.dp, 0.dp)) IconButton( onClick = navigation.onClick, modifier = navigation.modifier.size(40.dp).clip(CircleShape).padding(0.dp), ) { Icon( imageVector = navigation.icon, contentDescription = stringResource(navigation.iconDescription), tint = if (navigation.isActive) CustomTheme.colors.text else CustomTheme.colors.textSecondary, ) } }, actions = { for (item in items) { IconButton( onClick = item.onClick, modifier = item.modifier.size(40.dp).clip(CircleShape).padding(0.dp), ) { Icon( imageVector = item.icon, contentDescription = stringResource(item.iconDescription), tint = if (item.isActive) CustomTheme.colors.text else CustomTheme.colors.textSecondary, ) } Spacer(modifier = Modifier.padding(6.dp, 0.dp, 0.dp, 0.dp)) } }) }
E