Skip to content

Commit

Permalink
ADD custom theming
Browse files Browse the repository at this point in the history
Inspired by Hema implementation
  • Loading branch information
ninovanhooff committed Sep 10, 2024
1 parent ea6bb3d commit 6c6ca7c
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package nl.q42.template.ui.compose.composables.widgets

import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import nl.q42.template.ui.theme.AppTheme
import nl.q42.template.ui.theme.PreviewLightDark

@Composable
fun TemplateButton(text: String, onClick: () -> Unit) {
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = AppTheme.colors.accent,
contentColor = AppTheme.colors.buttonText
)
) {
Text(
text = text,
style = AppTheme.typography.body,
color = AppTheme.colors.buttonText
)
}
}

@Composable
@PreviewLightDark
private fun TemplateButtonPreview() {
AppTheme {
TemplateButton("Button", {})
}
}
11 changes: 11 additions & 0 deletions core/ui/src/main/kotlin/nl/q42/template/ui/theme/AppColorTokens.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package nl.q42.template.ui.theme

import androidx.compose.ui.graphics.Color

interface AppColorTokens {
val buttonText: Color
val accent: Color
val textPrimary: Color
val surface: Color
val error: Color
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package nl.q42.template.ui.theme

import androidx.compose.ui.graphics.Color

object AppColorTokensDark: AppColorTokens {
override val buttonText: Color = White
override val accent: Color = PurpleGrey80
override val textPrimary = White
override val surface = White
override val error = Pink80
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package nl.q42.template.ui.theme

import androidx.compose.ui.graphics.Color

object AppColorTokensLight : AppColorTokens {
override val buttonText: Color = White
override val accent: Color = Purple40
override val textPrimary = Black
override val surface: Color = White
override val error: Color = Red80
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Red80 = Color(0xFFE57373)

val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
Expand Down
10 changes: 10 additions & 0 deletions core/ui/src/main/kotlin/nl/q42/template/ui/theme/Dimens.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nl.q42.template.ui.theme

import androidx.compose.ui.unit.dp

object Dimens {
object Containers {
val cornerRadius = 8.dp
val cornerRadiusLarge = 16.dp
}
}
87 changes: 43 additions & 44 deletions core/ui/src/main/kotlin/nl/q42/template/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ package nl.q42.template.ui.theme

import android.annotation.SuppressLint
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

Expand All @@ -23,64 +22,64 @@ import androidx.core.view.WindowCompat
* README file of our project.
*/

private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80,
background = Black,
)

private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40,
background = White

/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
private val LocalAppTypography = staticCompositionLocalOf { AppTypography() }
private val LocalAppColorTokens = staticCompositionLocalOf<AppColorTokens> {
// Dummy default, will be replaced for the actual theme by the Provider
AppColorTokensLight
}
private val LocalAppShapes = staticCompositionLocalOf { AppShapes() }

@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
typography: AppTypography = AppTheme.typography,
colors: AppColorTokens = AppTheme.colors,
shapes: AppShapes = AppTheme.shapes,

// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}

darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
window.statusBarColor = colors.accent.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}

MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
CompositionLocalProvider(
LocalAppTypography provides typography,
LocalAppColorTokens provides if (darkTheme) AppColorTokensDark else AppColorTokensLight,
LocalAppShapes provides shapes,
content = content
)
}

object AppTheme {
val typography: AppTypography
@Composable
@ReadOnlyComposable
get() = LocalAppTypography.current
val colors: AppColorTokens
@Composable
@ReadOnlyComposable
get() = LocalAppColorTokens.current
val shapes: AppShapes
@Composable
@ReadOnlyComposable
get() = LocalAppShapes.current
}

@Immutable
data class AppShapes(
val small: Shape = RoundedCornerShape(Dimens.Containers.cornerRadius),
val medium: Shape = RoundedCornerShape(Dimens.Containers.cornerRadius),
val large: Shape = RoundedCornerShape(Dimens.Containers.cornerRadiusLarge)
)

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PreviewAppTheme(content: @Composable () -> Unit) {
AppTheme {
Expand Down
29 changes: 29 additions & 0 deletions core/ui/src/main/kotlin/nl/q42/template/ui/theme/Type.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nl.q42.template.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.runtime.Immutable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
Expand Down Expand Up @@ -31,4 +32,32 @@ val Typography = Typography(
letterSpacing = 0.5.sp
)
*/
)

val defaultFontFamily = FontFamily.Default

@Immutable
data class AppTypography (
// The names of these styles are shared with your team's design system
// So it is easy to communicate with designers about what text style to use

val body: TextStyle = TextStyle(
fontFamily = defaultFontFamily,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
val h1: TextStyle = TextStyle(
fontFamily = defaultFontFamily,
fontWeight = FontWeight.Bold,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
val label: TextStyle = TextStyle(
fontFamily = defaultFontFamily,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import nl.q42.template.home.main.presentation.HomeViewState
import nl.q42.template.ui.compose.composables.widgets.TemplateButton
import nl.q42.template.ui.compose.get
import nl.q42.template.ui.presentation.toViewStateString
import nl.q42.template.ui.theme.AppTheme
import nl.q42.template.ui.theme.PreviewAppTheme
import nl.q42.template.ui.theme.PreviewLightDark

Expand All @@ -36,21 +38,25 @@ internal fun HomeContent(
viewState.userEmailTitle?.get()?.let { Text(text = it) }

if (viewState.isLoading) CircularProgressIndicator()
if (viewState.showError) Text(text = "Error")
if (viewState.showError) Text(
text = "Error",
style = AppTheme.typography.body,
color = AppTheme.colors.error
)

Button(onClick = onLoadClicked) {
Text("Refresh")
}
TemplateButton("Refresh", onLoadClicked)

Button(onClick = onOpenSecondScreenClicked) {
Text("Open second screen")
}
Button(onClick = onOpenOnboardingClicked) {
Text("Open onboarding")
}
TemplateButton("Open second screen", onOpenSecondScreenClicked)

// Button(onClick = onOpenOnboardingClicked) {
// Text("Open onboarding")
// }
TemplateButton("Open Onboarding", onOpenOnboardingClicked)
}
}



@PreviewLightDark
@Composable
private fun HomeContentErrorPreview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import nl.q42.template.home.second.presentation.HomeSecondViewModel
import nl.q42.template.navigation.viewmodel.InitNavigator
import nl.q42.template.ui.compose.composables.widgets.TemplateButton
import nl.q42.template.ui.theme.AppTheme

@Destination
@Composable
Expand All @@ -40,10 +42,8 @@ fun HomeSecondScreen(
verticalArrangement = Arrangement.Center,
) {

Text(viewState.title, style = MaterialTheme.typography.titleMedium)
Text(viewState.title, style = AppTheme.typography.h1, color = AppTheme.colors.textPrimary)

Button(onClick = viewModel::onBackClicked) {
Text("Close")
}
TemplateButton("Close", viewModel::onBackClicked)
}
}

0 comments on commit 6c6ca7c

Please sign in to comment.