Android Jetpack Compose MVVM
Tags: android, jetpackcompose, mvvm, cleanarchitecture
Published: 2022-11-20 01:56:13

I will show you how I've created the screen views with `Jetpack Componse` for this [example](https://github.com/janirefdez/JetpackComposeMVVM)

![Jetpack Compose App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvncimbyuhhmstcn1e2v.gif)

First, lets explain the basics

## What is MVVM architecture?

Model — View — ViewModel (MVVM) is the industry-recognized software architecture pattern that overcomes all drawbacks of MVP and MVC design patterns. MVVM suggests separating the data presentation logic(Views or UI) from the core business logic part of the application. 

The separate code layers of MVVM are:
- **Model**: This layer is responsible for the abstraction of the data sources. Model and ViewModel work together to get and save the data.
- **View**: The purpose of this layer is to inform the ViewModel about the user’s action. This layer observes the ViewModel and does not contain any kind of application logic.
- **ViewModel**: It exposes those data streams which are relevant to the View. Moreover, it serves as a link between the Model and the View.

## What is Jetpack Compose?
Jetpack Compose is Android’s recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.
______________________________________________________________
**Now we are ready to start with the project!**

*Note: In this article I will only explain how the screen views have been defined. If you want to know more, you can check the whole project on Github: [JetpackComposeMVVM](https://github.com/janirefdez/JetpackComposeMVVM)

![Project structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j7hn51ujc8jm9w8fhv1.png)

### Jetpack Compose

To be able to display a list of Categories we first need to define how one category should be displayed. In this case we have the following (code below images):

![CategoryItem](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yorme44ewzjti5s05o18.png)

We also need to understand what are `Rows` and `Columns` in Jetpack Compose:
- Row: It arranges the views horizontally. 
- Column: It arranges the views vertically. 

![Row and Columns in Jetpack Compose](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tfy6w6cis3g7yt3ft9kq.png)


In this case we have a Row to display the `Image`, `Title` and `Description`. But we also have a column which contains the `Title` and `Description`.

The code for displaying a Category Item is the following:

```
@Composable
fun CategoryItem(category: Category, context: Context, onClickListener: (Category) -> Unit) {
    Card(
        modifier = Modifier
            .padding(8.dp, 4.dp)
            .fillMaxWidth()
            .height(300.dp)
            .clickable { onClickListener.invoke(category) },
        shape = RoundedCornerShape(25.dp),
    ) {
        Surface(
            color = Color.LightGray
        ) {

            Row(
                Modifier
                    .padding(4.dp)
                    .fillMaxSize()
            ) {

                Image(
                    painter = rememberAsyncImagePainter(
                        model = category.strThumb,
                        imageLoader = ImageLoader.Builder(context).crossfade(true).build()
                    ),
                    contentDescription = category.str,
                    modifier = Modifier
                        .fillMaxHeight()
                        .weight(0.2f)
                )
                Column(
                    verticalArrangement = Arrangement.Center,
                    modifier = Modifier
                        .padding(4.dp)
                        .fillMaxHeight()
                        .weight(0.8f)
                ) {
                    Text(
                        text = category.str,
                        style = MaterialTheme.typography.titleMedium,
                        fontWeight = FontWeight.Bold
                    )
                    Text(
                        text = category.strDescription,
                        style = MaterialTheme.typography.bodyMedium
                    )
                }
            }
        }
    }
}
```

Once we have how one category should look like, we can define how the list should be displayed:

```
@Composable
fun CategoryList(
    categoryList: List<Category>,
    context: Context,
    onClickListener: (Category) -> Unit
) {
    LazyColumn {
        itemsIndexed(items = categoryList) { _, item ->
            CategoryItem(category = item, context, onClickListener)
        }
    }
}
```

In this case we define a `LazyColumn` which is a vertically scrolling list that only composes and lays out the currently visible items. It’s similar to a Recyclerview in the classic Android View system.

The result is the following:

![Category List](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ce9tnxwn5mvhxkhnxvc1.jpg)

Now we need to call CategoryList method in the MainActivity:

```
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    private val viewModel: MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MealRecipesTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    CategoryList(categoryList = viewModel.categoryList, context = this) {
                        val intent = Intent(this@MainActivity, DetailActivity::class.java).apply {
                            putExtra(DetailActivity.DETAIL_CATEGORY, it)
                        }
                        intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
                        startActivity(intent)
                    }
                    viewModel.getCategories()

                }
            }
        }
    }
}
```

Note that when a category is clicked, another view will be displayed with the information of the category selected. This can be done because in the CategoryItem compose function we have:

```
.clickable { onClickListener.invoke(category) }
```

When clicking on a Category we go to another activity (there is an `Intent` in the MainActivity that will be called when the category is clicked). 

The new activity, DetailActivity, displays again the `Title`,`Description` and the `Image`. 

The compose function looks like this:

![Detail Category View](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vloxku5aycp5cxfzc9sx.png)

```
@Composable
fun CategoryItem(category: Category, context: Context) {
    Surface(
        modifier = Modifier
            .padding(4.dp)
            .fillMaxHeight(),
        color = Color.White,
    ) {
        Column(
            verticalArrangement = Arrangement.Top,
            modifier = Modifier
                .padding(20.dp)
                .fillMaxHeight()
        ) {

            Text(
                text = category.str,
                style = MaterialTheme.typography.titleMedium,
                fontWeight = FontWeight.Bold,
                modifier = Modifier
                    .padding(25.dp)
            )
            Image(
                painter = rememberAsyncImagePainter(
                    model = category.strThumb,
                    imageLoader = ImageLoader.Builder(context).crossfade(true).build()
                ),
                contentDescription = category.str,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 25.dp)
            )
            Text(
                text = category.strDescription,
                style = MaterialTheme.typography.bodyMedium,
                modifier = Modifier
                    .padding(25.dp)
            )

        }


    }
}
```

Then we need to call CategoryItem function in the DetailActivity:

```
@AndroidEntryPoint
class DetailActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val category = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            intent.getSerializableExtra(DETAIL_CATEGORY, Serializable::class.java) as Category
        } else {
            @Suppress("DEPRECATION")
            intent.getSerializableExtra(DETAIL_CATEGORY) as Category
        }
        setContent {
            MealRecipesTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    CategoryItem(category = category, context = this@DetailActivity)
                }
            }
        }
    }

    companion object {
        const val DETAIL_CATEGORY = "detail_category"
    }
}
```
The result is the following: 

![Category in detail](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/49649o5c7pv8j3wvcfx0.jpg)

And that's all!! That's how you create screen views with Jetpack Compose. Hope you enjoyed!!

If you want to check the whole project here you have the link: [JetpackComposeMVVM](https://github.com/janirefdez/JetpackComposeMVVM)

Don't forget to like and share! Thank you! :)