Skip to content

Conversation

@cartland
Copy link
Contributor

This introduces Jetpack Navigation 3 on Android and NavigationStack on iOS.

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.

cartland added 2 commits June 23, 2025 13:19
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.
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 CartViewModel to manage cart-specific UI state and logic, separating it from the MainViewModel for better modularity and adherence to KMP ViewModel patterns.
  • KMP ViewModel Demonstration: Showcased the effective use of Kotlin Multiplatform (KMP) ViewModels (MainViewModel and CartViewModel) across different screens on both Android and iOS, demonstrating shared logic with platform-specific UI.
  • Android SDK & Dependency Updates: Upgraded Android compileSdk and targetSdk to 36 and integrated new Navigation3 and kotlinx-serialization dependencies 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 NavigationStack for 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

  1. 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.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The material3AdaptiveNav3 dependency is using a SNAPSHOT version. Snapshot dependencies are unstable, can change without notice, and may break your build at any time. They should be avoided, especially in production or release builds.

Comment on lines +28 to +30
maven {
url = uri("https://androidx.dev/snapshots/builds/13617490/artifacts/repository")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The snapshot repository URL is pinned to a specific build ID (13617490). This is very brittle. If that build artifact is removed from the server, your build will fail. It's generally better to use a generic snapshot URL (e.g., one that always points to the latest snapshot) if available.

)

companion object {
val APP_CONTAINER_KEY = CreationExtras.Key<AppContainer>()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The APP_CONTAINER_KEY is also defined in MainViewModel. This creates two separate instances of CreationExtras.Key for the same purpose, which could lead to bugs. It's best to define this key once in a shared location and have both ViewModels reference that single instance.

Comment on lines +85 to +88
onNavBack = {
backStack.clear()
backStack.add(ListScreenKey)
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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

Comment on lines +90 to +96
colors = TopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
scrolledContainerColor = MaterialTheme.colorScheme.primary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text is hardcoded. For better maintainability and to support internationalization (i18n), it's best practice to use string resources.

                text = stringResource(R.string.cart_summary, cartItemCount),

LazyColumn {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(64.dp),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
verticalArrangement = Arrangement.spacedBy(64.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),

Observing(mainViewModel.homeUiState) { homeUIState in
ScrollView {
LazyVStack {
ForEach(homeUIState.fruitties, id: \.self) { value in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The cast this as ViewModelStoreOwner is redundant because the class CartViewModelStoreOwner already implements the ViewModelStoreOwner interface. You can safely remove it for cleaner code.

Suggested change
owner = this as ViewModelStoreOwner,
owner = this,

@cartland cartland closed this Jun 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant