Shady Abdulmunim
Shady Abdulmunim

Reputation: 45

Level difference in HorizontalPager Compose

I have two composable functions "ImageAndDescription"and "PageButtonsAndIndicator" implemented in a third composable file named "FullPage". I am using HorizontalPager in the "ImageAndDescription" composable but the problem is that even though I am setting a weight modifier for both composables in the "FullPage" file, the horizontal pager shows a level difference between each page, which leads to a very bad appearance

GIF https://jumpshare.com/s/pc4AyckmCxbqxFs7hC5Z

ImageAndDescription File

package com.example.al_jazeeranews.presentation.guide.composables

import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.al_jazeeranews.presentation.guide.page.Page
import com.example.al_jazeeranews.presentation.guide.page.pages
import com.example.al_jazeeranews.presentation.utils.PaddingConstants.MEDIUM_PADDING
import com.example.al_jazeeranews.presentation.utils.PaddingConstants.NORMAL_PADDING
import com.example.al_jazeeranews.ui.theme.AlJazeeraNewsTheme

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ImageAndDescription(
    modifier: Modifier = Modifier, pages: List<Page>,
    pagerState: PagerState = rememberPagerState { pages.size }
) {
    Column(
        modifier
            .fillMaxSize()
    ) {

        HorizontalPager(state = pagerState) { pageNumber ->
            PageItem(pages[pageNumber])
        }

    }

}

@Preview(backgroundColor = 0xFF1C1C1C, showBackground = true, uiMode = UI_MODE_NIGHT_YES)
@Preview(backgroundColor = 0xFFE7F4FD, uiMode = UI_MODE_NIGHT_NO, showBackground = true)
@Composable
fun PageItem(page: Page = pages[2]) {
    AlJazeeraNewsTheme {
        Column(
            Modifier
                .fillMaxWidth()

        ) {
            Image(
                modifier = Modifier
                    .fillMaxHeight(0.6f)
                    .fillMaxWidth(),
                painter = painterResource(page.imageRes),
                contentDescription = page.title,
                contentScale = ContentScale.Crop
            )

            Spacer(modifier = Modifier.height(NORMAL_PADDING))

            Text(
                text = page.title,
                style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onPrimary),

                modifier = Modifier.padding(horizontal = MEDIUM_PADDING)
            )

            Spacer(modifier = Modifier.height(NORMAL_PADDING))

            Text(
                text = page.description,
                style = MaterialTheme.typography.bodySmall.copy(color = MaterialTheme.colorScheme.onPrimary),
                modifier = Modifier.padding(horizontal = MEDIUM_PADDING)
            )
        }

    }
}

PageButtonsAndIndicator

package com.example.al_jazeeranews.presentation.guide.composables

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.al_jazeeranews.presentation.utils.PaddingConstants
import com.example.al_jazeeranews.ui.theme.AlJazeeraNewsTheme
import com.example.al_jazeeranews.ui.theme.SelectedBlue
import com.example.al_jazeeranews.ui.theme.UnselectedGrey
import com.example.al_jazeeranews.ui.theme.White

@Preview(showBackground = true)
@Composable
fun PageButtonsAndIndicator(
    modifier: Modifier,
    buttonsString: List<String> = listOf("Back", "Next"),
    numberOfPages: Int = 3,
    currentPage: Int = 1,
    onBackClick: () -> Unit = {},
    onNextClick: () -> Unit = {}
) {
    AlJazeeraNewsTheme {

        Row(
            modifier
                .fillMaxSize()
                .padding(PaddingConstants.MEDIUM_PADDING)
                .padding(bottom = PaddingConstants.NORMAL_PADDING),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                repeat(numberOfPages) { index ->
                    Box(
                        Modifier
                            .size(8.dp)
                            .clip(CircleShape)
                            .background(
                                color = if (index == currentPage) SelectedBlue
                                else UnselectedGrey
                            )
                    )
                }
            }
            Row(
                horizontalArrangement = Arrangement.spacedBy(8.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                if (currentPage != 0) {
                    TextButton(onClick =  onBackClick ) {
                        Text(
                            text = buttonsString[0],
                            style = MaterialTheme.typography.bodySmall.copy(
                                color = UnselectedGrey, fontWeight = FontWeight.SemiBold
                            )
                        )
                    }
                }

                    Button(
                        onClick = onNextClick,
                        shape = RoundedCornerShape(8.dp),
                        colors = ButtonDefaults.buttonColors(
                            contentColor = White,
                            containerColor = SelectedBlue
                        )
                    ) {
                        Text(text = buttonsString[1], style = MaterialTheme.typography.bodySmall)
                    }
            }
        }
    }


}

FullPage File

package com.example.al_jazeeranews.presentation.guide.composables

import android.annotation.SuppressLint
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.al_jazeeranews.presentation.guide.page.pages
import kotlinx.coroutines.launch

@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalFoundationApi::class)
@Preview
@Composable
fun FullPage(modifier: Modifier = Modifier) {
    Column(
        Modifier
            .fillMaxSize()
            .background(color = MaterialTheme.colorScheme.background)
    ) {
        val pagerState = rememberPagerState { pages.size }
        Column(Modifier.weight(0.8f)) {
            ImageAndDescription(
                pages = pages,
                pagerState = pagerState,
                modifier = Modifier.weight(8f)
            )
        }

        val buttonsString by derivedStateOf {
            when {
                pagerState.currentPage == 0 -> listOf("", "Next")
                pagerState.currentPage < pages.size - 1 -> listOf("Back", "Next")
                pagerState.currentPage == pages.size - 1 -> listOf("Back", "Get Started")
                else -> listOf("", "")
            }
        }

        val coroutineScope = rememberCoroutineScope()

        Column(Modifier.weight(0.2f)) {
            PageButtonsAndIndicator(
                Modifier,
                buttonsString,
                pages.size,
                pagerState.currentPage,
                {
                    coroutineScope.launch {
                        pagerState.animateScrollToPage(pagerState.currentPage - 1)
                    }
                },
                {
                    coroutineScope.launch {
                        if (pagerState.currentPage != pages.size - 1)
                            pagerState.animateScrollToPage(pagerState.currentPage + 1)
                    }
                }

            )
        }
    }

}

Upvotes: 1

Views: 71

Answers (1)

Shady Abdulmunim
Shady Abdulmunim

Reputation: 45

I actually discovered the problem, it was in the PageItem in the Image and Description file

fun PageItem(page: Page = pages[2]) {
    AlJazeeraNewsTheme {
        Column(
            Modifier
                .fillMaxWidth()
                .fillMaxHeight() //I should've added that to fill the maximum
                                 //specified size

        ) 

Upvotes: 0

Related Questions