-
Notifications
You must be signed in to change notification settings - Fork 37
feat: Add On boarding Empty Screens #99
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
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /build |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /* | ||
| * Copyright 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| * | ||
| * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE | ||
| */ | ||
| plugins { | ||
| alias(libs.plugins.cmp.feature.convention) | ||
| } | ||
|
|
||
| android { | ||
| namespace = "org.mifos.feature.onboarding" | ||
| } | ||
|
|
||
| kotlin { | ||
| sourceSets { | ||
| commonMain.dependencies { | ||
| implementation(projects.core.data) | ||
| implementation(projects.core.datastore) | ||
| implementation(projects.core.model) | ||
| implementation(projects.coreBase.ui) | ||
|
|
||
| implementation(compose.ui) | ||
| implementation(compose.foundation) | ||
| implementation(compose.material3) | ||
| implementation(compose.components.resources) | ||
| implementation(compose.components.uiToolingPreview) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| compose { | ||
| resources { | ||
| packageOfResClass = "org.mifos.feature.onboarding.generated.resources" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /* | ||
| * Copyright 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| * | ||
| * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE | ||
| */ | ||
| package org.mifos.feature.onboarding | ||
|
|
||
| import org.koin.core.module.dsl.viewModelOf | ||
| import org.koin.dsl.module | ||
|
|
||
| val OnboardingModule = module { | ||
| viewModelOf(::OnboardingViewmodel) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| * Copyright 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| * | ||
| * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE | ||
| */ | ||
| package org.mifos.feature.onboarding | ||
|
|
||
| import androidx.navigation.NavController | ||
| import androidx.navigation.NavGraphBuilder | ||
| import androidx.navigation.NavOptions | ||
| import kotlinx.serialization.Serializable | ||
| import template.core.base.ui.composableWithStayTransitions | ||
|
|
||
| @Serializable | ||
| data object OnBoardingRoute | ||
|
|
||
| fun NavController.navigateToOnBoarding(navOptions: NavOptions? = null) = navigate(OnBoardingRoute, navOptions) | ||
|
|
||
| fun NavGraphBuilder.onboardingDestination() { | ||
| composableWithStayTransitions<OnBoardingRoute> { | ||
| OnboardingScreen() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /* | ||
| * Copyright 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| * | ||
| * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE | ||
| */ | ||
| package org.mifos.feature.onboarding | ||
|
|
||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.material3.MaterialTheme | ||
| import androidx.compose.material3.Scaffold | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||
| import org.koin.compose.viewmodel.koinViewModel | ||
| import org.mifos.feature.onboarding.components.OnBoardingScreenPage | ||
|
|
||
| const val TOTAL_PAGES = 2 | ||
|
|
||
| @Composable | ||
| fun OnboardingScreen( | ||
| modifier: Modifier = Modifier, | ||
| viewmodel: OnboardingViewmodel = koinViewModel(), | ||
| ) { | ||
| val currentPage by viewmodel.currentPage.collectAsStateWithLifecycle() | ||
|
|
||
| Scaffold( | ||
| containerColor = MaterialTheme.colorScheme.background, | ||
| ) { | ||
| when (currentPage) { | ||
| 1 -> OnBoardingScreenPage( | ||
| onNext = viewmodel::onNextPage, | ||
| currentPage = currentPage, | ||
| title = "Welcome", | ||
| description = "Thank you for using our template for creating your project", | ||
| modifier = modifier.padding(it), | ||
| ) | ||
| 2 -> OnBoardingScreenPage( | ||
| onNext = viewmodel::onNextPage, | ||
| currentPage = currentPage, | ||
| title = "Get Started", | ||
| description = "You can now start using your project", | ||
| modifier = modifier.padding(it), | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright 2025 Mifos Initiative | ||
| * | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| * | ||
| * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE | ||
| */ | ||
| package org.mifos.feature.onboarding | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import androidx.lifecycle.viewModelScope | ||
| import kotlinx.coroutines.flow.MutableStateFlow | ||
| import kotlinx.coroutines.flow.StateFlow | ||
| import kotlinx.coroutines.flow.asStateFlow | ||
| import kotlinx.coroutines.launch | ||
| import org.mifos.core.datastore.UserPreferencesRepository | ||
|
|
||
| class OnboardingViewmodel( | ||
| private val preferenceRepository: UserPreferencesRepository, | ||
| ) : ViewModel() { | ||
|
|
||
| private val _currentPage = MutableStateFlow(1) | ||
| val currentPage: StateFlow<Int> = _currentPage.asStateFlow() | ||
|
|
||
| private val totalPages = TOTAL_PAGES | ||
|
|
||
| fun onNextPage() { | ||
| if (_currentPage.value < totalPages) { | ||
| _currentPage.value += 1 | ||
| } else { | ||
| updateOnboardingStatus() | ||
| } | ||
| } | ||
|
|
||
| private fun updateOnboardingStatus() { | ||
| viewModelScope.launch { | ||
| preferenceRepository.setFirstTimeState(false) | ||
| } | ||
| } | ||
|
Comment on lines
+29
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling and navigation callback after onboarding completion. The Consider:
🔎 Suggested approach with completion callback+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+
class OnboardingViewmodel(
private val preferenceRepository: UserPreferencesRepository,
) : ViewModel() {
private val _currentPage = MutableStateFlow(1)
val currentPage: StateFlow<Int> = _currentPage.asStateFlow()
+
+ private val _onboardingComplete = MutableSharedFlow<Boolean>()
+ val onboardingComplete: SharedFlow<Boolean> = _onboardingComplete.asSharedFlow()
private val totalPages = TOTAL_PAGES
fun onNextPage() {
if (_currentPage.value < totalPages) {
_currentPage.value += 1
} else {
updateOnboardingStatus()
}
}
private fun updateOnboardingStatus() {
viewModelScope.launch {
- preferenceRepository.setFirstTimeState(false)
+ try {
+ preferenceRepository.setFirstTimeState(false)
+ _onboardingComplete.emit(true)
+ } catch (e: Exception) {
+ // Handle error - maybe emit false or show error state
+ _onboardingComplete.emit(false)
+ }
}
}
}Then in the screen, collect 🤖 Prompt for AI Agents |
||
| } | ||
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.
Add an else branch to handle unexpected page values.
The
whenexpression only handles pages 1 and 2 without an else branch. IfcurrentPagesomehow becomes invalid (e.g., 0 or > TOTAL_PAGES), nothing will render, leaving a blank screen.🔎 Suggested fix with else branch
Scaffold( containerColor = MaterialTheme.colorScheme.background, ) { when (currentPage) { 1 -> OnBoardingScreenPage( onNext = viewmodel::onNextPage, currentPage = currentPage, title = "Welcome", description = "Thank you for using our template for creating your project", modifier = modifier.padding(it), ) 2 -> OnBoardingScreenPage( onNext = viewmodel::onNextPage, currentPage = currentPage, title = "Get Started", description = "You can now start using your project", modifier = modifier.padding(it), ) + else -> { + // Fallback for unexpected page values + OnBoardingScreenPage( + onNext = viewmodel::onNextPage, + currentPage = 1, + title = "Welcome", + description = "Thank you for using our template for creating your project", + modifier = modifier.padding(it), + ) + } } }📝 Committable suggestion
🤖 Prompt for AI Agents