Badran
Badran

Reputation: 535

How to implement horizontal scroller in android jetpack compose ui?

I'm trying to achieve a horizontal scroll view using jetpack compose like below:

Pic Of what I want

But I couldn't find any solution to set the width of cell to take width of screen with 16dp margin, and that's what I'm getting:

Pic Of what I'm getting

This the my code:

private val imageList : Array<Effect<Image>> =arrayOf(
        imageResource(R.drawable.maldive),
        imageResource(R.drawable.maldiveone),
        imageResource(R.drawable.maldivetwo))

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            createList()
        }


    }

    @Composable
    fun createList(){
        MaterialTheme() {
            HorizontalScroller(){
                Row(crossAxisSize = LayoutSize.Expand) {
                    (0..3).forEachIndexed { _, i ->
                            populateListItem(i)
                    }
                }
            }
        }
    }
    @Composable
    fun populateListItem(index: Int){
                Column(crossAxisSize = LayoutSize.Wrap, modifier = Spacing(16.dp)) {
                    Card(elevation = 0.dp, shape = RoundedCornerShape(8.dp, 8.dp, 8.dp, 8.dp)) {
                        val im: Image = +imageList[index.rem(3)]
                        Container(expanded = true,height = 180.dp)
                         {
                            DrawImage(image = im)
                        }
                    }
                    HeightSpacer(height = 16.dp)
                    Text("Maldive $index",
                        style = +themeTextStyle { h6 })
                    Text("Enjoy Our $index Resort!",
                        style = +themeTextStyle { body2 })
        }
    }

Upvotes: 7

Views: 14758

Answers (4)

nglauber
nglauber

Reputation: 23844

Just adding my solution here...

@Composable
fun HorizontalScrollScreen() {
    // replace with your items...
    val items = (1..10).map { "Item $it" } 
    // a wrapper to fill the entire screen
    Box(modifier = Modifier.fillMaxSize()) { 
        // BowWithConstraints will provide the maxWidth used below
        BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
            // LazyRow to display your items horizontally
            LazyRow(
                modifier = Modifier.fillMaxWidth(),
                state = rememberLazyListState()
            ) {
                itemsIndexed(items) { index, item ->
                    Card(
                        modifier = Modifier
                            .height(100.dp)
                            .width(maxWidth) // here is the trick
                            .padding(16.dp)
                    ) {
                        Text(item) // card's content
                    }
                }
            }
        }
    }
}

This implementation works, but you will not have the snap behavior. This behavior is not supported out-of-the-box by LazyRow (there's a feature request for that https://issuetracker.google.com/issues/166590434), but you can try to implement this using the flingBehavior parameter.

Or for now, you can use the Accompanist Pager library. https://github.com/google/accompanist/tree/main/pager

Upvotes: 1

Karl Einstein
Karl Einstein

Reputation: 75

You want your child item's width is match_parent right?. You should use com.google.accompanist:accompanist-pager:0.8.1 from google. It will look like horizontal viewpager

Link: https://google.github.io/accompanist/pager/

Upvotes: 0

Victor Ude
Victor Ude

Reputation: 433

From JetPack Compose 1.0.0-beta01 use BoxWithConstraints to get the screen's maximum width and height and pass that along to @Composable annotated functions and Modifiers as needed.

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth)
    }
}

docs: https://developer.android.com/jetpack/compose/layout#constraints

Upvotes: 0

Gopi S
Gopi S

Reputation: 643

The key is resources.displayMetrics.widthPixels, this will do the magic. replace your populateListItem function with below, it will work

@Composable
fun populateListItem(index: Int) {
    val padding = 16
    val dm = resources.displayMetrics
    val cardWidth = dm.widthPixels/dm.density - 16 * 2 // 2 is multiplied for left and right padding
    Column(crossAxisSize = LayoutSize.Wrap, modifier = Spacing(padding.dp)) {
        Card(elevation = 0.dp, shape = RoundedCornerShape(8.dp, 8.dp, 8.dp, 8.dp)) {
            val im: Image = +imageList[index.rem(3)]
            Container(width = cardWidth.dp, height = 180.dp)
            {
                DrawImage(image = im)
            }
        }
        HeightSpacer(height = 16.dp)
        Text("Maldive $index",
            style = +themeTextStyle { h6 })
        Text("Enjoy Our $index Resort!",
            style = +themeTextStyle { body2 })
    }
}

Upvotes: 1

Related Questions