Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architectural Pattern for Large-Scale Jetpack Compose Apps

Architectural Pattern for Large-Scale Jetpack Compose Apps

Jetpack Compose is a powerful tool that can help app developers build apps in a far more dynamic, complex, and polished way. In this talk, you’ll learn how to simplify building big apps with Jetpack Compose! We'll explore the best ways to structure your app using architectural patterns. Find out which pattern suits your project, and how to make your app perform well. It's all about managing complexity and making your app development easier using different architectural patterns.

My agenda for the talk includes:

1. Introduction to problems in Large-Scale Jetpack Compose Apps
2. The Importance of Architectural Patterns
3. Common Architectural Patterns and their differences - MVC, MVP, MVVM, MVI
4. Choosing the Right Pattern
5. Implementing the Chosen Pattern

By following these strategies, you can make app development easier and handle complex tasks more efficiently. When you combine Jetpack Compose with the right architectural pattern, creating strong, fast-performing apps becomes simpler and more rewarding.

Bhoomi Vaghasiya Gadhiya

January 03, 2024
Tweet

More Decks by Bhoomi Vaghasiya Gadhiya

Other Decks in Technology

Transcript

  1. Ahmedabad Who Am I? 󰟲 • Android Developer • Technical

    Author in DroidCon Academy • Technical Blogger : Medium @bhoomigadhiya • Technical Speaker in community • Empowering Women In Tech
  2. Architectural Patterns • MVC • MVP • MVVM • MVI

    • And many more... On Column Title
  3. MVVM • View Notifies the ViewModel about different actions •

    View has reference to the ViewModel but ViewModel has NO information about View! • The Consumer(View) of the data should know about the producer(ViewModel), but producer doesn’t care, who consumes the data!
  4. ViewModel class MyViewModel : ViewModel() { private val _items =

    MutableLiveData<List<MyItem/>() val items: LiveData<List<MyItem/> get() = _items init { fetchItems() } private fun fetchItems() { viewModelScope.launch { delay(2000) //Network call _items.value = generateItemList() } } }
  5. MVVM class MyViewModel : ViewModel() { // Different LiveData for

    representing different UI states private val _isLoading = MutableLiveData<Boolean>() val isLoading: LiveData<Boolean> get() = _isLoading private val _contentData = MutableLiveData<String>() val contentData: LiveData<String> get() = _contentData private val _errorData = MutableLiveData<String>() val errorData: LiveData<String> get() = _errorData }
  6. MVI (Model-View-Intent) Model Intent View User triggers an action Send

    Intent to Model View observes State Display States
  7. sealed class MyViewState { object Loading(val isLoading: Boolean) : MyViewState()

    data class Content(val contentData: String) : MyViewState() data class Error(val errorData: String) : MyViewState() } MVI (Model-View-Intent)
  8. MVVM + Compose • Reactive and declarative UI • Jetpack

    integration • State Management • Easy migration
  9. Use remember class MyViewModel : ViewModel() { private val _cachedResult

    = mutableStateOf("Initial Value") val cachedResult: State<String> get() = _cachedResult fun heavyCalculation() { val result = //some heavy operation _cachedResult.value = result } }
  10. Use remember class MyViewModel : ViewModel() { private val _cachedResult

    = mutableStateOf("Initial Value") val cachedResult: State<String> get() = _cachedResult fun heavyCalculation() { val result = //some heavy operation _cachedResult.value = result } }
  11. Use remember @Composable fun MyComposable(viewModel: MyViewModel) { val cachedResult by

    remember { viewModel.cachedResult } Column{ Button(onClick = { viewModel.heavyCalculation() }) { Text("Calculate Result") } Text(text = "Cached Result: $cachedResult") } }
  12. Use remember @Composable fun MyComposable(viewModel: MyViewModel) { val cachedResult by

    remember { viewModel.cachedResult } Column{ Button(onClick = { viewModel.heavyCalculation() }) { Text("Calculate Result") } Text(text = "Cached Result: $cachedResult") } }
  13. Use Side Effects class MyViewModel : ViewModel() { private val

    _result = MutableLiveData<String>() val result: LiveData<String> get() = _result fun performAsyncOperation() { viewModelScope.launch { // Asynchronous operation delay(2000) _result.value = "Async operation completed: ${System.currentTimeMillis()}" } } }
  14. Use Side Effects class MyViewModel : ViewModel() { private val

    _result = MutableLiveData<String>() val result: LiveData<String> get() = _result fun performAsyncOperation() { viewModelScope.launch { // Asynchronous operation delay(2000) _result.value = "Async operation completed: ${System.currentTimeMillis()}" } } }
  15. Use Side Effects @Composable fun MyComposable(viewModel: MyViewModel) { val result

    by viewModel.result.observeAsState() // Trigger asynchronous operation when needed LaunchedEffect(result) { viewModel.performAsyncOperation() } Text(text = result /: "Loading//.") }
  16. Use Side Effects @Composable fun MyComposable(viewModel: MyViewModel) { val result

    by viewModel.result.observeAsState() // Trigger asynchronous operation when needed LaunchedEffect(result) { viewModel.performAsyncOperation() } Text(text = result /: "Loading//.") }
  17. Lazy Loading Without Key LazyColumn { items(items.size) { //content }

    } LazyColumn { items(items.size, key = { it }) { //content } } Lazy Loading With Key
  18. Lazy Loading Without Key LazyColumn { items(items.size) { //content }

    } LazyColumn { items(items.size, key = { it }) { //content } } Lazy Loading With Key
  19. For UI-Specific State: @Composable fun MyComposable() { var isExpanded by

    remember { mutableStateOf(false) } // Use isExpanded locally within this composable }
  20. For Business Logic & Data Management: class MyViewModel : ViewModel()

    { private val _items = mutableStateOf<List<String/>(emptyList()) val items: State<List<String/> get() = _items // Business logic to update items fun updateItems(newItems: List<String>) { _items.value = newItems } }
  21. @Composable fun MyComposable(viewModel: MyViewModel) { val items by remember {

    mutableStateOf(listOf( //items ))} LazyColumn { //show the items } Button { //Add the item } } Avoid using MutableStateOf
  22. @Composable fun MyComposable(viewModel: MyViewModel) { val items by remember {

    mutableStateOf(listOf( //items ))} LazyColumn { //show the items } Button { //Add the item } } Avoid using MutableStateOf
  23. @Composable fun MyComposable(viewModel: MyViewModel) { val items by viewModel.items.collectAsState() //

    Observe state changes LazyColumn { //show the items } Button { //Add the item } } Instead, use ViewModel
  24. @Composable fun MyComposable(viewModel: MyViewModel) { val items by viewModel.items.collectAsState() //

    Observe state changes LazyColumn { //show the items } Button { //Add the item } } Instead, use ViewModel
  25. class MyViewModel : ViewModel() { val item = MutableLiveData(MyItem(1,"Item 1"))

    ////. } @Composable fun MyComposable(viewModel: MyViewModel) { val item = viewModel.item //Direct access to LiveData Text(item.value/!.name) //Accessing LiveData value directly ////. } Avoid direct ViewModel access from Composable
  26. class MyViewModel : ViewModel() { val item = MutableLiveData(MyItem(1,"Item 1"))

    ////. } @Composable fun MyComposable(viewModel: MyViewModel) { val item = viewModel.item //Direct access to LiveData Text(item.value/!.name) //Accessing LiveData value directly ////. } Avoid direct ViewModel access from Composable
  27. class MyViewModel : ViewModel() { private val _item = MutableLiveData(MyItem(1,"Item

    1")) val item : LiveData<MyItem> get() = _item } @Composable fun MyComposable(viewModel: MyViewModel) { val item by viewModel.item.observeAsState() // Observe state changes Text(item.name) } Instead, observe state changes
  28. class MyViewModel : ViewModel() { private val _item = MutableLiveData(MyItem(1,"Item

    1")) val item : LiveData<MyItem> get() = _item } @Composable fun MyComposable(viewModel: MyViewModel) { val item by viewModel.item.observeAsState() // Observe state changes Text(item.name) } Instead, observe state changes
  29. Ahmedabad Resources • https://developer.android.com/jetpack/compose/performance/be stpractices • https://www.youtube.com/watch?v=cnU2zMnmmpg • https://www.youtube.com/watch?v=97BRLkicQd0 •

    https://speakerdeck.com/bkinya/modern-android-development-us ing-mvi-architecture • https://speakerdeck.com/lupsyn/mvvm-compose-utp-a-killer-com bination-for-successful-deliveries