diff --git a/composeApp/src/commonMain/composeResources/drawable/Onboarding.png b/composeApp/src/commonMain/composeResources/drawable/Onboarding.png
new file mode 100644
index 000000000..7d9e2dfa8
Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/Onboarding.png differ
diff --git a/composeApp/src/commonMain/composeResources/drawable/arrow-down.png b/composeApp/src/commonMain/composeResources/drawable/arrow-down.png
new file mode 100644
index 000000000..568bfce77
Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/arrow-down.png differ
diff --git a/composeApp/src/commonMain/composeResources/values-ar/strings.xml b/composeApp/src/commonMain/composeResources/values-ar/strings.xml
index 06dc5f424..6cb57184a 100644
--- a/composeApp/src/commonMain/composeResources/values-ar/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-ar/strings.xml
@@ -37,4 +37,12 @@
كجم
قدم
رطل
+ مستعد للبدء
+ هل لديك حساب؟
+ تسجيل الدخول
+ اختر اللغة
+ سيتم استخدام هذه اللغة في جميع أنحاء التطبيق.
+ العربية
+ الإنجليزية
+ تأكيد
\ No newline at end of file
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index d455de279..a67f48578 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -34,6 +34,15 @@
Meal Image
Calories Icon
Kcal
+ Ready to Start
+ Do you have an account?
+ Login
+ Choose Language
+ This language will be used throughout the app.
+ Arabic
+ English
+ Confirm
+
cm
kg
ft
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/di/viewModelModule.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/di/viewModelModule.kt
index 22d162a76..60a196384 100644
--- a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/di/viewModelModule.kt
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/di/viewModelModule.kt
@@ -1,9 +1,11 @@
package com.cairosquad.evolvefit.di
+import com.cairosquad.evolvefit.ui.screen.onBoarding.OnBoardingViewModel
import com.cairosquad.evolvefit.viewmodel.register.RegisterViewModel
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
val viewModelModule = module {
viewModelOf(::RegisterViewModel)
+ viewModelOf(::OnBoardingViewModel)
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnBoardingViewModel.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnBoardingViewModel.kt
new file mode 100644
index 000000000..68b03aba8
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnBoardingViewModel.kt
@@ -0,0 +1,37 @@
+package com.cairosquad.evolvefit.ui.screen.onBoarding
+
+import com.cairosquad.evolvefit.viewmodel.base.BaseViewModel
+
+class OnBoardingViewModel :
+ BaseViewModel(OnboardingScreenState()),
+ OnboardingScreenListener {
+
+ override fun onChangeLanguage(language: OnboardingScreenState.Language) {
+ updateState {
+ it.copy(bottomSheetSelectedLanguage = language)
+ }
+ }
+
+ override fun onConfirmClicked() {
+ updateState {
+ it.copy(
+ selectedLanguage = it.bottomSheetSelectedLanguage,
+ isBottomSheetOpen = false
+ )
+ }
+ }
+
+ override fun toggleBottomSheet(isOpen: Boolean) {
+ updateState {
+ it.copy(isBottomSheetOpen = isOpen)
+ }
+ }
+
+ override fun onSignUpClicked() {
+ sendEffect(OnboardingScreenEffect.NavigateToRegister)
+ }
+
+ override fun onLoginClicked() {
+ sendEffect(OnboardingScreenEffect.NavigateToLogin)
+ }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreen.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreen.kt
index d7f51de44..d6e91fd04 100644
--- a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreen.kt
@@ -1,39 +1,222 @@
package com.cairosquad.evolvefit.ui.screen.onBoarding
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.Button
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
+import com.cairosquad.evolvefit.design_system.component.BottomSheet
+import com.cairosquad.evolvefit.design_system.component.CheckboxItem
+import com.cairosquad.evolvefit.design_system.component.CheckboxStyle
+import com.cairosquad.evolvefit.design_system.component.PrimaryButton
+import com.cairosquad.evolvefit.design_system.theme.AppTheme
import com.cairosquad.evolvefit.design_system.theme.Theme
+import com.cairosquad.evolvefit.ui.navigation.LoginRoute
+import com.cairosquad.evolvefit.ui.navigation.RegisterRoute
+import com.cairosquad.evolvefit.ui.util.ObserveAsEffect
+import com.cairosquad.evolvefit.ui.util.noRippleClickable
+import org.jetbrains.compose.ui.tooling.preview.Preview
+import evolvefit.composeapp.generated.resources.Onboarding
+import evolvefit.composeapp.generated.resources.Res
+import evolvefit.composeapp.generated.resources.arrow_down
+import evolvefit.composeapp.generated.resources.choose_language
+import evolvefit.composeapp.generated.resources.do_you_have_an_account
+import evolvefit.composeapp.generated.resources.language_selection_description
+import evolvefit.composeapp.generated.resources.login
+import evolvefit.composeapp.generated.resources.ready_to_start
+import org.jetbrains.compose.resources.painterResource
+import org.jetbrains.compose.resources.stringResource
+import org.koin.compose.viewmodel.koinViewModel
@Composable
fun OnboardingScreen(
- navigateToLogin: () -> Unit,
- navigateToRegister: () -> Unit,
+ viewmodel: OnBoardingViewModel = koinViewModel(),
+ navigateToRegister : () -> Unit,
+ navigateToLogin : () -> Unit,
) {
- Column(
- modifier = Modifier.fillMaxSize(),
- verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically),
- horizontalAlignment = Alignment.CenterHorizontally
+ val state by viewmodel.screenState.collectAsStateWithLifecycle()
+ ObserveAsEffect(viewmodel.effect) { effect ->
+ when (effect) {
+ OnboardingScreenEffect.NavigateToLogin -> {
+ navigateToLogin()
+ }
+ OnboardingScreenEffect.NavigateToRegister -> {
+ navigateToRegister()
+ }
+ }
+ }
+ OnboardingScreenContent(
+ state = state,
+ onSignupClick = viewmodel::onSignUpClicked,
+ onLoginClick = viewmodel::onLoginClicked,
+ onToggleBottomSheet = viewmodel::toggleBottomSheet,
+ onLanguageSelected = viewmodel::onChangeLanguage,
+ onConfirmClick = viewmodel::onConfirmClicked
+ )
+}
+
+@Composable
+fun OnboardingScreenContent(
+ state: OnboardingScreenState,
+ modifier: Modifier = Modifier,
+ onSignupClick: () -> Unit,
+ onLoginClick: () -> Unit,
+ onToggleBottomSheet: (isOpen: Boolean) -> Unit,
+ onLanguageSelected: (language: OnboardingScreenState.Language) -> Unit,
+ onConfirmClick: () -> Unit
+) {
+ Box(
+ modifier = modifier.fillMaxSize()
) {
+ Image(
+ painter = painterResource(Res.drawable.Onboarding),
+ contentDescription = "onboarding image",
+ modifier = Modifier.fillMaxSize(),
+ contentScale = ContentScale.FillBounds
+ )
+ Row(
+ modifier = Modifier.align(Alignment.TopEnd).statusBarsPadding().height(48.dp)
+ .wrapContentWidth()
+ .noRippleClickable { onToggleBottomSheet(state.isBottomSheetOpen.not()) },
+ horizontalArrangement = Arrangement.End
+ ) {
+ Text(
+ text = stringResource(state.selectedLanguage.displayNameRes),
+ style = Theme.textStyle.label.mediumMedium16,
+ color = Theme.color.surfaces.textColor,
+ modifier = Modifier.padding(top = 14.5.dp, bottom = 14.5.dp, end = 8.dp)
+ )
+ Image(
+ painter = painterResource(Res.drawable.arrow_down),
+ contentDescription = "arrow down",
+ modifier = Modifier.padding(
+ end = 16.dp, top = 14.5.dp, bottom = 14.5.dp
+ ).size(20.dp)
+ )
+ }
+ //bottom section [ready to start - login]
+ Column(
+ modifier = Modifier.align(Alignment.BottomCenter),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Box(
+ modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 24.dp)
+ .clip(RoundedCornerShape(24.dp))
+ .height(48.dp).background(
+ Theme.color.brand.primary,
+ ).fillMaxWidth().noRippleClickable(onClick = onSignupClick),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = stringResource(Res.string.ready_to_start),
+ style = Theme.textStyle.body.mediumMedium14,
+ color = Theme.color.brand.onPrimary
+ )
+ }
- Text("Welcome to EvolveFit", color = Theme.color.surfaces.onSurface)
- Text("Get Fit Don't Quit", color = Theme.color.surfaces.onSurface)
+ Row(
+ modifier = Modifier.padding(bottom = 24.dp).fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(Res.string.do_you_have_an_account),
+ style = Theme.textStyle.label.mediumMedium14,
+ color = Theme.color.surfaces.textColor,
+ modifier = Modifier.padding(end = 4.dp)
+ )
+ Text(
+ text = stringResource(Res.string.login),
+ style = Theme.textStyle.label.mediumMedium16,
+ color = Theme.color.brand.primary,
+ modifier = Modifier.padding(end = 4.dp).noRippleClickable(onClick = onLoginClick)
+ )
+
+ }
- Button(
- onClick = navigateToLogin
- ){
- Text("go to Login")
- }
- Button(
- onClick = navigateToRegister
- ){
- Text("go to Register")
}
+
+ BottomSheet(
+ isVisible = state.isBottomSheetOpen,
+ onDismiss = {
+ onToggleBottomSheet(false)
+ },
+ content = {
+ Column(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ Text(
+ text = stringResource(Res.string.choose_language),
+ style = Theme.textStyle.label.mediumMedium16,
+ color = Theme.color.surfaces.onSurface,
+ modifier = Modifier.padding(bottom = 4.dp)
+ )
+ Text(
+ text = stringResource(Res.string.language_selection_description),
+ style = Theme.textStyle.label.mediumMedium12,
+ color = Theme.color.surfaces.onSurfaceVariant,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ OnboardingScreenState.Language.entries.forEach { language ->
+ CheckboxItem(
+ text = stringResource(language.displayNameRes),
+ isChecked = language == state.bottomSheetSelectedLanguage,
+ onCheckedChange = { onLanguageSelected(language) },
+ style = CheckboxStyle.Tick,
+ modifier = modifier.padding(
+ bottom = 12.dp
+ )
+ )
+ }
+ PrimaryButton(
+ text = "Confirm",
+ onClick = { onConfirmClick() },
+ modifier = Modifier.padding(
+ start = 16.dp, end = 16.dp, bottom = 16.dp , top = 28.dp
+ )
+ )
+ }
+ },
+ )
+
}
-}
\ No newline at end of file
+}
+
+@Preview
+@Composable
+fun OnboardingScreenContentPreview() {
+ var state = OnboardingScreenState().copy(isBottomSheetOpen = true,)
+ AppTheme(
+ isDarkTheme = true
+ ) {
+ OnboardingScreenContent(
+ state,
+ onSignupClick = { },
+ onLoginClick = { },
+ onToggleBottomSheet = {state.isBottomSheetOpen.not()},
+ onLanguageSelected = { },
+ onConfirmClick = {}
+ )
+ }
+
+}
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenEffect.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenEffect.kt
new file mode 100644
index 000000000..9f2d608ab
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenEffect.kt
@@ -0,0 +1,6 @@
+package com.cairosquad.evolvefit.ui.screen.onBoarding
+
+sealed class OnboardingScreenEffect {
+ object NavigateToLogin : OnboardingScreenEffect()
+ object NavigateToRegister : OnboardingScreenEffect()
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenListener.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenListener.kt
new file mode 100644
index 000000000..32457af83
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenListener.kt
@@ -0,0 +1,9 @@
+package com.cairosquad.evolvefit.ui.screen.onBoarding
+
+interface OnboardingScreenListener {
+ fun onChangeLanguage(language: OnboardingScreenState.Language)
+ fun toggleBottomSheet(isOpen: Boolean)
+ fun onSignUpClicked()
+ fun onLoginClicked()
+ fun onConfirmClicked()
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenState.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenState.kt
new file mode 100644
index 000000000..166bc8dfc
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/screen/onBoarding/OnboardingScreenState.kt
@@ -0,0 +1,17 @@
+package com.cairosquad.evolvefit.ui.screen.onBoarding
+
+import evolvefit.composeapp.generated.resources.Res
+import evolvefit.composeapp.generated.resources.arabic
+import evolvefit.composeapp.generated.resources.english
+import org.jetbrains.compose.resources.StringResource
+
+data class OnboardingScreenState(
+ val selectedLanguage: Language = Language.ENGLISH,
+ val isBottomSheetOpen: Boolean = false,
+ val bottomSheetSelectedLanguage : Language = Language.ENGLISH
+) {
+ enum class Language( val displayNameRes: StringResource) {
+ ARABIC(Res.string.arabic),
+ ENGLISH(Res.string.english)
+ }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/util/NoRippleClick.kt b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/util/NoRippleClick.kt
new file mode 100644
index 000000000..4bb3c888a
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/cairosquad/evolvefit/ui/util/NoRippleClick.kt
@@ -0,0 +1,15 @@
+package com.cairosquad.evolvefit.ui.util
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+
+fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = composed {
+ this.clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }) {
+ onClick()
+ }
+}