Learn with code: Jetpack Compose — States and Custom shapes (Part 2)
Learn to create custom shapes and handle states in Jetpack Compose

This is part of a multi-part series about learning to use Jetpack Compose through code. This part of the series will be focusing on building the game details screen and also covering the test cases for this screen.
Other articles in this series:
Note: All the images and game data in the article are taken from the awesome RAWG API.
Dissecting the details screen

There are a few components here and I have highlighted only the main ones in the above image. Let’s see what each of these components are from top to bottom: Game image, play button, game title, game genre, then below that on the same row you have released info and rating info, game description, show more/less toggle, platforms, stores, game developer and finally the game publisher.
Phew! Quite a few components one below the other! By the time we finish designing this screen you will see how easy and fast we can do this. Let’s code!
Column
As you would have guessed by now, we can use a Column
to design this layout as all the components are one below the other.
Column is a layout composable that places its children in a vertical sequence.
Running the app now will give you the following screen:

The one thing you will notice right away is that your screen is not scrollable! To enable scrolling we can make use of verticalScroll
modifier.
The
verticalScroll
andhorizontalScroll
modifiers provide the simplest way to allow the user to scroll an element when the bounds of its contents are larger than its maximum size constraints.
Let’s go ahead and add it to our Column
.

As promised, it hardly took us a few minutes to build this! Now, onto the interesting parts of the design!
Game Image and Play Button
The play button is overlapping the game image. First, let’s style our play button.
Now, let’s align the play button to the bottom of our game image. To do this, we can simply wrap our GameImage
and PlayTrailer
inside a ConstraintLayout
and provide the necessary constraints.
Let’s preview this and see what we got so far.

We have almost got our desired result but the GameImage
is missing something.
Custom shapes
In traditional XML layouts you could shape your views using a custom drawable or you could use a ShapeableImageView
for shaping your images.
Compose already provides some basic shapes like RoundedCornerShape
, CircleShape
, CutCornerShape
but if you want to draw your own custom shape you can easily do it by extending the Shape
interface. Here, you will have to override the createOutline() method which expects you to return anOutline
.
If you look at the code, Outline
is a sealed class
which has 3 subclasses:
Rectangle
— used for a rectangular areaRounded
— used for rectangular area with rounded cornersGeneric
— this let’s you define your ownPath
As you would have guessed, we will using the generic shape and providing it with our custom path.
Let’s now create our custom path by implementing the drawArchPath() method. Our image needs a rounded bottom shape. You can visualise the path as follows:

Let’s add the shape we just created to our GameImage
.
Let’s preview our GameImage
and PlayTrailer
now and see what we get.

Looks pretty good! I will leave designing the release and rating info to you. Let me know about your design in the comments!
As you can see so far, the game description is pretty long and not everyone will be interested in reading it all. Let’s create a show more/less toggle to handle this use case.
Show more/less toggle — Compose States
One way we can handle this show more/less use case is by simply changing the maxLines
attribute of your Text
composable. To do this, we can basically make use of States to tell our composable to recompose whenever the maxLines
value changes.
One more thing to consider here is that, we need to display Show More only if the game description overflows maxLines
. To do this, we can make use of onTextLayout
callback. Let’s code!
State in an app is any value that can change over time.
Let’s run the app now and see what we get.

Looks pretty good! Again, I am leaving the designing of release and rating info to you.
Testing the composables
Now that we have our game details screen ready, let’s go ahead and write a test for it.
The test is pretty self-explanatory. We are passing in fake GameDetailsEntity
to our composable and then asserting that all the views are displayed.
One thing to note here is that, if your view is off-screen and you are trying to assert that the view is displayed then your test case will fail. For this, you can first scroll to your view using performScroll()
before making your assertion.
I will leave the rest of the test cases to you. Use all your creativity and make sure you cover as many test cases as possible for this screen!
You can find the complete source code with all the tests for the details screen in this repository.
What’s next?
In this post we have designed our game details screen and also tested the same. In the next post, let’s explore and build the game videos screen and also the different UI tests for this screen. See you there:
Thanks for reading! If you liked the article please do leave a clap 👏 and don’t forget to subscribe and follow to get regular updates! :) You can also connect with me on LinkedIn. I would love to see your designs and tests!
We at ShareChat are constantly working on making our apps better across all our clients: Android, iOS and Web. If you are interested in building ShareChat/Moj or solving interesting problems, let us know by applying here!
Additional Resources
- Android Developers website to dive deeper into column in compose
- Android Developers website to learn more about shapes
- Android Developers website to understand more about states in compose
- Android Developers website to dive deeper into testing in jetpack compose