Esistono diversi modi per creare le tue app utilizzando gli stili. La scelta dipende dalla posizione della tua app rispetto all'adozione di Material Design:
- Sistema di progettazione completamente personalizzato, che non utilizza Material Design
- Consiglio: definisci stili dei componenti che utilizzano valori del tema ed esponi i parametri di stile nei componenti del sistema di progettazione.
- Utilizzo di Material Design
- Consiglio: attendi l'adozione di Material per l'integrazione con gli stili. Se possibile, utilizza gli stili sui tuoi componenti.
Il livello Stile
Nel modello Compose tradizionale, la personalizzazione si basa spesso in larga misura sulla sostituzione dei token globali (colori e tipografia) forniti da MaterialTheme o sul wrapping e sulla sostituzione delle proprietà di un componente componibile del sistema di progettazione, ove possibile.
A volte, all'interno del livello Material ci sono proprietà che non sono esposte
tramite i sottosistemi o i parametri, ma sono valori predefiniti hardcoded nel
componente stesso.
Con l'API Styles, è disponibile un nuovo livello di astrazione che funge da ponte tra sottosistemi e componenti: Styles.
| incorporato | Responsabilità | Esempio |
|---|---|---|
| Valori del sottosistema | Valori denominati | val Primary = Color(0xFF34A85E) |
| Stili atomici | Stile che modifica esattamente una proprietà | val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then largeSize then interactiveShadowAtomic |
| Stili dei componenti | Configurazioni specifiche per i componenti | Un pulsante con sfondo principale e spaziatura interna di 16 dp. val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) } |
| Componenti | L'elemento UI funzionale che utilizza uno stile. | Button(style = buttonStyle) { ... } |
Stili atomici e monolitici
Con l'API Styles, puoi suddividere uno stile in stili atomici separati.
Anziché definire stili complessi e specifici per i componenti come baseButtonStyle,
puoi anche creare stili di utilità piccoli e monoscopo. Questi fungono da
"atomi".
// Define single-purpose "atomic" styles val paddingAtomic = Style { contentPadding(16.dp) } val roundedCornerShapeAtomic = Style { shape(RoundedCornerShape(8.dp)) } val primaryBackgroundAtomic = Style { background(Color.Blue) } val largeSizeAtomic = Style { size(100.dp, 40.dp) } val interactiveShadowAtomic = Style { hovered { animate { dropShadow( Shadow( offset = DpOffset( 0.dp, 0.dp ), radius = 2.dp, spread = 0.dp, color = Color.Blue, ) ) } } }
Composizione con "allora"
Una delle potenti funzionalità della nuova API Styles è l'operatore then, che
consente di unire più oggetti Style. In questo modo puoi creare un componente utilizzando
classi di utilità atomiche.
Tradizionale (non atomico):
// One large monolithic style val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) }
Refactoring atomico:
// Combine atoms to create the final appearance val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then interactiveShadowAtomic
Adottare gli stili nel sistema di progettazione
Quando adotti gli stili all'interno del tuo sistema di progettazione, considera le seguenti opzioni, a seconda della posizione del tuo sistema di progettazione nello spettro.
Sistema di progettazione personalizzato con Stili
Da considerare quando: ti è stata consegnata una guida al brand completa che non si basa su Material Design e non prevedi di utilizzarlo.
Strategia: implementa un sistema di progettazione completamente personalizzato ed esponi gli stili come parte del tema.
Questa opzione è il percorso personalizzato se non utilizzi Material come lingua principale del sistema di progettazione. Hai ignorato completamente MaterialTheme per le definizioni visive e
hai già creato il tuo tema personalizzato. Crea un CompanyTheme che
funge da contenitore per gli stili.
- Come funziona: crea un oggetto
CompanyThemeche contenga oggettiStyleper ogni componente del sistema. I tuoi componenti (wrapper intorno alla logica Material o implementazioni personalizzate diBoxoLayout) utilizzano direttamente questi stili ed espongono un parametroStyleper i consumatori del tuo sistema di progettazione. - Lo strato Stile: gli stili sono la definizione principale del tuo sistema di progettazione. I token sono variabili denominate inserite in questi stili. Ciò consente una personalizzazione approfondita, ad esempio la definizione di animazioni uniche per i cambiamenti di stato (ad esempio, l'animazione di scala e colore alla pressione).
Se stai creando un tema personalizzato senza utilizzare Material e vuoi adottare stili, aggiungi l'elenco di stili al tema. In questo modo puoi accedere agli stili di base da qualsiasi punto del progetto.
Crea una classe
Stylesche memorizzi i vari stili della tua applicazione e crea i valori predefiniti. Ad esempio, nell'app Jetsnack, la classe è denominataJetsnackStyles:object JetsnackStyles{ val buttonStyle: Style = Style { shape(shapes.medium) background(colors.brand) contentColor(colors.textPrimary) contentPaddingVertical(8.dp) contentPaddingHorizontal(24.dp) textStyle(typography.labelLarge) disabled { animate { background(colors.brandSecondary) } } } val cardStyle: Style = Style { shape(shapes.medium) background(colors.uiBackground) contentColor(colors.textPrimary) } }
Fornisci
Stylescome parte del tema generale ed esponi le funzioni di estensione helper suStyleScopeper accedere ai sottosistemi:@Immutable class JetsnackTheme( val colors: JetsnackColors = LightJetsnackColors, val typography: androidx.compose.material3.Typography = androidx.compose.material3.Typography(), val shapes: Shapes = Shapes() ) { companion object { val colors: JetsnackColors @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.colors val typography: androidx.compose.material3.Typography @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.typography val shapes: Shapes @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.shapes val styles: JetsnackStyles = JetsnackStyles val LocalJetsnackTheme: ProvidableCompositionLocal<JetsnackTheme> get() = LocalJetsnackThemeInstance } } val StyleScope.colors: JetsnackColors get() = LocalJetsnackTheme.currentValue.colors val StyleScope.typography: androidx.compose.material3.Typography get() = LocalJetsnackTheme.currentValue.typography val StyleScope.shapes: Shapes get() = LocalJetsnackTheme.currentValue.shapes internal val LocalJetsnackThemeInstance = staticCompositionLocalOf { JetsnackTheme() } @Composable fun JetsnackTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colors = if (darkTheme) DarkJetsnackColors else LightJetsnackColors val theme = JetsnackTheme(colors = colors) CompositionLocalProvider( LocalJetsnackTheme provides theme, ) { MaterialTheme( typography = LocalJetsnackTheme.current.typography, shapes = LocalJetsnackTheme.current.shapes, content = content, ) } }
Accedi a
JetsnackStylesall'interno del tuo composable:@Composable fun CustomButton(modifier: Modifier, style: Style = Style, text: String) { val interactionSource = remember { MutableInteractionSource() } val styleState = remember(interactionSource) { MutableStyleState(interactionSource) } // Apply style to top level container in combination with incoming style from parameter. Box(modifier = modifier .clickable( interactionSource = interactionSource, indication = null, enabled = true, role = Role.Button, onClick = { }, ) .styleable(styleState, JetsnackTheme.styles.buttonStyle, style)) { Text(text) } }
Oltre all'adozione del tema globale, esistono strategie alternative per incorporare
Styles nelle tue app. Puoi sfruttare Styles in linea per siti di chiamate specifici o utilizzare definizioni statiche quando le funzionalità di applicazione di temi complete non sono necessarie.
Styles non deve essere scambiato in modo condizionale, a meno che lo stile non sia
fondamentalmente diverso. È preferibile accedere ai token dinamici all'interno di una definizione visiva anziché passare da un oggetto di stile all'altro.