I'm using BottomSheetScaffold for BottomSheet.
I followed some sample and it worked.
but when I click outside the BottomSheet, the view's click event is called behind the sheet.
I want to prevent the view click when the Sheet is expanded.
Is there any way?
here is my code
val bottomSheetState = rememberBottomSheetState(
initialValue = BottomSheetValue.Collapsed,
)
val scaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = bottomSheetState,
)
val coroutineScope = rememberCoroutineScope()
val purchaseButtonHeight = 76
val closedSheetHeaderHeight = 50
val sheetHeaderRadius = 24
BottomSheetScaffold(
modifier = Modifier.pointerInput(Unit) {
detectTapGestures(
onTap = {
println("TAG : detectTapGestures ${it}")
coroutineScope.launch {
if (bottomSheetState.isCollapsed) {
bottomSheetState.expand()
} else {
bottomSheetState.collapse()
}
}
})
},
scaffoldState = scaffoldState,
sheetShape = RoundedCornerShape(
topStart = sheetHeaderRadius.dp,
topEnd = sheetHeaderRadius.dp
),
sheetPeekHeight = (purchaseButtonHeight + closedSheetHeaderHeight).dp,
sheetContent = {
Box(
modifier = Modifier
.wrapContentHeight()
.padding(horizontal = 16.dp),
contentAlignment = Alignment.BottomCenter
) {
Column {
for (i in 0 until 5) {
Text(
text = "Item ${i}",
modifier = Modifier
.height(50.dp)
.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.height(purchaseButtonHeight.dp))
}
}
})
Related
I want to make the text to be resizing dynamically when I scroll and them to stay that size when I stop scrolling.
For example here the number 4 to be 20.sp and the numbers 3 and 5 to be 18.sp (one index away) and 2 - 6 to be 16.sp and so on. I think that could be created with
Modifier.scale(), but have not found a way for now.
#OptIn(ExperimentalSnapperApi::class)
#Composable
fun Example() {
val listStateHour = rememberLazyListState()
val hourTexts: List<String> = (1..23).map { it.toString().padStart(2, '0') }
Box(
modifier = Modifier.background(color = Color.LightGray).fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.height(32.dp)
.fillMaxWidth()
.background(color = Color.Black.copy(0.2f)),
) {}
LazyColumn(
modifier = Modifier.height(200.dp),
state = listStateHour,
horizontalAlignment = Alignment.Start,
contentPadding = PaddingValues(vertical = 116.dp),
flingBehavior = rememberSnapperFlingBehavior(lazyListState = listStateHour)
) {
itemsIndexed(hourTexts) { index, item ->
Box(
modifier = Modifier.height(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item,
color = Color.Black,
fontSize = 20.sp
)
}
}
}
}
}
External dependencies that I am using -> implementation 'dev.chrisbanes.snapper:snapper:0.3.0'
You can observe listStateHour.firstVisibleItemIndex and calculate a scaling factor depending on the distance an item has to that value.
I didn't test it with the flingBehaviour you're using but this might help you:
val listStateHour = rememberLazyListState()
val firstVisibleItemIndex by remember { derivedStateOf { listStateHour.firstVisibleItemIndex } }
val hourTexts: List<String> = (1..23).map { it.toString().padStart(2, '0')
Box(
modifier = Modifier
.background(color = Color.LightGray)
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.height(32.dp)
.fillMaxWidth()
.background(color = Color.Black.copy(0.2f)),
) {}
LazyColumn(
modifier = Modifier.height(200.dp),
state = listStateHour,
horizontalAlignment = Alignment.Start,
contentPadding = PaddingValues(vertical = 116.dp)
) {
itemsIndexed(hourTexts) { index, item ->
Box(
modifier = Modifier.height(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item,
color = Color.Black,
fontSize = 20.sp,
modifier = Modifier.scale(
scaleText(
firstVisibleIndex = firstVisibleItemIndex,
itemIndex = index
)
)
)
}
}
}
}
fun scaleText(firstVisibleIndex: Int, itemIndex: Int): Float {
val distance = abs( firstVisibleIndex - itemIndex)
return 1f - distance * .2f
}
I have a lazy column in which there is a list of cards in which there is a row and two columns in it, the height of the first column is much larger than the second. The second column has text with a different background made using a surface. The fillMaxHeight modifiers do not work. Already tried to do it by other means:
1)
Row(
modifier = Modifier
.height(IntrinsicSize.Min)
However, there is a problem, this only works with static components, and this is a lazy line in my columns, and the composition starts to crash
2) Get the height in this way.
//Save height
val heightIs = remember {
mutableStateOf(0.dp)
}
//Get the context
val localDensity = LocalDensity.current
Column(modifier = Modifier.fillMaxWidth(0.7f).onSizeChanged{cord->
heightIs.value = with(localDensity){
cord.height.toDp()
}
The problem with this solution is that when there is no lazy row, it doesn't properly distribute the height.
Short code:
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 2.dp, vertical = 4.dp), elevation = 4.dp
) {
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
Column(Modifier.fillMaxWidth(0.7f)) {
Surface(
color = MaterialTheme.colors.primaryVariant,
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = historyOrderItem.id.toString(),
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(4.dp)
)
Text(
text = historyOrderItem.status,
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(4.dp)
)
}
}
LazyRow(modifier = modifier.fillMaxWidth()) {
items(items = historyOrderItem.items, key = { it.itemId }) {
ItemDoneOrderShort(it)
}
}
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = { onCopy(historyOrderItem) }) {
Text(
text = "ПОВТОРИТЬ",
style = MaterialTheme.typography.h6,
textAlign = TextAlign.Right,
maxLines = 1,
)
}
}
}
Surface(color = MaterialTheme.colors.colorAccent) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Оплатить",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h5,
)
}
}
}
}
Is there any way to remove this padding from the BottomNavigationItem?
Image
If I have very large text, I have to use ResponsiveText to manage this, but that's not what I intended. What I need is that it doesn't have that side padding/margin, both on the left and on the right, in order to occupy as much space as possible.
My code:
#Composable
fun BottomNavBar(
backStackEntryState: State<NavBackStackEntry?>,
navController: NavController,
bottomNavItems: List<NavigationItem>
) {
BottomNavigation(
backgroundColor = DarkGray.copy(alpha = 0.6f),
elevation = Dimen0,
modifier = Modifier
.padding(Dimen10, Dimen20, Dimen10, Dimen20)
.clip(RoundedCornerShape(Dimen13, Dimen13, Dimen13, Dimen13))
) {
bottomNavItems.forEach { item ->
val isSelected = item.route == backStackEntryState.value?.destination?.route
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon.orZero()),
contentDescription = stringResource(id = item.title)
)
},
label = {
ResponsiveText(
text = stringResource(id = item.title),
textStyle = TextStyle14,
maxLines = 1
)
},
selectedContentColor = Color.White,
unselectedContentColor = Color.White,
alwaysShowLabel = true,
selected = isSelected,
onClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route = route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
},
modifier = if (isSelected) {
Modifier
.clip(RoundedCornerShape(Dimen13, Dimen13, Dimen13, Dimen13))
.background(color = DarkGray)
} else {
Modifier.background(color = Color.Unspecified)
}
)
}
}
}
Apparently this is currently (I am using compose version '1.2.0-rc03') not possible when using BottomNavigation, as there is padding set for each element in these lines:
.padding(horizontal = BottomNavigationItemHorizontalPadding)
Here is what is said about this value:
/**
* Padding at the start and end of a [BottomNavigationItem]
*/
private val BottomNavigationItemHorizontalPadding = 12.dp
[Solution]
Just copy BottomNavigation from androidx and remov this line:
.padding(horizontal = BottomNavigationItemHorizontalPadding)
However, it is necessary that the first and last elements still have padding, so add the innerHorizontalPaddings parameter to the your CustomBottomNavigation constructor
There are a few more changes, you can see the full code of my CustomBottomNavigation here
Example of usage:
CustomBottomNavigation(
...,
innerHorizontalPaddings = 12.dp
) {
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(...)
},
label = {
Text(
...
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = 2.dp)
)
},
...
)
}
}
Another solution is to wrap the label in a BoxWithConstraints and draw outside of it:
BottomNavigationItem(
label = {
/**
* Because of [BottomNavigationItemHorizontalPadding] (12.dp), we need to
* think (and draw) outside the box.
*/
BoxWithConstraints {
Text(
modifier = Modifier
.wrapContentWidth(unbounded = true)
.requiredWidth(maxWidth + 24.dp), // 24.dp = the padding * 2
text = "Centered text and clipped at the end if too long",
softWrap = false,
textAlign = TextAlign.Center
)
}
},
...
)
To get a little bit of padding, you can set requiredWidth(maxWidth + 18.dp).
With this solution, you don't need to copy the enire BottomNavigation :)
I'm trying to use ConstraintLayout in Compose. I want the second Composable to be constrained at the end of the first Composable. The first composable can exist or not. In each case, I would like to have different margins. Thus, I use goneMargin but this seems to not be respected. Do I need to do something else?
ConstraintLayout {
val (firstItemRef, secondItemRef) = createRefs()
if (isFirstItemVisible) {
Text(
text = "First",
modifier = Modifier.constrainAs(firstItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = parent.start)
}
)
}
Text(
text = "Second",
modifier = Modifier.constrainAs(secondItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = firstItemRef.end, margin = 8.dp, goneMargin = 16.dp)
}
)
}
As a workaround, we could do something like that, but this seems to counterfeit the purpose of goneMargin
Text(
text = "Second",
modifier = Modifier.constrainAs(secondItemRef) {
val margin = if (isFirstItemVisible) 8.dp else 16.dp
val end = if (isFirstItemVisible) firstItemRef.end else parent.end
top.linkTo(anchor = parent.top)
start.linkTo(anchor = end, margin = margin)
}
)
You have to use the visibility property in ConstrainScope like this:
ConstraintLayout(Modifier.fillMaxSize()) {
val (firstItemRef, secondItemRef) = createRefs()
Text(
text = "First",
modifier = Modifier.constrainAs(firstItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = parent.start)
// >> This is what you want <<<
visibility = if (isFirstItemVisible) Visibility.Visible else Visibility.Gone
}
)
Text(
text = "Second",
modifier = Modifier.constrainAs(secondItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = firstItemRef.end, margin = 8.dp, goneMargin = 16.dp)
}
)
}
I've a HorizontalPager with 3 items.
I need to know exact offset according to user's swipe
HorizontalPager(
count = 3
) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = ""
)
}
In Compose many views have some state parameter, using which you can check its state.
In case of pager, it the state has currentPage and currentPageOffset info. To calculate final offset you also need to check view size with Modifier.onSizeChanged or BoxWithConstraints.
I'm using derivedStateOf to prevent redundant recompositions.
val pagerState = rememberPagerState()
val (pagerWidth, setPagerWidth) = remember { mutableStateOf<Int?>(null) }
val scrollOffset by remember(pagerWidth) {
derivedStateOf {
if (pagerWidth == null) return#derivedStateOf 0f
(pagerState.currentPage + pagerState.currentPageOffset) * pagerWidth
}
}
HorizontalPager(
state = pagerState,
count = 3,
modifier = Modifier
.onSizeChanged {
setPagerWidth(it.width)
}
) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = ""
)
}
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "",
modifier = Modifier
.fillMaxWidth()
.offset(x = with(LocalDensity.current) { -scrollOffset.toDp() })
)