DEV Community

Janire Fernandez
Janire Fernandez

Posted on • Edited on

Android Jetpack Compose MVVM

I will show you how I've created the screen views with Jetpack Componse for this example

Jetpack Compose App

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

Project structure

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

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

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
                    )
                }
            }
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

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)
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

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

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()

                }
            }
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

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) }


Enter fullscreen mode Exit fullscreen mode

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



@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)
            )

        }


    }
}


Enter fullscreen mode Exit fullscreen mode

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"
    }
}


Enter fullscreen mode Exit fullscreen mode

The result is the following:

Category in detail

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

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

Top comments (0)