How to use Pager Layout in Jetpack Compose? - android-jetpack-compose

I want to have different image on different page in HorizontalPager. How to do that
val pagerState = rememberPagerState(pageCount = 3)
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth()
) {
Image(painter = painterResource(image1), contentDescription = null)
}

HorizontalPager content is called for each page, you can get page index from the lambda argument:
val pagerState = rememberPagerState(pageCount = 3)
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth()
) { page ->
Image(
painter = painterResource(
when(page) {
0 -> R.drawable.my_image1
1 -> R.drawable.my_image2
2 -> R.drawable.my_image3
else -> throw IllegalStateException("image not provided for page $page")
}
), contentDescription = null
)
}

Related

the view outside bottom sheet is called when sheet expanded

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))
}
}
})

Resizing text dynamically in LazyColumn depending on scroll - Jetpack Compose

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
}

How to handle error on outlined edit text checking regex in compose

I have this outlined edit text, and I want to display a red error color when user tries to put '#' on the outline edit text when they're trying to sign in. I was wondering how I can handle error on an edit text in jetpack compose
UserInputTextField(
fieldState = usernameState.value,
onFieldChange = { usernameState.value = it },
label = "Enter Name",
)
#Composable
fun UserInputTextField(
fieldState: String,
onFieldChange: (String) -> Unit,
label: String,
modifier: Modifier = Modifier,
) {
androidx.compose.material.OutlinedTextField(
value = fieldState, onValueChange = {
onFieldChange(it)
},
label = { androidx.compose.material.Text(text = label) },
modifier = modifier
.fillMaxWidth()
.padding(top = 16.dp)
.semantics { testTag = TestTags.LoginContent.USERNAME_FIELD },
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Color.Blue,
unfocusedBorderColor = Color.Black
)
)
}
Use isError attribute of TextField.
Example
isError = fieldState.contains("#"),
Sample code for reference
#Composable
fun ErrorCheck() {
val (text, setText) = remember {
mutableStateOf("")
}
UserInputTextField(
fieldState = text,
onFieldChange = setText,
label = "Email",
)
}
#Composable
fun UserInputTextField(
fieldState: String,
onFieldChange: (String) -> Unit,
label: String,
modifier: Modifier = Modifier,
) {
androidx.compose.material.OutlinedTextField(
value = fieldState,
onValueChange = {
onFieldChange(it)
},
isError = fieldState.contains("#"),
modifier = modifier
.fillMaxWidth()
.padding(
all = 16.dp,
),
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Color.Blue,
unfocusedBorderColor = Color.Black,
)
)
}

expand Image as much as screen size jetpack compose

I have a LazyColumn and some childs in it like below
LazyColumn(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
item {
Child(
modifier = Modifier,
firstImage = fakeImage,
secondImage = fakeImage,
onImageClick = {}
)
}
item {
Child(
modifier = Modifier,
firstImage = fakeImage,
secondImage = fakeImage,
onImageClick = {}
)
}
}
here is what is inside of TwoPiecesLayout
#ExperimentalMaterialApi
#Composable
fun Child(
modifier: Modifier,
firstImage: Image,
secondImage: Image,
onImageClick: (Image) -> Unit
) {
val height = (LocalConfiguration.current.screenWidthDp / 2) - 56
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
ImageCell(
modifier = Modifier
.weight(
weight = 1F
)
.height(
height = height.dp
),
image = firstImage,
onImageClick = {
onImageClick(firstImage)
}
)
ImageCell(
modifier = Modifier
.weight(
weight = 3F
)
.height(
height = height.dp
),
image = secondImage,
onImageClick = {
onImageClick(secondImage)
}
)
}
}
when every of Images in Child clicked I want to expand their size as much as screen's size just like the material design choreography
https://storage.cloud.google.com/non-spec-apps/mio-direct-embeds/videos/FADE.mp4
how can I do this?
This is not just for image, with basically any Composable, you can apply this method
var expanded by remember { mutableStateOf (false) }
val animF by animateFloatAsState(
initialState = 0.25f,
targetState = if (expanded) 1f else 0.25f
)
MyComposable(
Modifier.fillMaxSize(animF) // pass the animated Fraction here
.clickable { expanded = !expanded }
)
This will oscillate between 0.25 of the entire screen to 1f of the same, upon clicking the Composable.
See? Super-easy, barely an inconvenience.

HorizontalPager detect swipe offset

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() })
)

Resources