├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── tigp.png │ │ │ │ ├── airtel.png │ │ │ │ ├── tigopesa.png │ │ │ │ ├── vodacom.png │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── values │ │ │ │ ├── themes.xml │ │ │ │ ├── colors.xml │ │ │ │ └── strings.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-anydpi-v33 │ │ │ │ └── ic_launcher.xml │ │ │ ├── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── mtali │ │ │ │ └── onboarding │ │ │ │ ├── app │ │ │ │ ├── App.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ └── MainActivity.kt │ │ │ │ └── core │ │ │ │ ├── ui │ │ │ │ ├── theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Shape.kt │ │ │ │ │ ├── Type.kt │ │ │ │ │ └── Theme.kt │ │ │ │ └── components │ │ │ │ │ └── Indicator.kt │ │ │ │ └── models │ │ │ │ └── Onboarding.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── mtali │ │ │ └── onboarding │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── mtali │ │ └── onboarding │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── docs └── home.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── README.md ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /docs/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/docs/home.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/tigp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/drawable/tigp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/airtel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/drawable/airtel.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/tigopesa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/drawable/tigopesa.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/vodacom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/drawable/vodacom.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtali/Onboarding/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/app/App.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.app 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class App : Application() -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Apr 16 22:12:52 EAT 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "Onboarding" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/core/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.core.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) 9 | 10 | val Blue = Color(0xFF0D47A1) 11 | val Yellow = Color(0xFFFAD206) 12 | val Green = Color(0xFF80DD65) -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/core/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.core.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/mtali/onboarding/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/core/models/Onboarding.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.core.models 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.annotation.StringRes 5 | import androidx.compose.ui.graphics.Color 6 | import com.mtali.onboarding.core.ui.theme.Purple700 7 | 8 | data class Onboarding( 9 | @DrawableRes val image: Int, 10 | @StringRes val title: Int, 11 | @StringRes val desc: Int, 12 | val backgroundColor: Color, 13 | val mainColor: Color = Purple700 14 | ) 15 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mtali/onboarding/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.* 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.mtali.onboarding", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Onboarding 3 | Tigo Pesa 4 | As a Tigo Pesa merchant you can always do more with the service. Now you can move money, pay bills, suppliers and draw cash from Tigo Pesa agents. 5 | Airtel Money 6 | Airtel Money continues to improve the lives of our customers.Now Airtel Money customers cand send and withdraw Money without incurring the government Levy amount up to Tsh 29,999 /= 7 | M-Pesa 8 | M-Pesa is a mobile phone-based money transfer service, payments and micro-financing service, launched in 2007 by Vodafone and Safaricom, the largest mobile network operator 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/core/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.core.ui.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Onboarding 2 | 3 | This app showcases how to implement a simple onboarding experience using Jetpack Compose. 4 | 5 | ## Features 6 | 7 | - [x] Onboarding screens with custom illustrations 8 | - [x] Navigation between screens with Pager 9 | - [x] Button visibility depends on the current page 10 | - [x] Animated pager indicator 11 | 12 | ## Screenshots 13 | 14 |

15 | 16 |

17 | 18 | ## Technologies Used 19 | 20 | - Kotlin 21 | - Jetpack Compose 22 | - Hilt Dependency Injection 23 | - Material Design 24 | - Pager 25 | 26 | ## Installation 27 | 28 | - Clone the repository 29 | - Open the project in Android Studio 4.2 or newer 30 | - Build and run the app on a connected device or emulator 31 | 32 | ## Usage 33 | 34 | - When the app is launched, the onboarding screens will be displayed 35 | - Swipe left or right to navigate between the screens 36 | - On the last screen, tap the "Get Started" button to navigate to the main app screen 37 | 38 | 39 | ## Contributions 40 | 41 | Contributions are welcome! Feel free to fork this repository and submit a pull request. 42 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/core/ui/components/Indicator.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.core.ui.components 2 | 3 | import androidx.compose.animation.core.animateDpAsState 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.height 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.foundation.layout.width 9 | import androidx.compose.foundation.shape.CircleShape 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.unit.dp 15 | 16 | @Composable 17 | fun Indicator(isSelected: Boolean, color: Color) { 18 | val width = animateDpAsState( 19 | targetValue = if (isSelected) 20.dp else 10.dp, 20 | label = "indicator-width" 21 | ) 22 | 23 | Box( 24 | modifier = Modifier 25 | .padding(4.dp) 26 | .height(10.dp) 27 | .width(width.value) 28 | .clip(CircleShape) 29 | .background(if (isSelected) color else Color.Gray.copy(alpha = 0.5f)) 30 | ) 31 | 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/core/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.core.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun OnboardingTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { 32 | val colors = if (darkTheme) { 33 | DarkColorPalette 34 | } else { 35 | LightColorPalette 36 | } 37 | 38 | MaterialTheme( 39 | colors = colors, 40 | typography = Typography, 41 | shapes = Shapes, 42 | content = content 43 | ) 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/app/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.app 2 | 3 | import androidx.compose.ui.graphics.Color 4 | import androidx.lifecycle.ViewModel 5 | import com.mtali.onboarding.R 6 | import com.mtali.onboarding.core.models.Onboarding 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class MainViewModel @Inject constructor() : ViewModel() { 12 | 13 | 14 | companion object { 15 | val onboardingData = listOf( 16 | Onboarding( 17 | image = R.drawable.tigp, 18 | title = R.string.tigo_title, 19 | desc = R.string.tigo_desc, 20 | backgroundColor = Color(0xFF183E66), 21 | mainColor = Color(0xFF183E66), 22 | ), 23 | Onboarding( 24 | image = R.drawable.airtel, 25 | title = R.string.airtel_title, 26 | desc = R.string.airtel_desc, 27 | backgroundColor = Color(0xFFECF5FC), 28 | mainColor = Color(0xFFED0006) 29 | ), 30 | Onboarding( 31 | image = R.drawable.vodacom, 32 | title = R.string.mpesa_title, 33 | desc = R.string.mpesa_desc, 34 | backgroundColor = Color(0xFFFA0000), 35 | mainColor = Color(0xFFED0006) 36 | ), 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'dagger.hilt.android.plugin' 5 | id "com.google.devtools.ksp" version "$ksp_version" 6 | id 'kotlin-kapt' 7 | } 8 | 9 | android { 10 | namespace 'com.mtali.onboarding' 11 | compileSdk 33 12 | 13 | defaultConfig { 14 | applicationId "com.mtali.tigopesa" 15 | minSdk 24 16 | targetSdk 33 17 | versionCode 1 18 | versionName "1.0" 19 | 20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 21 | vectorDrawables { 22 | useSupportLibrary true 23 | } 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | buildFeatures { 40 | compose true 41 | } 42 | composeOptions { 43 | kotlinCompilerExtensionVersion "$compose_compiler_version" 44 | } 45 | packagingOptions { 46 | resources { 47 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 48 | } 49 | } 50 | } 51 | 52 | dependencies { 53 | 54 | // Core 55 | implementation "androidx.core:core-ktx:$core_version" 56 | 57 | // Lifecycle 58 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 59 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" 60 | implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version" 61 | 62 | // Activity 63 | //noinspection GradleDependency 64 | implementation "androidx.activity:activity-compose:$activity_version" 65 | 66 | // Compose BOOM 67 | def composeBom = platform("androidx.compose:compose-bom:$compose_boom_version") 68 | implementation composeBom 69 | implementation 'androidx.compose.ui:ui' 70 | implementation 'androidx.compose.ui:ui-tooling-preview' 71 | implementation 'androidx.compose.material:material' 72 | implementation 'androidx.compose.material:material-icons-extended' 73 | 74 | // Compose Navigation 75 | implementation "androidx.navigation:navigation-compose:$compose_navigation_version" 76 | 77 | // Hilt 78 | implementation "com.google.dagger:hilt-android:$hilt_verson" 79 | kapt "com.google.dagger:hilt-compiler:$hilt_verson" 80 | 81 | // Hilt Compose 82 | implementation "androidx.hilt:hilt-navigation-compose:$hilt_navigation_compose_version" 83 | 84 | // DataStore 85 | implementation "androidx.datastore:datastore-preferences:$datastore_version" 86 | 87 | // Timber 88 | implementation "com.jakewharton.timber:timber:$timber_version" 89 | 90 | // KSP 91 | implementation "com.google.devtools.ksp:symbol-processing-api:$ksp_version" 92 | 93 | // Code 94 | implementation "androidx.tracing:tracing:1.2.0-beta03" 95 | 96 | 97 | testImplementation 'junit:junit:4.13.2' 98 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 99 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 100 | // Compose Testing 101 | androidTestImplementation composeBom 102 | androidTestImplementation 'androidx.compose.ui:ui-test-junit4' 103 | debugImplementation 'androidx.compose.ui:ui-tooling' 104 | debugImplementation 'androidx.compose.ui:ui-test-manifest' 105 | } 106 | 107 | kapt { 108 | correctErrorTypes true 109 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ 2 | env sh 3 | 4 | # 5 | 6 | # Copyright 2015 the original author or authors. 7 | # 8 | 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | 14 | # https://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | ############################################################################## 25 | ## 26 | ## 27 | Gradle start 28 | up script 29 | for 30 | UN *X 31 | ## 32 | ############################################################################## 33 | 34 | # Attempt to set APP_HOME 35 | # Resolve links: $0 may be a link 36 | PRG = "$0" 37 | # Need this for relative symlinks. 38 | while [ -h "$PRG" ]; do 39 | ls = 40 | `ls -ld "$PRG"` 41 | link = 42 | `expr "$ls" : '.*-> \(.*\)$'` 43 | if expr "$link" : '/.*' > /dev/ 44 | null; 45 | then 46 | PRG = "$link" 47 | else 48 | PRG = 49 | `dirname "$PRG"`"/$link" 50 | fi 51 | done 52 | SAVED = "`pwd`" 53 | cd "`dirname \"$PRG\"`/" >/dev/ 54 | null 55 | APP_HOME = "`pwd -P`" 56 | cd "$SAVED" >/dev/ 57 | null 58 | 59 | APP_NAME = "Gradle" 60 | APP_BASE_NAME = 61 | `basename "$0"` 62 | 63 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 64 | DEFAULT_JVM_OPTS = '"-Xmx64m" "-Xms64m"' 65 | 66 | # Use the maximum available, or set MAX_FD != -1 to use that value. 67 | MAX_FD = "maximum" 68 | 69 | warn() { 70 | echo 71 | "$*" 72 | } 73 | 74 | die() { 75 | echo 76 | echo 77 | "$*" 78 | echo 79 | exit 80 | 1 81 | } 82 | 83 | # OS specific support (must be 'true' or 'false'). 84 | cygwin = false 85 | msys = false 86 | darwin = false 87 | nonstop = false 88 | case "`uname`" 89 | in 90 | CYGWIN 91 | * ) 92 | cygwin = true;; 93 | Darwin* ) 94 | darwin = true;; 95 | MINGW* ) 96 | msys = true;; 97 | NONSTOP* ) 98 | nonstop = true;; 99 | esac 100 | 101 | CLASSPATH = $APP_HOME / gradle / wrapper / gradle - wrapper.jar 102 | 103 | 104 | # Determine the Java command to use to start the JVM. 105 | if [ -n "$JAVA_HOME" ]; then 106 | if [ -x "$JAVA_HOME/jre/sh/java" ]; 107 | then 108 | # IBM's JDK on AIX uses strange locations for the executables 109 | JAVACMD = "$JAVA_HOME/jre/sh/java" 110 | else 111 | JAVACMD = "$JAVA_HOME/bin/java" 112 | fi 113 | if [ ! -x "$JAVACMD" ]; 114 | then 115 | die 116 | "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 117 | 118 | Please set 119 | the JAVA_HOME 120 | variable in 121 | your environment 122 | to match 123 | the 124 | location 125 | of your 126 | Java installation 127 | ." 128 | fi 129 | else 130 | JAVACMD = "java" 131 | which java 132 | >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 133 | 134 | Please set 135 | the JAVA_HOME 136 | variable in 137 | your environment 138 | to match 139 | the 140 | location 141 | of your 142 | Java installation 143 | ." 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; 148 | then 149 | MAX_FD_LIMIT = 150 | `ulimit -H -n` 151 | if [ $? -eq 0 ]; then 152 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; 153 | then 154 | MAX_FD = "$MAX_FD_LIMIT" 155 | fi 156 | ulimit 157 | - 158 | n $MAX_FD 159 | if [ $? -ne 0 ]; 160 | then 161 | warn 162 | "Could not set maximum file descriptor limit: $MAX_FD" 163 | fi 164 | else 165 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 166 | fi 167 | fi 168 | 169 | # For Darwin, add options to specify how the application appears in the dock 170 | if 171 | $darwin; 172 | then 173 | GRADLE_OPTS = "$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 174 | fi 175 | 176 | # For Cygwin or MSYS, switch paths to Windows format before running java 177 | if [ "$cygwin" = "true" -o "$msys" = "true" ]; 178 | then 179 | APP_HOME = 180 | `cygpath --path --mixed "$APP_HOME"` 181 | CLASSPATH = 182 | `cygpath --path --mixed "$CLASSPATH"` 183 | 184 | JAVACMD = 185 | `cygpath --unix "$JAVACMD"` 186 | 187 | # We build the pattern for arguments to be converted via cygpath 188 | ROOTDIRSRAW = 189 | `find -L / -maxdepth 1 -mindepth 1 - 190 | type d 191 | 2>/dev/null` 192 | SEP = "" 193 | for 194 | dir in 195 | $ROOTDIRSRAW; 196 | do 197 | ROOTDIRS = "$ROOTDIRS$SEP$dir" 198 | SEP = "|" 199 | done 200 | OURCYGPATTERN = "(^($ROOTDIRS))" 201 | # Add a user-defined pattern to the cygpath arguments 202 | if [ "$GRADLE_CYGPATTERN" != "" ]; 203 | then 204 | OURCYGPATTERN = "$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 205 | fi 206 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 207 | i = 0 208 | for 209 | arg in 210 | "$@"; do 211 | CHECK = 212 | `echo "$arg"|egrep -c "$OURCYGPATTERN" -` 213 | CHECK2 = 214 | `echo "$arg"|egrep -c "^-"` ### Determine if 215 | an option 216 | 217 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### 218 | Added a 219 | condition 220 | eval 221 | ` 222 | echo args$i 223 | `=`cygpath --path --ignore --mixed "$arg"` 224 | else 225 | eval ` 226 | echo args$i 227 | `="\"$arg\"" 228 | fi 229 | i = 230 | ` 231 | expr $i 232 | + 1` 233 | done 234 | case 235 | $i in 236 | 0) set --;; 237 | 1) set -- "$args0";; 238 | 2) set -- "$args0" "$args1";; 239 | 3) set -- "$args0" "$args1" "$args2";; 240 | 4) set -- "$args0" "$args1" "$args2" "$args3";; 241 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4";; 242 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5";; 243 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6";; 244 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7";; 245 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8";; 246 | esac 247 | fi 248 | 249 | # Escape application args 250 | 251 | save() { 252 | for 253 | i 254 | do printf % s\\n 255 | "$i" | sed 256 | "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; 257 | done 258 | echo 259 | " " 260 | } 261 | 262 | APP_ARGS = 263 | `save "$@"` 264 | 265 | # Collect all arguments for the java command, following the shell quoting and substitution rules 266 | eval set 267 | -- 268 | $DEFAULT_JVM_OPTS $JAVA_OPTS 269 | $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 270 | 271 | exec "$JAVACMD" "$@" 272 | -------------------------------------------------------------------------------- /app/src/main/java/com/mtali/onboarding/app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mtali.onboarding.app 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.BorderStroke 7 | import androidx.compose.foundation.ExperimentalFoundationApi 8 | import androidx.compose.foundation.Image 9 | import androidx.compose.foundation.background 10 | import androidx.compose.foundation.layout.Arrangement 11 | import androidx.compose.foundation.layout.Box 12 | import androidx.compose.foundation.layout.Column 13 | import androidx.compose.foundation.layout.PaddingValues 14 | import androidx.compose.foundation.layout.Row 15 | import androidx.compose.foundation.layout.fillMaxSize 16 | import androidx.compose.foundation.layout.fillMaxWidth 17 | import androidx.compose.foundation.layout.height 18 | import androidx.compose.foundation.layout.padding 19 | import androidx.compose.foundation.layout.size 20 | import androidx.compose.foundation.pager.HorizontalPager 21 | import androidx.compose.foundation.pager.rememberPagerState 22 | import androidx.compose.foundation.shape.RoundedCornerShape 23 | import androidx.compose.material.Button 24 | import androidx.compose.material.ButtonDefaults 25 | import androidx.compose.material.Card 26 | import androidx.compose.material.Icon 27 | import androidx.compose.material.MaterialTheme 28 | import androidx.compose.material.OutlinedButton 29 | import androidx.compose.material.Surface 30 | import androidx.compose.material.Text 31 | import androidx.compose.material.TextButton 32 | import androidx.compose.material.icons.Icons 33 | import androidx.compose.material.icons.outlined.KeyboardArrowRight 34 | import androidx.compose.runtime.Composable 35 | import androidx.compose.runtime.rememberCoroutineScope 36 | import androidx.compose.ui.Alignment 37 | import androidx.compose.ui.Modifier 38 | import androidx.compose.ui.graphics.Color 39 | import androidx.compose.ui.res.painterResource 40 | import androidx.compose.ui.res.stringResource 41 | import androidx.compose.ui.text.font.FontWeight 42 | import androidx.compose.ui.text.style.TextAlign 43 | import androidx.compose.ui.tooling.preview.Preview 44 | import androidx.compose.ui.unit.dp 45 | import androidx.compose.ui.unit.sp 46 | import com.mtali.onboarding.app.MainViewModel.Companion.onboardingData 47 | import com.mtali.onboarding.core.models.Onboarding 48 | import com.mtali.onboarding.core.ui.components.Indicator 49 | import com.mtali.onboarding.core.ui.theme.Blue 50 | import com.mtali.onboarding.core.ui.theme.OnboardingTheme 51 | import dagger.hilt.android.AndroidEntryPoint 52 | import kotlinx.coroutines.Dispatchers 53 | import kotlinx.coroutines.launch 54 | import kotlinx.coroutines.withContext 55 | 56 | @AndroidEntryPoint 57 | class MainActivity : ComponentActivity() { 58 | 59 | override fun onCreate(savedInstanceState: Bundle?) { 60 | super.onCreate(savedInstanceState) 61 | setContent { 62 | OnboardingTheme { 63 | // A surface container using the 'background' color from the theme 64 | Surface( 65 | modifier = Modifier.fillMaxSize(), 66 | color = MaterialTheme.colors.background 67 | ) { 68 | OnboardingApp() 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | @Composable 76 | private fun OnboardingApp() { 77 | OnboardingScreen( 78 | items = onboardingData, 79 | modifier = Modifier 80 | .fillMaxWidth() 81 | .background(Blue) 82 | ) 83 | } 84 | 85 | @OptIn(ExperimentalFoundationApi::class) 86 | @Composable 87 | private fun OnboardingScreen( 88 | modifier: Modifier = Modifier, 89 | items: List, 90 | ) { 91 | val pagerState = rememberPagerState() 92 | val coroutineScope = rememberCoroutineScope() 93 | 94 | Box(modifier = modifier) { 95 | Column(horizontalAlignment = Alignment.CenterHorizontally) { 96 | HorizontalPager(pageCount = items.size, state = pagerState) { page -> 97 | Column( 98 | modifier = Modifier 99 | .fillMaxSize() 100 | .background(items[page].backgroundColor), 101 | horizontalAlignment = Alignment.CenterHorizontally, 102 | verticalArrangement = Arrangement.Top 103 | ) { 104 | Image( 105 | painter = painterResource(id = items[page].image), 106 | contentDescription = stringResource(id = items[page].title), 107 | modifier = Modifier 108 | .fillMaxWidth() 109 | .height(400.dp) 110 | ) 111 | } 112 | } 113 | } 114 | 115 | Box(modifier = Modifier.align(Alignment.BottomCenter)) { 116 | Card( 117 | modifier = Modifier 118 | .fillMaxWidth() 119 | .height(340.dp), 120 | backgroundColor = Color.White, 121 | elevation = 0.dp, 122 | shape = RoundedCornerShape(topStart = 80.dp) 123 | ) { 124 | Box { 125 | Column( 126 | modifier = Modifier, 127 | horizontalAlignment = Alignment.CenterHorizontally 128 | ) { 129 | PagerIndicator( 130 | currentPage = pagerState.currentPage, 131 | items = onboardingData 132 | ) 133 | 134 | Text( 135 | text = stringResource(id = items[pagerState.currentPage].title), 136 | modifier = Modifier 137 | .fillMaxWidth() 138 | .padding(top = 20.dp, end = 30.dp), 139 | color = items[pagerState.currentPage].mainColor, 140 | textAlign = TextAlign.Right, 141 | fontSize = 20.sp, 142 | fontWeight = FontWeight.ExtraBold 143 | ) 144 | 145 | Text( 146 | text = stringResource(id = items[pagerState.currentPage].desc), 147 | modifier = Modifier.padding(top = 20.dp, start = 40.dp, end = 20.dp), 148 | color = Color.Black, 149 | fontSize = 19.sp, 150 | textAlign = TextAlign.Center, 151 | fontWeight = FontWeight.ExtraLight 152 | ) 153 | } 154 | 155 | Box( 156 | modifier = Modifier 157 | .align(Alignment.BottomCenter) 158 | .padding(30.dp) 159 | ) { 160 | Row( 161 | modifier = Modifier.fillMaxWidth(), 162 | verticalAlignment = Alignment.CenterVertically, 163 | horizontalArrangement = Arrangement.SpaceBetween 164 | ) { 165 | 166 | 167 | if (pagerState.currentPage != 2) { 168 | TextButton(onClick = { 169 | //skip 170 | }) { 171 | Text( 172 | text = "Skip Now", 173 | color = Color(0xFF292D32), 174 | textAlign = TextAlign.Right, 175 | fontSize = 14.sp, 176 | fontWeight = FontWeight.SemiBold 177 | ) 178 | } 179 | 180 | OutlinedButton( 181 | onClick = { 182 | coroutineScope.launch { 183 | withContext(Dispatchers.Main) { 184 | pagerState.scrollToPage(pagerState.currentPage + 1) 185 | } 186 | 187 | } 188 | }, 189 | border = BorderStroke( 190 | 3.dp, 191 | items[pagerState.currentPage].mainColor 192 | ), 193 | shape = RoundedCornerShape(50), 194 | colors = ButtonDefaults.outlinedButtonColors(contentColor = items[pagerState.currentPage].mainColor), 195 | modifier = Modifier.size(65.dp) 196 | ) { 197 | Icon( 198 | imageVector = Icons.Outlined.KeyboardArrowRight, 199 | contentDescription = null, 200 | tint = items[pagerState.currentPage].mainColor, 201 | modifier = Modifier.size(30.dp) 202 | ) 203 | } 204 | } else { 205 | Button( 206 | onClick = { 207 | //show home screen 208 | }, 209 | modifier = Modifier.fillMaxWidth(), 210 | colors = ButtonDefaults.buttonColors( 211 | backgroundColor = items[pagerState.currentPage].mainColor 212 | ), 213 | contentPadding = PaddingValues(vertical = 12.dp), 214 | elevation = ButtonDefaults.elevation( 215 | defaultElevation = 0.dp 216 | ) 217 | ) { 218 | Text( 219 | text = "Get Started", 220 | color = Color.White, 221 | fontSize = 16.sp 222 | ) 223 | } 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | @Composable 234 | fun PagerIndicator( 235 | modifier: Modifier = Modifier, 236 | currentPage: Int, 237 | items: List 238 | ) { 239 | Row( 240 | horizontalArrangement = Arrangement.SpaceBetween, 241 | modifier = modifier.padding(top = 20.dp) 242 | ) { 243 | repeat(items.size) { 244 | Indicator(isSelected = it == currentPage, color = items[it].mainColor) 245 | } 246 | } 247 | } 248 | 249 | @Preview(showBackground = true) 250 | @Composable 251 | fun DefaultPreview() { 252 | OnboardingTheme { 253 | OnboardingApp() 254 | } 255 | } --------------------------------------------------------------------------------