HorizontalPager in LazyColumn. Jetpack Compose

50 Views Asked by At

The problem is that the pager is inside LazyColumn, and inside the pager is the list. When changing pages, if the dimensions of one do not match the other, then a white screen is visible for a long time.

fun CustomTabRow(
    tabs: List<Pair<String, @Composable () -> Unit>>,
    modifier: Modifier = Modifier,
    spaceBetweenTabAndPager: Dp = 0.dp,
    isBeyondBoundsPageCount: Boolean = true,
    horizontalSpaceBetweenContent: Dp = 0.dp,
    selectedTabIndex: Int = 0,
    maxLinesInTitle: Int = 1
) {
    val scope = rememberCoroutineScope()
    val pagerState = rememberPagerState(
        initialPage = selectedTabIndex,
        initialPageOffsetFraction = 0f,
    ) {
        tabs.size
    }
    val dimensions = LocalDimensions.current
    val indicatorHeight = 3.dp
    pagerState.currentPageOffsetFraction

    Column(modifier = modifier) {
        TabRow(
            modifier = Modifier.padding(horizontal = horizontalSpaceBetweenContent),
            selectedTabIndex = pagerState.currentPage,
            indicator = { tabPositions ->
                TabRowDefaults.SecondaryIndicator(
                    color = PrimaryMain,
                    modifier = Modifier
                        .tabIndicatorOffset(tabPositions[pagerState.currentPage])
                        .height(indicatorHeight)
                        .clip(shape = RoundedCornerShape(size = dimensions.pagerIndicatorCornerRadius))
                )
            }
        ) {
            tabs.forEachIndexed { index, tabItem ->
                Tab(
                    modifier = Modifier.background(color = BackgroundBase),
                    selected = pagerState.currentPage == index,
                    onClick = {
                        scope.launch {
                            pagerState.animateScrollToPage(index)
                        }
                    },
                    selectedContentColor = PrimaryMain,
                    unselectedContentColor = SecondaryText,
                    text = {
                        Text(
                            text = tabItem.first,
                            style = BoldMontserrat16,
                            maxLines = maxLinesInTitle
                        )
                    }
                )
            }
        }
        HorizontalPager(
            state = pagerState,
            beyondBoundsPageCount = if (isBeyondBoundsPageCount) tabs.size else 0,
            verticalAlignment = Alignment.Top
        ) { index ->
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(horizontal = horizontalSpaceBetweenContent)
                    .padding(top = spaceBetweenTabAndPager)
            ) {
                tabs[index].second()
            }
        }
    }
}

class TimeTrackingScreen : TimeTracking {

    @OptIn(ExperimentalFoundationApi::class)
    @Composable
    override fun Content() {
        val dimensions = LocalDimensions.current
        val navigator = LocalNavigator.currentOrThrow
        val snackbarHostState = LocalSnackbarHostState.current
        val horizontalPadding = Modifier.padding(horizontal = dimensions.horizontalMedium)

        val screenModel = getScreenModel<TimeTrackingScreenModel>()
        val screenState by screenModel.state.collectAsState()
        val state by screenModel.timeTrackingState.collectAsState()
        val loginScreen = rememberScreen(SharedScreen.LoginScreen)

        when (screenState) {
            is TimeTrackingScreenModel.State.EditLeavingDetailsDialog -> {
                EditLeavingHoursDialog(
                    leavingDetails =
                    (screenState as TimeTrackingScreenModel.State.EditLeavingDetailsDialog)
                        .selectedLeavingDetails,
                    rateMultiplier = state.rate,
                    onSaveHoursClick = rememberSafe { leavingDetails ->
                        screenModel.changeLeavingDetail(leavingDetails = leavingDetails)
                    },
                    onDismiss = rememberSafe { ->
                        screenModel.clearErrors()
                    }
                )
            }

            is TimeTrackingScreenModel.State.EditProjectDetailsDialog -> {
                EditProjectHoursDialog(
                    projectDetails =
                    (screenState as TimeTrackingScreenModel.State.EditProjectDetailsDialog)
                        .selectedProjectDetails,
                    onSaveHoursClick = rememberSafe { projectDetails ->
                        screenModel.changeProjectDetail(projectDetails = projectDetails)
                    },
                    onDismiss = rememberSafe { ->
                        screenModel.clearErrors()
                    }
                )
            }

            else -> { /* Do nothing */ }
        }

        LaunchedEffect(screenState) {
            when (screenState) {
                is TimeTrackingScreenModel.State.Error -> {
                    (screenState as TimeTrackingScreenModel.State.Error).message?.also { message ->
                        snackbarHostState.showMessage(message = message)
                        screenModel.clearErrors()
                    }
                }

                is TimeTrackingScreenModel.State.CriticalError -> {
                    (screenState as TimeTrackingScreenModel.State.CriticalError).message?.also { message ->
                        snackbarHostState.showMessage(message = message)
                        screenModel.clearErrors()
                    }

                    navigator.replaceAll(loginScreen)
                }

                else -> { /* Do nothing */
                }
            }
        }
        Localization(locale = DateUtils.DEFAULT_LOCALE) {
            val localization = Vocabulary.localization

            if (screenState is TimeTrackingScreenModel.State.Delete) {
                DeletionDialog(
                    title = localization.removalOfAbsenceStr(),
                    description = localization.descriptionOfRemovalStr(),
                    positiveButtonText = localization.deleteStr(),
                    negativeButtonText = localization.canselStr(),
                    state = screenState,
                    dimensions = dimensions,
                    onNegativeButtonClick = {
                        screenModel.clearErrors()
                    },
                    onPositiveButtonClick = rememberSafe { ->
                        screenModel.removeLeavingDetail(
                            (screenState as TimeTrackingScreenModel.State.Delete).leavingDetails
                        )
                    }
                )
            }
            LazyColumn(
                modifier = Modifier
                    .fillMaxSize()
                    .background(color = BackgroundBase)
            ) {
                item(key = TimeTrackingScreenItems.HEADER) {
                    Row(
                        modifier = horizontalPadding.padding(top = dimensions.verticalMedium),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Text(
                            text = localization.timeTrackingStr(),
                            style = BoldMontserrat22.copy(color = BlackDefaultText)
                        )
                        BetaDie(modifier = Modifier.padding(dimensions.horizontalSmall))
                    }
                    SpacerHeight(height = dimensions.verticalMedium)
                }

                item(key = TimeTrackingScreenItems.ENTER_HOURS) {
                    Text(
                        modifier = horizontalPadding,
                        text = localization.enterHoursStr(),
                        style = SemiBoldMontserrat22
                    )
                    SpacerHeight(height = dimensions.verticalSmall)
                }

                item(key = TimeTrackingScreenItems.ENTERING_AND_MISSING_HOURS_CONTENT) {
                    CustomTabRowWithEnteringAndMissingHoursContent(
                        projectList = state.projectDetails,
                        currentMonth = state.currentMonth,
                        typesAbsence = listOf(
                            LeaveType.SickLeave,
                            LeaveType.Idle,
                            LeaveType.Training,
                            LeaveType.Vacation
                        ),
                        state = screenState,
                        localization = localization,
                        onSaveEnteringHoursSpentOnWork = rememberSafe { projectDetails ->
                            screenModel.saveProjectDetails(
                                projectDetails = projectDetails
                            )
                        },
                        onSaveMissingHours = rememberSafe { leavingDetails ->
                            screenModel.saveLeavingDetails(
                                leavingDetails = leavingDetails
                            )
                        },
                        onMonthChange = rememberSafe { selectedMonth ->
                            screenModel.changeHoursContext(selectedMonth, state.currentYear)
                        },
                        rateMultiplier = state.rate
                    )
                    SpacerHeight(height = dimensions.verticalXXlarge)
                }

                item(key = TimeTrackingScreenItems.MONTH_YEAR_FOR_HOURS_SPENT) {
                    MonthYearForHoursSpent(
                        modifier = horizontalPadding.animateItemPlacement(),
                        text = localization.hoursForStr(),
                        onClick = rememberSafe { monthId, year ->
                            screenModel.changeHoursContext(monthId = monthId, year = year)
                        },
                        currentYearId = state.currentYear,
                        currentMonthId = state.currentMonth
                    )
                    SpacerHeight(height = dimensions.verticalLarge)
                }

                item(key = TimeTrackingScreenItems.CIRCULAR_DIAGRAM) {
                    CustomCircularDiagram(
                        modifier = horizontalPadding.animateItemPlacement(),
                        projectList = state.projectsForDiagram,
                        onBackClick = rememberSafe { -> screenModel.backPressedDiagram() },
                        onNextClick = rememberSafe { -> screenModel.nextPressedDiagram() },
                        isBackEnabled = state.isBackDiagramEnabled,
                        isNextEnabled = state.isNextDiagramEnabled,
                        totalTime = state.normHours
                    )
                    SpacerHeight(height = dimensions.verticalLarge)
                }

                item(key = TimeTrackingScreenItems.PROJECTS_AND_LEAVING_LIST) {
                    CustomTabRow(
                        spaceBetweenTabAndPager = dimensions.verticalMedium,
                        horizontalSpaceBetweenContent = dimensions.horizontalMedium,
                        isBeyondBoundsPageCount = false,
                        tabs = listOf(
                            localization.hoursForStr() to {
                                ProjectsInfoList(
                                    onIconClick = rememberSafe { selectedProjectDetails ->
                                        screenModel.showEditProjectDetailsDialog(selectedProjectDetails)
                                    },
                                    projectsInfoList = state.projectDetails
                                )
                                SpacerHeight(height = dimensions.verticalLarge)
                            },
                            localization.absenceStr() to {
                                LeavingDetailsList(
                                    leavingDetailsList = state.leavingDetails,
                                    onEditIconClick = rememberSafe { selectedLeavingDetails ->
                                        screenModel.showEditLeavingDetailsDialog(selectedLeavingDetails)
                                    },
                                    onDeleteIconClick = rememberSafe { selectedLeavingDetails ->
                                        screenModel.showDeletionDialog(selectedLeavingDetails)
                                    }
                                )
                                SpacerHeight(height = dimensions.verticalLarge)
                            }
                        )
                    )
                }
            }
        }
    }
}

@Composable
fun DeletionDialog(
    state: TimeTrackingScreenModel.State,
    title: String,
    dimensions: Dimensions,
    description: String,
    positiveButtonText: String,
    negativeButtonText: String,
    modifier: Modifier = Modifier,
    onNegativeButtonClick: () -> Unit,
    onPositiveButtonClick: () -> Unit
) {
    DialogWrapper(
        modifier = modifier.padding(horizontal = dimensions.horizontalMedium),
        showDialog = state is TimeTrackingScreenModel.State.Delete,
        onDismissRequest = { /* Do nothing */ }
    ) {
        WarningDialog(
            modifier = Modifier.padding(dimensions.horizontalMedium),
            title = title,
            description = description,
            isLoading = state is TimeTrackingScreenModel.State.Loading,
            confirmButtonText = positiveButtonText,
            cancelButtonText = negativeButtonText,
            onConfirmButtonClick = onPositiveButtonClick,
            onCanselButtonRequest = onNegativeButtonClick
        )
    }
}

@Composable
fun CustomTabRowWithEnteringAndMissingHoursContent(
    onSaveEnteringHoursSpentOnWork: (projectDetails: UiProjectDetails) -> Unit,
    onSaveMissingHours: (leavingDetails: UiLeavingDetails) -> Unit,
    currentMonth: Int,
    state: TimeTrackingScreenModel.State,
    localization: Localization,
    onMonthChange: (selectedMonth: Int) -> Unit,
    projectList: List<UiProjectDetails>,
    typesAbsence: List<LeaveType>,
    rateMultiplier: Double
) {
    val dimensions = LocalDimensions.current

    CustomTabRow(
        spaceBetweenTabAndPager = dimensions.verticalMedium,
        horizontalSpaceBetweenContent = dimensions.horizontalMedium,
        tabs = listOf(
            localization.hoursForStr() to {
                EnteringHoursSpentOnWork(
                    projectList = projectList,
                    onSaveProjectDetails = onSaveEnteringHoursSpentOnWork,
                    onMonthChange = onMonthChange,
                    isLoading = state is TimeTrackingScreenModel.State.Loading,
                    currentMonth = currentMonth
                )
            },
            localization.absenceStr() to {
                MissingHours(
                    modifier = Modifier.fillMaxSize(),
                    typesAbsence = typesAbsence,
                    isLoading = state is TimeTrackingScreenModel.State.Loading,
                    onSaveLeavingDetails = onSaveMissingHours,
                    rateMultiplier = rateMultiplier
                )
            }
        ),
        isBeyondBoundsPageCount = false
    )
}

enter image description here

https://www.veed.io/view/85492348-dd0a-4f5b-955b-4e949b31b3bb?panel=share

0

There are 0 best solutions below