Takeshi567
Takeshi567

Reputation: 218

Hot to fix problem with unconcurrent state crashes?

I have auto slider banner every 3 sec it goes to next banner and also you can manually change banner. That's my launched effect and I use Horizontal Pager from foundation I also needed to calculate if 1/2 of the banner is visible, so I have done another Box on it. In terms of testing when I have turned off my banner compose section and test it for hours it didn't crash with unconcurrent state change. When banner works crash appears from time to time not so often, but it happens.

stacktrace

Fatal Exception: java.lang.IllegalStateException
Unsupported concurrent change during composition. A state object was modified by composition as well as being modified outside composition.
androidx.compose.runtime.Recomposer.applyAndCheck (Recomposer.kt:1)

version tested 1.6.1, 1.6.8, 1.7.0-rc01

    const val foundation = "androidx.compose.foundation:foundation:$composeVersion"
const val AUTO_SLIDER_DELAY = 3000.toLong()
    val state = rememberPagerState(pageCount = { sliderItems.count() })

   LaunchedEffect(state.currentPage, sliderHoldTimestamp, sliderItems.size) {
        state.let { localState ->
            delay(AUTO_SLIDER_DELAY)
            coroutineScope.launch {
                if (sliderItems.isNotEmpty() && localState.pageCount != 0) {
                    var newPosition = localState.currentPage + 1
                    if (newPosition > sliderItems.size - 1) newPosition = 0
                    localState.animateScrollToPage(newPosition.mod(localState.pageCount))
                }
            }
        }
    }

Part with Horizontal Pager

Box(
        modifier = Modifier
            .fillMaxWidth()
            .shadow(
                elevation = 4.dp, ambientColor = ShadowColor, spotColor = ShadowSpotColor
            )
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .bottomElevation(4.dp)
                .onGloballyPositioned { layoutCoordinates ->
                    coroutineScope.launch {
                        delay(10L)
                        val height = layoutCoordinates.boundsInWindow().bottom
                        if (height != previousHeight.floatValue) {
                            isVisibleFlow.value =
                                calculateIsVisible(layoutCoordinates, screenHeightPx)
                            previousHeight.floatValue = height
                        }
                    }
                },
                contentAlignment = Alignment.BottomCenter,
        ) {
            if (sliderItems.isNotEmpty() && !isLoading) {
                HorizontalPager(
                    state = state
                ) { page ->
                    when (val sliderItem = sliderItems[page]) {
                        is SliderBannerItem -> {
                            imageUrl.value = sliderItem.link
                            sliderItemPromo.value = sliderItem

                            if (state.currentPage == page) {
                                sliderItemPromoForEvent.value = sliderItemPromo.value
                            }
                            if ((sliderItem.containsAlco == true) && !ageVerificationState.isConfirmed()) {
                                AsyncBlurryImage(
                                    imageUrl = imageUrl.value,
                                    contentScale = ContentScale.Crop,
                                    contentDescription = null,
                                    modifier = Modifier
                                        .aspectRatio(1.77f)
                                        .clickable {
                                            events(
                                                PromoSliderEvents.RequestAgeVerification(
                                                    AgeVerificationEventSource.Slider(null)
                                                )
                                            )
                                        },
                                )
                            }

Also this is my code to send event, but the problem is it triggers all the time and I quite not sure why. It seems like this banner recompose almost every 1ms, should it work like this? BEFORESLIDERITEM Logs practically all the time.

  if (sliderItems.isNotEmpty() && state.pageCount != 0 && sliderItemPromoForEvent.value != null) {
        Log.d("sliderItems", "BEFORESLIDERITEM")
        LaunchedEffect(sliderItemPromoForEvent.value) {
            Log.d("sliderItems", "AfterInLaunchedEffect")
            withContext(Dispatchers.Main) {
                try {
                    delay(1000L)
                    val sliderItemCurrentPage = sliderItemPromoForEvent.value

                    if (isVisibleFlow.value) {
                        sliderItemCurrentPage.let {
                            withContext(Dispatchers.IO) {
                                events.invoke(
                                    PromoSliderEvents.SendViewPromotionEvent(
                                        it?.redirectUrl.toString(),
                                        it?.link.toString(),
                                        state.currentPage.toString()
                                    )
                                )
                            }
                        }
                    }
                    sliderItemPromoForEvent.value = null
                } catch (e: Exception) {
                    Napier.d("${e.stackTraceToString()} - PromoSlider.kt; SliderItems")
                }
            }
        }
    }

Thanks for any help and advices.

Upvotes: 1

Views: 61

Answers (0)

Related Questions