Skip to content

Commit

Permalink
feat(onboarding): basic onboarding and edge to edge (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
kikin81 authored May 17, 2024
1 parent aa2ed16 commit 5820b18
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 9 deletions.
41 changes: 41 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions app/src/main/java/us/kikin/android/gamingbacklog/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import us.kikin.android.gamingbacklog.presentation.onboarding.OnboardingScreen
import us.kikin.android.gamingbacklog.ui.theme.AppTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
installSplashScreen()
enableEdgeToEdge()
setContent {
AppTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
Box(modifier = Modifier.background(color = MaterialTheme.colorScheme.background)) {
OnboardingScreen()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package us.kikin.android.gamingbacklog.presentation.common

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun PageIndicator(
pageSize: Int,
selectedPage: Int,
modifier: Modifier = Modifier,
selectedColor: Color = MaterialTheme.colorScheme.primary,
unselectedColor: Color = Color.Blue
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween
) {
repeat(pageSize) { page ->
Box(
modifier = Modifier
.size(14.dp)
.clip(CircleShape)
.background(
if (page == selectedPage) selectedColor else unselectedColor
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package us.kikin.android.gamingbacklog.presentation.common

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import us.kikin.android.gamingbacklog.ui.theme.AppTheme

@Composable
fun PrimaryButton(
text: String,
modifier: Modifier = Modifier,
onClick: () -> Unit
) {
Button(
modifier = modifier,
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
shape = RoundedCornerShape(size = 6.dp)
) {
Text(
text = text,
style = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.SemiBold)
)
}
}

@Composable
@Preview
internal fun PrimaryButtonPreview() {
AppTheme {
PrimaryButton(text = "Hello World") {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package us.kikin.android.gamingbacklog.presentation.common

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight

@Composable
fun GhostButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TextButton(onClick = onClick, modifier = modifier) {
Text(
text = text,
style = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.SemiBold)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package us.kikin.android.gamingbacklog.presentation.onboarding

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import us.kikin.android.gamingbacklog.presentation.common.GhostButton
import us.kikin.android.gamingbacklog.presentation.common.PageIndicator
import us.kikin.android.gamingbacklog.presentation.common.PrimaryButton
import us.kikin.android.gamingbacklog.presentation.onboarding.components.OnboardingPage
import us.kikin.android.gamingbacklog.ui.theme.AppTheme

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun OnboardingScreen(
modifier: Modifier = Modifier
) {
Column(modifier = modifier.fillMaxSize()) {
val pagerState = rememberPagerState(initialPage = 0) {
pages.size
}

val buttonState = remember {
derivedStateOf {
when (pagerState.currentPage) {
0 -> listOf("", "Next")
1 -> listOf("Back", "Next")
2 -> listOf("Back", "Next")
3 -> listOf("Back", "Get Started")
else -> listOf("", "")
}
}
}

HorizontalPager(state = pagerState) { index ->
OnboardingPage(page = pages[index])
}

Spacer(modifier = Modifier.weight(1f))

Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.navigationBarsPadding(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
PageIndicator(
modifier = Modifier.width(68.dp),
pageSize = pages.size,
selectedPage = pagerState.currentPage
)

Row(verticalAlignment = Alignment.CenterVertically) {
val scope = rememberCoroutineScope()
if (buttonState.value[0].isNotEmpty()) {
GhostButton(text = buttonState.value[0],
onClick = {
scope.launch { pagerState.animateScrollToPage(pagerState.currentPage - 1) }
})
}
PrimaryButton(text = buttonState.value[1],
onClick = {
scope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) }
})
}
}
Spacer(modifier = Modifier.weight(0.5f))
}
}

@Composable
@Preview(showBackground = true)
internal fun OnboardingScreenPreview() {
AppTheme {
OnboardingScreen()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package us.kikin.android.gamingbacklog.presentation.onboarding

import androidx.annotation.DrawableRes
import us.kikin.android.gamingbacklog.R

data class Page(
val title: String,
val description: String,
@DrawableRes val imageRes: Int
)

val pages = listOf(
Page(
title = "Welcome to Gaming Backlog",
description = "Keep track of your video game backlog",
imageRes = R.drawable.onboarding1
),
Page(
title = "Add Games",
description = "Add games to your backlog",
imageRes = R.drawable.onboarding2
),
Page(
title = "Track Progress",
description = "Track your progress and completion status",
imageRes = R.drawable.onboarding3
),
Page(
title = "Get Started",
description = "Get started by adding your first game",
imageRes = R.drawable.onboarding4
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package us.kikin.android.gamingbacklog.presentation.onboarding.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import us.kikin.android.gamingbacklog.R
import us.kikin.android.gamingbacklog.presentation.onboarding.Page
import us.kikin.android.gamingbacklog.ui.theme.AppTheme

@Composable
fun OnboardingPage(
page: Page,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Image(
painter = painterResource(id = page.imageRes),
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(fraction = 0.6f),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = page.title,
style = MaterialTheme.typography.displaySmall,
modifier = Modifier.padding(horizontal = 32.dp),
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = page.description,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(horizontal = 32.dp),
color = MaterialTheme.colorScheme.onSurface
)
}
}

@Composable
@Preview(showBackground = true)
internal fun OnboardingPagePreview() {
AppTheme {
OnboardingPage(
page = Page(
title = "Welcome to Gaming Backlog",
description = "Keep track of your video game backlog",
imageRes = R.drawable.onboarding1
)
)
}
}
Binary file added app/src/main/res/drawable/onboarding1.webp
Binary file not shown.
Binary file added app/src/main/res/drawable/onboarding2.webp
Binary file not shown.
Binary file added app/src/main/res/drawable/onboarding3.webp
Binary file not shown.
Binary file added app/src/main/res/drawable/onboarding4.webp
Binary file not shown.
5 changes: 4 additions & 1 deletion app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="Theme.GamingBacklog" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.GamingBacklog" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>

0 comments on commit 5820b18

Please sign in to comment.