identity: shared M3 Expressive theme factory + nav slide spec
FloretExpressiveTheme(lightScheme, darkScheme, darkTheme, dynamicColor, typography) holds the family's theme mechanics — dynamic-colour resolution (API 31+), system light/dark fallback, and the STANDARD motion scheme — while each app supplies its own seed-derived colour schemes, so the look stays per-app and the machinery is shared. rememberNavSlideSpec() shares the fast spatial nav slide. android-library, Compose-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
39
identity/build.gradle.kts
Normal file
39
identity/build.gradle.kts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// identity — the family's Material 3 Expressive theme factory + shared motion.
|
||||||
|
// Holds the theme MECHANICS (dynamic-colour resolution, light/dark, the STANDARD
|
||||||
|
// motion scheme) and the nav slide spec; each app supplies its own seed-derived
|
||||||
|
// colour schemes, so the look stays per-app while the machinery is shared.
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.library)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "de.jeanlucmakiola.floret.identity"
|
||||||
|
compileSdk = 37
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 29
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.ui)
|
||||||
|
implementation(libs.androidx.foundation)
|
||||||
|
implementation(libs.androidx.material3)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package de.jeanlucmakiola.floret.identity
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The horizontal slide spec for screen-to-screen navigation: the *fast*
|
||||||
|
* spring-physics spec from the active motion scheme — snappy with a subtle
|
||||||
|
* springy settle, rather than a fixed easing curve. Shared so the family's apps
|
||||||
|
* move the same way. Read in composable scope so the non-composable `NavHost`
|
||||||
|
* transition lambdas can capture it.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun rememberNavSlideSpec(): FiniteAnimationSpec<IntOffset> =
|
||||||
|
MaterialTheme.motionScheme.fastSpatialSpec()
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package de.jeanlucmakiola.floret.identity
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
|
import androidx.compose.material3.MotionScheme
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The family's Material 3 Expressive theme factory. Honours system light/dark
|
||||||
|
* and Material You dynamic colour (API 31+), falling back to the app-supplied
|
||||||
|
* [lightScheme] / [darkScheme] otherwise. Routes all component + custom motion
|
||||||
|
* through `MaterialTheme.motionScheme` with the **standard** scheme — spring
|
||||||
|
* choreography without the overshoot, the family's deliberate motion choice.
|
||||||
|
*
|
||||||
|
* The mechanics are shared; the look is not. Each app keeps its identity by
|
||||||
|
* passing its own hand-tuned, seed-derived [lightScheme] / [darkScheme] and an
|
||||||
|
* optional [typography]. A thin per-app wrapper (e.g. `AgendulaTheme`) supplies
|
||||||
|
* those and forwards [darkTheme] / [dynamicColor] from settings.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun FloretExpressiveTheme(
|
||||||
|
lightScheme: ColorScheme,
|
||||||
|
darkScheme: ColorScheme,
|
||||||
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
|
dynamicColor: Boolean = true,
|
||||||
|
typography: Typography = Typography(),
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
val colorScheme = when {
|
||||||
|
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
if (darkTheme) dynamicDarkColorScheme(ctx) else dynamicLightColorScheme(ctx)
|
||||||
|
}
|
||||||
|
darkTheme -> darkScheme
|
||||||
|
else -> lightScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialExpressiveTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = typography,
|
||||||
|
motionScheme = MotionScheme.standard(),
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -33,3 +33,4 @@ rootProject.name = "floret-kit"
|
|||||||
|
|
||||||
include(":core-time")
|
include(":core-time")
|
||||||
include(":core-crash")
|
include(":core-crash")
|
||||||
|
include(":identity")
|
||||||
|
|||||||
Reference in New Issue
Block a user