ModalNavigationDrawer doesn't totally collapse in Jetpack Compose - android

Here is the code that produces this strange behavior:
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
Column(
modifier = Modifier.fillMaxSize()
) {
items.forEach { item ->
NavigationDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
icon = {
Icon(
imageVector = item.icon,
contentDescription = null
)
},
label = {
Text(
text = item.name
)
},
onClick = {},
selected = true
)
}
}
},
content = {
Scaffold(
topBar = {},
content = { padding ->
Box(
modifier = Modifier.fillMaxSize().padding(padding)
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Profile"
)
}
}
}
)
}
)
I'm using:
implementation "androidx.compose.material3:material3:1.0.0-alpha16"
This exact code worked perfectly fine before updating from 1.0.0-alpha15.
How to solve this?

If you put the drawer contents into a ModalDrawerSheet it might help.
Thus:
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Column(
modifier = Modifier.fillMaxSize()
etc

Related

<SOLVED> TextField New Line Issue in Alert Dialog with Jetpack Compose

I want to create an alert dialog with Jetpack Compose. In this alert dialog include a TextField component from Compose.
I created that alert dialog but there is a problem here that when I press enter button where is on the device keyboard to add a new line my content is moving to up.
How can I solve this UI issue?
Thanks in advance.
The Problem;
Here is my code;
#Composable
fun SimpleAlert(
title: String = "Ask a Question",
onDismiss: () -> Unit,
onSubmit: (text: String) -> Unit
) {
val text = remember { mutableStateOf("") }
val textHint = "Write your question..."
val textLength = remember { mutableStateOf(0) }
val isAnonymous = remember { mutableStateOf(false) }
AlertDialog(
onDismissRequest = {
onDismiss()
},
title = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.drawable.ic_exit),
contentDescription = "",
modifier = Modifier.
background(Color.Gray, CircleShape).clickable {
onDismiss()
}
)
}
Text(
text = title,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
},
text = {
Column(
verticalArrangement = Arrangement.Center
) {
TextField(
value = text.value,
onValueChange = {
if (it.length <= 200) {
textLength.value = it.length
text.value = it
}
},
label = {
Text(text = textHint)
},
modifier = Modifier
.padding(top = 16.dp)
.sizeIn(minHeight = 120.dp)
.background(Color.Transparent)
)
}
},
buttons = {
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Switch(checked = isAnonymous.value, onCheckedChange = {
isAnonymous.value = it
})
Text(text = "Anonymously")
}
Text(text = "${textLength.value}/200")
}
Button(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 24.dp),
onClick = {
onSubmit(text.value)
}) { Text(text = "Submit") }
}
)
}
SOLUTION ✅
To solve this issue just use Dialog instead of AlertDialog
This is definitely what you are looking for
TextField(
value = text.value,
onValueChange = {
if (it.length <= 200) {
textLength.value = it.length
text.value = it
}
},
label = {
Text(text = textHint)
},
modifier = Modifier
.padding(top = 16.dp)
.sizeIn(minHeight = 120.dp)
.background(Color.Transparent),
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
//Do something here
},
),
)

Update LazyColumn after remove opertation in KMM JetpackCompose app

I was deveoping an Kotlin Multiplatform app, where I develop the UI using Jetpack Compose framework, where I recover some data from network using graphql, to keep it into a local database build with SQLDelight.
My poblem comes when I try to implement a remove operation into my localDatabase, due to the LazyColumn component doesn't update after remove some item.
I saw there are some solution using SnapshotStateList, but I don't see how to implemented, and I try to do it using a mutableState of a boolean variable, which tells me if there some change which need to be update, but the Composable doesn't recompose neither.
this is my main component where I added the list:
#Composable
fun MyCharactersView(viewModel: MainViewModel, bottomBar: #Composable () -> Unit, popBack: () -> Unit, navController: NavController, characterSelect: (character: CharacterModel) -> Unit) {
val isLoading = viewModel.isLoading.collectAsState()
val loadError = viewModel.loadError.collectAsState()
val charactersBought = viewModel.characterBought.collectAsState()
val charactersBoughtSnapShot : SnapshotStateList<List<CharacterModel>> = SnapshotStateList(charactersBought.value)
LaunchedEffect(key1 = Unit){
viewModel.getAllBoughtCharacters().also {
Log.d("info", "Lanza el getAllBoughtCharacters:count: ${charactersBought.value.size}")
}
}
when {
isLoading.value -> {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
CircularProgressIndicator(
color = MaterialTheme.colors.primary,
modifier = Modifier
.fillMaxWidth(0.4f)
.fillMaxHeight(0.4f)
.align(Alignment.Center)
)
}
}
loadError.value != "" -> {
Text(
text = "Se ha producido un error",
color = Color.Red,
textAlign = TextAlign.Center
)
}
else -> {
if(viewModel.updateList.value){
viewModel.getAllBoughtCharacters().also {
Log.d("info", "Llama al viewModel.updateList.value: ${charactersBought.value}")
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(
text = "MyCharacters",
style = TextStyle(
color = Color.White,
fontStyle = FontStyle.Italic,
textAlign = TextAlign.Center
)
)},
navigationIcon = {
IconButton(onClick = { popBack() }) {
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
}
}
)
},
bottomBar = bottomBar)
{ paddingValues ->
Surface(
modifier = Modifier
.background(
Color.Transparent
)
) {
if(charactersBought.value.isEmpty()){
Text(
text = "You don't have any character jet.\nTry to buy some one",
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.padding(start = (LocalConfiguration.current.screenWidthDp/4).dp, end = 0.dp, top = (LocalConfiguration.current.screenHeightDp/4).dp)
)
}else{
LazyColumn(
contentPadding = paddingValues,
modifier = Modifier
.background(Color.Transparent)
) {
Log.d("info", "CharacterResponse in items function: ${charactersBought.value}")
items(charactersBought.value) { character ->
MyCharactersListRowView(
navController = navController,
viewModel = viewModel,
characterModel = character!!,
characterSelected = characterSelect
)
}
}
}
}
}
}
}
}
And this is my ListRow component:
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun MyCharactersListRowView(
navController: NavController,
viewModel: MainViewModel,
characterModel: CharacterModel,
characterSelected: (character : CharacterModel) -> Unit
) {
val openDialog = remember { mutableStateOf(false) }
if(openDialog.value) {
SimpleAlertDialog(
show = true,
onConfirm = { viewModel.deleteCharacter(characterModel.id.toLong().also {
if(viewModel.rowAffected.value.toInt() != 0) {
Toast.makeText(appContext, "${characterModel.name} remove correctly !", Toast.LENGTH_LONG).show().also {
openDialog.value = false
viewModel.updateList.value = true
}
}
}) },
onDismiss = { openDialog.value = false },
textDescription = "Would you like to remove ${characterModel.name} from your characters?",
textTittle = "Remove"
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { characterSelected(characterModel) })
.padding(vertical = 8.dp, horizontal = 16.dp)
.combinedClickable(
onLongClick = {
openDialog.value = true
},
onClick = {
navController.navigate(Screens.CharacterDetailsScreen.route + "/${characterModel.id}")
}
),
verticalAlignment = Alignment.CenterVertically
){
AsyncImage(
model = characterModel.image,
contentDescription = characterModel.name,
contentScale = ContentScale.Fit,
modifier = Modifier
.size(60.dp)
.clip(CircleShape)
)
Column(modifier = Modifier.weight(1F)) {
Text(
text = characterModel.name,
style = MaterialTheme.typography.h5,
fontWeight = FontWeight.Bold
)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
Text(
text = "Seen in ${characterModel.numberEpisodes} episodes, most frequently in ${characterModel.location}",
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.Thin
)
}
}
}
Divider()
}
So if you knows some way to force to recompose a composable, (which I think is not possible), or a working solution using the SnapShotStateList, and can share, take thanks in advance !

Navigating between composable's using a Navigation Drawer in Jetpack Compose

I am trying to set up navigation for the drawer icons/textfield in jetpack compose but not exactly sure how to do it properly. How can I set the navigation so that whenever I click in one of the Icons I get navigated to that composable screen? This is currently my MainDrawer layout:
#Composable
fun MainDrawer() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
topBar = {
AppBar(
onNavigationIconClick = {
scope.launch {
scaffoldState.drawerState.isOpen
}
}
)
},
drawerContent = {
DrawerHeader()
DrawerBody(
items = listOf(
MenuItem(
id = "item1",
title = "item1",
contentDescription = "Go to item1 screen",
icon = Icons.Default.Home
),
MenuItem(
id = "item2",
title = "item2",
contentDescription = "Go to item2 screen",
icon = Icons.Default.Settings
),
MenuItem(
id = "item3",
title = "item3",
contentDescription = "Ge to item3",
icon = Icons.Default.Info
),
MenuItem(
id = "item4",
title = "item4",
contentDescription = "Go to Your item4",
icon = Icons.Default.Info
),
MenuItem(
id = "item5",
title = "item5",
contentDescription = "Your item5",
icon = Icons.Default.Info
),
MenuItem(
id = "item6",
title = "item6",
contentDescription = "Your item6",
icon = Icons.Default.Info
),
MenuItem(
id = "item7",
title = "item7",
contentDescription = "item7",
icon = Icons.Default.Info
),
MenuItem(
id = "item8",
title = "item8",
contentDescription = "item8",
icon = Icons.Default.Info
),
)
) {
println("Clicked on ${it.title}")
}
}
) {
}
}
Drawer Body:
This contains the element of the body
#Composable
fun DrawerBody(
items: List<MenuItem>,
modifier: Modifier = Modifier,
itemTextStyle: TextStyle = TextStyle(fontSize = 18.sp),
onItemClick: (MenuItem) -> Unit
) {
LazyColumn(modifier) {
items(items) { item ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
onItemClick(item)
}
.padding(16.dp)
) {
Icon(
imageVector = item.icon,
contentDescription = item.contentDescription
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = item.title,
style = itemTextStyle,
modifier = Modifier.weight(1f)
)
}
}
}
}
I managed to solve this issue. This is how it will work:
https://gyazo.com/4c32e855becff72f8979650545ad7f39
This is how I did it:
Begin by adding the dependencies to your project:
// Navigation with Compose
implementation "androidx.navigation:navigation-compose:2.5.0"
// Modal Drawer Layout
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
Start by creating the TopAppBar for the app. This is the following code:
#Composable
fun TopBar(scope: CoroutineScope, scaffoldState: ScaffoldState) {
TopAppBar(
title = { Text(text = stringResource(R.string.app_name), fontSize = 18.sp) },
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(Icons.Filled.Menu, "")
}
},
backgroundColor = colorResource(id = R.color.purple_200),
contentColor = Color.White
)
}
Next, create a sealed class. This class will represent the items you want inside your drawer. I put the following as an example:
sealed class NavDrawerItem(var route: String, var icon: Int, var title: String) {
object Add : NavDrawerItem("add", android.R.drawable.ic_menu_add, "Add")
object Edit : NavDrawerItem("edit", android.R.drawable.ic_menu_edit, "Edit")
object Search : NavDrawerItem("search", android.R.drawable.ic_menu_search, "Search")
object Location : NavDrawerItem("location", android.R.drawable.ic_menu_mylocation, "Location")
object Preferences : NavDrawerItem("preferences", android.R.drawable.ic_menu_preferences, "Preferences")
}
Now, create the highlighter. When an item in the drawer has been pressed, this will highlight what item has been pressed.
#Composable
fun DrawerItem(item: NavDrawerItem, selected: Boolean, onItemClick: (NavDrawerItem) -> Unit) {
val background = if (selected) R.color.purple_200 else android.R.color.transparent
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { onItemClick(item) })
.height(45.dp)
.background(colorResource(id = background))
.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = item.icon),
contentDescription = item.title,
colorFilter = ColorFilter.tint(Color.White),
contentScale = ContentScale.Fit,
modifier = Modifier
.height(35.dp)
.width(35.dp)
)
Spacer(modifier = Modifier.width(7.dp))
Text(
text = item.title,
fontSize = 18.sp,
color = Color.White
)
}
}
Here we create each screen for the respective items. 1 composable screen for each item. I created an easy and quick screen but you can do your own screens here.
#Composable
fun AddScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myColor))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Add Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
#Composable
fun EditScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myOrange))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Edit Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
#Composable
fun SearchScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.purple_500))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Search Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
#Composable
fun LocationScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myGreen))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Location Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
#Composable
fun PreferencesScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myGreen))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Preference Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
Now we need to create the actual navigation using a navcontroller and navHostController. We do this in the following way:
#Composable
fun ComposeNavigation(navController: NavHostController) {
NavHost(navController, startDestination = NavDrawerItem.Add.route) {
composable(NavDrawerItem.Add.route) {
AddScreen()
}
composable(NavDrawerItem.Edit.route) {
EditScreen()
}
composable(NavDrawerItem.Search.route) {
SearchScreen()
}
composable(NavDrawerItem.Location.route) {
LocationScreen()
}
composable(NavDrawerItem.Preferences.route) {
PreferencesScreen()
}
}
}
After, create DrawerLayout with all the items, coroutines and navbackstack.
#Composable
fun DrawerLayout(scope: CoroutineScope, scaffoldState: ScaffoldState, navController: NavController) {
val items = listOf(
NavDrawerItem.Add,
NavDrawerItem.Edit,
NavDrawerItem.Search,
NavDrawerItem.Location,
NavDrawerItem.Preferences
)
Column {
// Header
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = R.drawable.ic_launcher_foreground.toString(),
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.padding(10.dp)
)
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(5.dp)
)
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
DrawerItem(item = item, selected = currentRoute == item.route, onItemClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
scope.launch {
scaffoldState.drawerState.close()
}
})
}
}
}
Finally we create the mainLayout that will work as the main frame for our app. Here will use a scaffold for the topbar and drawercontent:
#SuppressLint("UnusedMaterialScaffoldPaddingParameter")
#Composable
fun MainLayout() {
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
val scope = rememberCoroutineScope()
val navController = rememberNavController()
Scaffold(
scaffoldState = scaffoldState,
topBar = { TopBar(scope = scope, scaffoldState = scaffoldState) },
drawerBackgroundColor = colorResource(id = R.color.myColor),
drawerContent = {
DrawerLayout(scope = scope, scaffoldState = scaffoldState, navController = navController)
},
) {
ComposeNavigation(navController = navController)
}
}
Hope this helped anyone looking for a functional navigation drawer! Feel free to give it a arrowUp if this works for you. Happy Coding!

Jetpack Compose: Textfield and FAB not using full width

I am trying to put a TextField and a FAB inside a bottomBar using Jetpack Compose.
I wrapped the two with a box, which has the modifier "fillMaxWidth".
But the two controls dont use the full width.
Does anyone know, how to fix this issue?
Here is my Code:
#Composable
fun ChatView() {
Scaffold(
topBar= { ChannelButton() },
bottomBar = { ChatBox() },
modifier = Modifier
.padding(10.dp)
) {
ChatList()
}
}
#Composable
fun ChatBox() {
Box(modifier = Modifier
.background(DiscordDarkGray)
.fillMaxWidth()
){
Column(modifier = Modifier
.padding(10.dp)
.fillMaxWidth()) {
HorizontalCenteredRow(modifier = Modifier
.fillMaxWidth()) {
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = { textState.value = it }
)
Spacer(modifier = Modifier.width(10.dp))
FloatingIconActionButton (
icon = Icons.Default.Send,
onClick = { /*TODO*/ },
backgroundColor = DiscordBlue
)
}
Spacer(modifier = Modifier.height(60.dp))
}
}
}
Here is the Code of the HorizontalCenteredRow:
#Composable
fun HorizontalCenteredRow(
modifier: Modifier = Modifier,
content: #Composable RowScope.() -> Unit
) {
Row(
modifier = modifier
.wrapContentSize(Alignment.Center),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
content = content
)
}
Here is the code of the FAB:
#Composable
fun FloatingIconActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
backgroundColor: Color = MaterialTheme.colors.secondary,
contentColor: Color = contentColorFor(backgroundColor),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
icon: ImageVector = Icons.Default.QuestionAnswer,
iconContentDescription: String = "",
) {
Surface(
modifier = modifier.let {
if (enabled) {
it.clickable(
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = null
)
} else it
},
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation.elevation(interactionSource).value
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = Modifier
.defaultMinSize(minWidth = FabSize, minHeight = FabSize)
.indication(interactionSource, rememberRipple()),
contentAlignment = Alignment.Center
) {
Icon(
icon,
iconContentDescription,
tint = if (enabled) {
colors().onPrimary
} else {
colors().onPrimary.transparentize(.6f)
}
)
}
}
}
}
}
Using
HorizontalCenteredRow(
modifier = Modifier.fillMaxWidth()
the Row fills all the space, but it doesn't mean that the children occupy the entire space.
If you want to fill all the space with the children you have to change the default dimensions of them.
For example you can apply modifier = Modifier.weight(1f) to the TextField.
Something like:
HorizontalCenteredRow(modifier = Modifier
.fillMaxWidth()) {
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = { textState.value = it },
modifier = Modifier.weight(1f)
)

Create chip with outline Jetpack Compose

I have the following composable function to build a Chip:
#Composable
fun CategoryChip(
category: String,
isSelected: Boolean = false,
onSelectedCategoryChanged: (String) -> Unit,
onExecuteSearch: () -> Unit
) {
Surface(
modifier = Modifier.padding(end = 8.dp, bottom = 8.dp),
elevation = 8.dp,
shape = RoundedCornerShape(16.dp),
color = when {
isSelected -> colorResource(R.color.teal_200)
else -> colorResource(R.color.purple_500)
}
) {
Row(modifier = Modifier
.toggleable(
value = isSelected,
onValueChange = {
onSelectedCategoryChanged(category)
onExecuteSearch()
}
)) {
Text(
text = category,
style = MaterialTheme.typography.body2,
color = Color.White,
modifier = Modifier.padding(8.dp)
)
}
}
}
This creates the following chip:
But what I am trying to achieve is the following:
Is it possible to create a shape like that with Jetpack Compose?
Starting with M2 1.2.0-alpha02 you can use the Chip or FilterChip composable:
Chip(
onClick = { /* Do something! */ },
border = BorderStroke(
ChipDefaults.OutlinedBorderSize,
Color.Red
),
colors = ChipDefaults.chipColors(
backgroundColor = Color.White,
contentColor = Color.Red
),
leadingIcon = {
Icon(
Icons.Filled.Settings,
contentDescription = "Localized description"
)
}
) {
Text("Change settings")
}
With M3 (androidx.compose.material3) you can use one of these options:
AssistChip
FilterChip
InputChip
SuggestionChip
Something like:
AssistChip(
onClick = { /* Do something! */ },
label = { Text("Assist Chip") },
leadingIcon = {
Icon(
Icons.Filled.Settings,
contentDescription = "Localized description",
Modifier.size(AssistChipDefaults.IconSize)
)
}
)
Yes, all you have to do is add a border to your surface.
Surface(
modifier = Modifier.padding(end = 8.dp, bottom = 8.dp),
elevation = 8.dp,
shape = RoundedCornerShape(16.dp),
border = BorderStroke(
width = 1.dp,
color = when {
isSelected -> colorResource(R.color.teal_200)
else -> colorResource(R.color.purple_500)
}
)
)
Building on Code Poet's answer i wanted to show how to do a Material Chip with background color:
#Composable
fun buildChip(label: String, icon: ImageVector? = null) {
Box(modifier = Modifier.padding(8.dp)) {
Surface(
elevation = 1.dp,
shape = MaterialTheme.shapes.small,
color = Color.LightGray
) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (icon != null) Icon(
icon,
modifier = Modifier
.fillMaxHeight()
.padding(horizontal = 4.dp)
)
Text(
label,
modifier = Modifier.padding(8.dp),
style = MaterialTheme.typography.button.copy(color = Color.DarkGray)
)
}
}
}
}
Simply use ChipDefaults.outlinedBorder and Defaults.outlinedChipColors():
Chip(
onClick = {},
border = ChipDefaults.outlinedBorder,
colors = ChipDefaults.outlinedChipColors(),
) {
Text(
text = "Trends",
)
}
compose version 1.2.1 , kotlinCompilerExtensionVersion 1.3.1
Also add accompanist Flow library
implementation
"com.google.accompanist:accompanist-flowlayout:0.26.4-beta"
We will use SuggestionChip composable function to create chips.
#Composable
fun SuggestionChipLayout() {
val chips by remember { mutableStateOf(listOf("India", "France", "Spain","Netherland","Austarlia","Nepal")) }
var chipState by remember { mutableStateOf("") }
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Suggestion Chip Example")
Spacer(modifier = Modifier.height(20.dp))
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
mainAxisSpacing = 16.dp,
crossAxisSpacing = 16.dp,
) {
chips.forEach {
SuggestionChipEachRow(chip = it, it == chipState) { chip ->
chipState = chip
}
}
}
}
}
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun SuggestionChipEachRow(
chip: String,
selected: Boolean,
onChipState: (String) -> Unit
) {
SuggestionChip(onClick = {
if (!selected)
onChipState(chip)
else
onChipState("")
}, label = {
Text(text = chip)
},
border = SuggestionChipDefaults.suggestionChipBorder(
borderWidth = 1.dp,
borderColor = if (selected) Color.Transparent else PurpleGrey40
),
modifier = Modifier.padding(horizontal = 16.dp),
colors = SuggestionChipDefaults.suggestionChipColors(
containerColor = if (selected) Purple80 else Color.Transparent
),
shape = RoundedCornerShape(16.dp)
)
}
Filter Chips: In filter chip we can select multiple items at a time
compose version 1.2.1 , kotlinCompilerExtensionVersion 1.3.1 Also add accompanist Flow library
implementation
"com.google.accompanist:accompanist-flowlayout:0.26.4-beta"
we will use FilterChip composable function to create Filter Chip
#SuppressLint("MutableCollectionMutableState")
#Composable
fun FilterChipLayout() {
val originalChips by remember {
mutableStateOf(
listOf(
"chip1",
"chip2",
"chip3",
"chip4",
"chip5"
)
)
}
val temp: Set<Int> = emptySet()
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Filter Chip Example")
Spacer(modifier = Modifier.height(20.dp))
FilterChipEachRow(chipList = originalChips, tempList = temp)
}
}
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun FilterChipEachRow(
chipList: List<String>,
tempList: Set<Int>
) {
var multipleChecked by rememberSaveable { mutableStateOf(tempList) }
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
mainAxisSpacing = 16.dp,
crossAxisSpacing = 16.dp,
) {
chipList.forEachIndexed { index, s ->
FilterChip(selected = multipleChecked.contains(index), onClick = {
multipleChecked = if (multipleChecked.contains(index))
multipleChecked.minus(index)
else
multipleChecked.plus(index)
}, label = {
Text(text = s)
},
border = FilterChipDefaults.filterChipBorder(
borderColor = if (!multipleChecked.contains(index)) PurpleGrey40 else Color.Transparent,
borderWidth = if (multipleChecked.contains(index)) 0.dp else 2.dp
),
shape = RoundedCornerShape(8.dp),
leadingIcon = {
(if (multipleChecked.contains(index)) Icons.Default.Check else null)?.let {
Icon(
it,
contentDescription = ""
)
}
}
)
}
}
}

Categories

Resources