-
Notifications
You must be signed in to change notification settings - Fork 119
Demonstrate KMP ViewModel on multiple screens #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This commit introduces Navigation3 for screen navigation and updates several dependencies: Android: - Updates compileSdk and targetSdk to 36 in androidApp build.gradle.kts. - Adds kotlinx-serialization plugin. - Implements basic navigation using Navigation3 for the ListScreen and CartScreen. - Adds CartScreen composable. - Integrates CartViewModel and AppContainer for dependency injection in CartScreen. - Updates dependencies in libs.versions.toml, including Navigation3 and kotlinx-serialization. iOS: - Adds ObservableViewModel for swiftui interop. - Updates iosApp to use NavigationStack and integrate cart navigation. - Adds CartViewModelStoreOwner for ios.
This commit refactors the CartScreen in both Android and iOS and removes the ObservableViewModel in iOS. Android: - Updates padding on CartScreen for better layout. - Centers items horizontally within the LazyColumn. iOS: - Removes ObservableViewModel.swift as it is no longer needed. - Clears mainViewModelStoreOwner on ContentView disappear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of Changes
Hello @cartland, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly enhances the application's architecture by introducing multi-screen navigation capabilities on both Android and iOS platforms. It demonstrates how to effectively use Kotlin Multiplatform (KMP) ViewModels to manage UI state across different screens, leveraging platform-specific navigation frameworks while maintaining a shared business logic layer. The changes also involve necessary SDK and dependency updates for modern Android and iOS development.
Highlights
- Multi-Platform Navigation Integration: Implemented distinct navigation solutions for Android (Jetpack Navigation 3) and iOS (SwiftUI NavigationStack) to support multi-screen flows within the application.
- Dedicated Cart ViewModel: Introduced a new
CartViewModelto manage cart-specific UI state and logic, separating it from theMainViewModelfor better modularity and adherence to KMP ViewModel patterns. - KMP ViewModel Demonstration: Showcased the effective use of Kotlin Multiplatform (KMP) ViewModels (
MainViewModelandCartViewModel) across different screens on both Android and iOS, demonstrating shared logic with platform-specific UI. - Android SDK & Dependency Updates: Upgraded Android
compileSdkandtargetSdkto 36 and integrated new Navigation3 andkotlinx-serializationdependencies to support modern Android development practices. - iOS Deployment Target Update: Increased the iOS deployment target to 16.6 to enable the use of SwiftUI's
NavigationStackfor improved navigation capabilities.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces multi-screen navigation using Jetpack Navigation 3 on Android and NavigationStack on iOS, demonstrating a shared ViewModel architecture in a KMP project. The changes are well-structured, separating concerns into different ViewModels and screens. My review focuses on improving navigation consistency, dependency management risks with snapshot versions, API usage in Compose, and ensuring robust dependency injection and view logic in both Android and iOS implementations.
| nav3Core = "1.0.0-alpha01" | ||
| lifecycleViewmodelNav3 = "1.0.0-alpha01" | ||
| kotlinxSerializationCore = "1.8.1" | ||
| material3AdaptiveNav3 = "1.0.0-SNAPSHOT" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| maven { | ||
| url = uri("https://androidx.dev/snapshots/builds/13617490/artifacts/repository") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ) | ||
|
|
||
| companion object { | ||
| val APP_CONTAINER_KEY = CreationExtras.Key<AppContainer>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| onNavBack = { | ||
| backStack.clear() | ||
| backStack.add(ListScreenKey) | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The navigation logic for the back button in the CartScreen's top app bar is inconsistent with the system back behavior. Currently, it clears the entire back stack and navigates to a new ListScreen. This is more of a "navigate up to home" action. The system back gesture, handled by onBack in NavDisplay, will simply pop the CartScreen off the stack. For a consistent user experience, consider using the same pop behavior for the in-app back button.
onNavBack = {
backStack.removeLastOrNull()
},| colors = TopAppBarColors( | ||
| containerColor = MaterialTheme.colorScheme.primary, | ||
| scrolledContainerColor = MaterialTheme.colorScheme.primary, | ||
| navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||
| titleContentColor = MaterialTheme.colorScheme.onPrimary, | ||
| actionIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||
| ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of manually constructing TopAppBarColors, it's recommended to use the default factory function from TopAppBarDefaults. This ensures that the colors are consistent with the MaterialTheme and handles different states (like disabled) correctly, making your UI more robust and easier to maintain.
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
),| ) { | ||
| val cartItemCount = cartState.cartDetails.sumOf { it.count } | ||
| Text( | ||
| text = "Cart has $cartItemCount items", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| LazyColumn { | ||
| LazyColumn( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| verticalArrangement = Arrangement.spacedBy(64.dp), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A vertical arrangement spacing of 64.dp between items in the LazyColumn is quite large and might not be what you intended. If this is a typo, you might want to adjust it to a more common value like 8.dp or 16.dp.
| verticalArrangement = Arrangement.spacedBy(64.dp), | |
| verticalArrangement = Arrangement.spacedBy(16.dp), |
| Observing(mainViewModel.homeUiState) { homeUIState in | ||
| ScrollView { | ||
| LazyVStack { | ||
| ForEach(homeUIState.fruitties, id: \.self) { value in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using id: \.self for ForEach requires Fruittie to be Hashable. While this might work if Fruittie is a class and you have unique instances, it's safer and more explicit to use a stable, unique identifier from the data model, like an id property. The Android ListScreen uses key = { it.id }, which suggests a stable id is available.
| ForEach(homeUIState.fruitties, id: \.self) { value in | |
| ForEach(homeUIState.fruitties, id: \.id) { value in |
| // Create an instance of CartViewModel with the CreationExtras. | ||
| val cartViewModel: CartViewModel by lazy { | ||
| ViewModelProvider.create( | ||
| owner = this as ViewModelStoreOwner, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This introduces Jetpack Navigation 3 on Android and NavigationStack on iOS.
Android:
iOS: