- Step1 :- First of all add dependency
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
datastorePreferences = "1.1.1" // For hilt dependency
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" } // In pluginshiltPlugin={id="com.google.dagger.hilt.android",version.ref="hiltAndroid"} //also add aboove line in project level
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.IOException
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
val Context.datastore: DataStore<Preferences> by preferencesDataStore(name = "on_boarding_pref")
class DataStoreRepository(context: Context) {
private object PreferencesKey {
val onBoardingKey = booleanPreferencesKey(name = "on_boarding_completed")
}
private val dataStore = context.datastore
suspend fun saveOnBoardingState(completed: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKey.onBoardingKey] = completed
}
}
fun readOnBoardingState(): Flow<Boolean> {
return dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
val onBoardingState = preferences[PreferencesKey.onBoardingKey] ?: false
onBoardingState
}
}
} - Step2 :- If you're using DI use this
@Module
@InstallIn(SingletonComponent::class)
object MainModule {
@Provides
@Singleton
fun provideDataStoreRepository(
@ApplicationContext context: Context
) = DataStoreRepository(context = context)
} - Step3 :- I have created two viewmodel 1 is SplashViewmodel and 2 is WelcomeViewModel
@HiltViewModel
class SplashViewModel @Inject constructor(private val respository: DataStoreRepository) :
ViewModel() {
private val _isLoading: MutableState<Boolean> = mutableStateOf(true)
val isLoading: State<Boolean> = _isLoading
private val _startDestinaton: MutableState<String> = mutableStateOf(Screen.Welcome.route)
val startDestination: State<String> = _startDestinaton
init {
viewModelScope.launch {
delay(2000) // Introduce a 2-second delay for splash screen
respository.readOnBoardingState().collect { completed ->
_startDestinaton.value = if (completed) Screen.Home.route else Screen.Welcome.route
}
_isLoading.value = false
}
}
}Also create for hiltimport androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.jetpacktutorials.ui.theme.data.DataStoreRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class WelcomeViewModel @Inject constructor(private val repository: DataStoreRepository) :
ViewModel() {
fun saveOnBoardingState(completed: Boolean) {
viewModelScope.launch(Dispatchers.IO) {
repository.saveOnBoardingState(completed = completed)
}
}
}@HiltAndroidApp
class MyApplication:Application() {
} - Step4 :- I have 3 screens
@Composable
fun HomeScreen() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
modifier = Modifier.padding(20.dp),
text = "Welcome to Home Screen",
fontSize = MaterialTheme.typography.titleLarge.fontSize
)
}
}
@Composable
@Preview(showBackground = true)
fun PreviewHomeScreen() {
HomeScreen()
}@Composable
fun WelcomeScreen(
navController: NavController,
welcomeViewModel: WelcomeViewModel = hiltViewModel()
) {
val pages = listOf(OnBoardingPage.First, OnBoardingPage.Second, OnBoardingPage.Third)
val pagerState = rememberPagerState(
initialPage = 0,
initialPageOffsetFraction = 0f,
pageCount = { pages.size }
)
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier.weight(10f), state = pagerState, beyondViewportPageCount = 3
) { position ->
PagerScreen(onBoardingPage = pages[position])
}
HorizontalPagerIndicator(
pagerState = pagerState,
pageCount = pages.size,
indicatorShape = CircleShape,
activeColor = Color.Black,
inactiveColor = Color.Gray, // Use Gray for better contrast
indicatorWidth = 10.dp, // Adjust the size of the indicators
indicatorHeight = 10.dp,
spacing = 8.dp, // Add spacing between indicators
modifier = Modifier
.padding(top = 16.dp)
.align(Alignment.CenterHorizontally)
.weight(1f)
// Fill width for centering
)
FinishButton(modifier = Modifier.weight(1f), pagerState = pagerState) {
welcomeViewModel.saveOnBoardingState(completed = true)
navController.popBackStack()
navController.navigate(Screen.Home.route)
}
}
}
@Composable
fun PagerScreen(onBoardingPage: OnBoardingPage) {
Column(
modifier = Modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
Image(
modifier = Modifier
.fillMaxWidth(0.5f)
.fillMaxHeight(0.6f),
painter = painterResource(id = onBoardingPage.image),
contentDescription = "Pager Page"
)
Text(
modifier = Modifier.fillMaxWidth(),
text = onBoardingPage.title,
fontSize = MaterialTheme.typography.headlineMedium.fontSize,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 40.dp)
.padding(top = 20.dp),
text = onBoardingPage.description,
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
fontWeight = FontWeight.Medium,
textAlign = TextAlign.Center
)
}
}
// Finish button for when slider ends..
@Composable
fun FinishButton(
modifier: Modifier,
pagerState: PagerState,
onclick: () -> Unit
) {
Row(
modifier = Modifier
.padding(horizontal = 40.dp)
.padding(top = 14.dp),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.Center
) {
AnimatedVisibility(
modifier = Modifier.fillMaxWidth(),
visible = pagerState.currentPage == 2
) {
Button(
onClick = onclick,
colors = ButtonDefaults.buttonColors(
contentColor = Color.White
)
) {
Text("Finish")
}
}
}
}
@Composable
@Preview(showBackground = true)
fun FirstScreenPreview() {
Column(modifier = Modifier.fillMaxSize()) {
PagerScreen(onBoardingPage = OnBoardingPage.First)
}
}
@Composable
@Preview(showBackground = true)
fun SecondScreenPreview() {
Column(modifier = Modifier.fillMaxSize()) {
PagerScreen(onBoardingPage = OnBoardingPage.Second)
}
}
@Composable
@Preview(showBackground = true)
fun ThirdScreenPreview() {
Column(modifier = Modifier.fillMaxSize()) {
PagerScreen(onBoardingPage = OnBoardingPage.Third)
}
}@Composable
fun AnimatedSplashScreen(navController: NavHostController, splashViewModel: SplashViewModel = hiltViewModel()) {
val isLoading by splashViewModel.isLoading
val startDestination by splashViewModel.startDestination
var startAnimation by remember { mutableStateOf(false) }
val alphaAnim = animateFloatAsState(
targetValue = if (startAnimation) 1f else 0f,
animationSpec = tween(
durationMillis = 3000
), label = "alphaAnimation"
)
// Display the splash screen with the animation
Splash(alpha = alphaAnim.value)
// Start the animation and navigation effect
LaunchedEffect(key1 = true) {
if (isLoading) {
// Start animation once loading is finished
startAnimation = true
// After the animation duration, navigate to the next screen
delay(3000) // wait for animation to finish
navController.popBackStack()
navController.navigate(startDestination)
}
}
}
@Composable
fun Splash(alpha: Float) {
Box(
modifier = Modifier
.background(if (isSystemInDarkTheme()) Color.Black else Color.DarkGray)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Icon(
modifier = Modifier
.size(120.dp)
.alpha(alpha = alpha),
imageVector = Icons.Default.Email,
contentDescription = "Logo Icon",
tint = Color.White
)
//For drawable image
/* Image(
painter = painterResource(id = R.drawable.ic_launcher_background), // Replace with your drawable
contentDescription = "Email Icon",
modifier = Modifier
.size(120.dp)
.alpha(alpha = alpha),
contentScale = ContentScale.Fit, // Optional: adjust the scaling of the image
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.White) // Apply tint if needed
)*/
}
} - Step5 :- For onboarding i have created fix data with using sealed class
import androidx.annotation.DrawableRes
import com.example.jetpacktutorials.R
sealed class OnBoardingPage(
@DrawableRes
val image: Int,
val title: String,
val description: String
) {
data object First :
OnBoardingPage(image = R.drawable.img1, "First Slider", "Hii, This is first Slider , Welcome In this App")
data object Second :
OnBoardingPage(image = R.drawable.img2, "Second Slider", "Hii, This is Second Slider, Welcome In this App")
data object Third :
OnBoardingPage(image = R.drawable.img3, "Third Slider", "Hii, This is Third Slider,Welcome In this App")
} - Step6 :- For Screen Routing
sealed class Screen(val route: String) {
data object Welcome:Screen("welcome")
data object Splash : Screen("splash_screen")
data object Home : Screen("home_screen")
} - Step7 :- For this i have this navgraph
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.jetpacktutorials.ui.theme.ui.home.HomeScreen
import com.example.jetpacktutorials.ui.theme.ui.welcome.WelcomeScreen
import com.example.jetpacktutorials.ui.theme.utils.AnimatedSplashScreen
@Composable
fun SetupNavGraph(navController: NavHostController,startDestination:String) {
NavHost(
navController = navController,
startDestination =startDestination
) {
composable(route = Screen.Welcome.route) {
WelcomeScreen(navController = navController)
}
composable(route = Screen.Home.route) {
HomeScreen()
}
composable(route = Screen.Splash.route) {
AnimatedSplashScreen(navController = navController)
}
}
}
0 Comments