Book Image

Kickstart Modern Android Development with Jetpack and Kotlin

By : Catalin Ghita
5 (1)
Book Image

Kickstart Modern Android Development with Jetpack and Kotlin

5 (1)
By: Catalin Ghita

Overview of this book

With Jetpack libraries, you can build and design high-quality, robust Android apps that have an improved architecture and work consistently across different versions and devices. This book will help you understand how Jetpack allows developers to follow best practices and architectural patterns when building Android apps while also eliminating boilerplate code. Developers working with Android and Kotlin will be able to put their knowledge to work with this condensed practical guide to building apps with the most popular Jetpack libraries, including Jetpack Compose, ViewModel, Hilt, Room, Paging, Lifecycle, and Navigation. You'll get to grips with relevant libraries and architectural patterns, including popular libraries in the Android ecosystem such as Retrofit, Coroutines, and Flow while building modern applications with real-world data. By the end of this Android app development book, you'll have learned how to leverage Jetpack libraries and your knowledge of architectural concepts for building, designing, and testing robust Android applications for various use cases.
Table of Contents (17 chapters)
1
Part 1: Exploring the Core Jetpack Suite and Other Libraries
7
Part 2: A Guide to Clean Application Architecture with Jetpack Libraries
13
Part 3: Diving into Other Jetpack Libraries

Exploring lists with Compose

In the previous section, we built a Compose-based screen that features a list of restaurants. However, if you run the application or preview the screen in interactive mode, you will notice that the list doesn't scroll. This is a huge inconvenience that we will address in this section by adding scroll capabilities to our Column composable.

Next, we will specify why Column is suited for static content, whereas if the list is large and its size is dynamic or dictated by the server's response, we should use lazy composables. We will explore a variety of lazy composables and understand why they are better suited for large lists.

To summarize, this section will cover the following topics:

  • Adding scrolling to the Column composable
  • Introducing lazy composables
  • Using LazyColumn to display restaurants

Let's start by adding scrolling capabilities to our RestaurantsScreen composable.

Adding scrolling to the Column composable

Our list of restaurants is long, and it can't scroll. This is a bad user experience, so let's fix it.

Let's make the Column scrollable by passing a Modifier.verticalScroll modifier that receives a ScrollState:

@Composable
fun RestaurantsScreen() {
   Column(Modifier.verticalScroll(rememberScrollState())) {
       ...
   }
}

We want the scrolling position to be retained across recompositions. That's why, by passing rememberScrollState to the verticalScroll modifier, we ensure that every time the UI recomposes, the scroll state is remembered and retained. The rememberScrollState persistence mechanism is similar to the remember { } block, which we used previously to retain the TextField's state across recompositions.

Now, you can Run the app or preview it in Interactive mode and check out the scrolling effect.

However, we have one final issue with our Column that is related to how Column lays and composes its elements. Let's dive into that now and try to find a better alternative.

Introducing lazy composables

Let's take a short break from our restaurant app and try to think of a better way of handling large lists. Using Row or Column for displaying long lists of items, or maybe a list of unknown size, can prove detrimental to your UI and impact your app's performance. This happens because Row and Column render or lay all their children out, whether they are visible or not. They are good for displaying static content, yet passing a large list can cause your UI to become laggy or even unusable.

Two lazy composables called LazyColumn and LazyRow come to your rescue since they only compose or output those items that are currently visible on the screen, hence the term lazy. So, as you can see, they are somehow similar to the old RecyclerView.

As the only difference between Row and Column was the way children were laid out on the screen – horizontally or vertically – the same thing applies with LazyRow and LazyColumn. These lazy composables lay their children out horizontally or vertically and provide scrolling capabilities out of the box. As they only render the visible items, lazy composables are a much better fit for large lists.

Yet, lazy composables are different than the regular composables that we've used so far. That's mainly because instead of accepting @Composable content, they expose a domain-specific language (DSL) defined by a LazyListScope block:

@Composable
fun LazyColumn(
   ...
   content: LazyListScope.() -> Unit
) { … }

The LazyListScope DSL allows us to describe the item contents that we want to be displayed as part of the list. The most commonly used ones are item() and items(). Such example usage of LazyColumn that makes use of DSL is as follows:

LazyColumn {
   item() {
       Text(text = "Custom header item")
   }
   items(myLongList) { myItem ->
       MyComposable(myItem)
   }
   item(2) {
       Text(text = "Custom footer item")
   }
}

item() adds a single composable element to the list, while items() can receive not only a standalone list of content such as myLongList but also an Int, which will add the same item multiple times.

The code that we featured previously should render a vertical list that contains the following:

  • A header Text composable
  • A list of MyComposable composables that are the same size as myLongList
  • Two Text footer composables

Returning from the DSL world, a noteworthy argument for the lazy composables is contentPadding, which allows you to define horizontal/vertical padding surrounding your list. This argument expects a PaddingValues object – we will use it soon; don't worry!

Now, we will soon receive the restaurants from a remote server, which means we don't know the size of the list, so it's time to implement such a lazy composable in our Restaurants application as well.

Using LazyColumn to display restaurants

We are currently using Column to display our dummyRestaurants list. We know why that's not the best practice, so to optimize our UI for dynamic content, we will replace it with LazyColumn so that we can continue displaying the restaurants vertically.

Go back to the RestaurantsScreen.kt file and, inside of the RestaurantScreen composable, replace the Column composable with LazyColumn:

@Composable
fun RestaurantsScreen() {
   LazyColumn(
       contentPadding = PaddingValues(
           vertical = 8.dp,
           horizontal = 8.dp)) {
       items(dummyRestaurants) { restaurant ->
           RestaurantItem(restaurant)
       }
   }
}

We've used its DSL and specified the items properties that should populate our LazyColumn by passing the dummyRestaurants list. We obtained access to each item as a restaurant of type Restaurant and rendered it through a RestaurantItem composable.

We also added additional padding through the contentPadding argument to our LazyColumn by passing a PaddingValues object where we configured the vertical and horizontal padding.

You can now Run the app and check out the scrolling effect. In our case, the output is the same, yet if we were to test the app with a very long list of restaurants, we would have a much smoother scroll effect and a better UI experience with LazyColumn than with Column.

We've done it! We've built our first Compose-based app from scratch while exploring tons of composable functions. We've added a list that scrolls beautifully, and we can now be proud of the result!