Choć Material Design to zalecany system, to w Jetpack Compose implementacji Material Design, nie musisz z niego korzystać. Materiał jest wykonany całkowicie na publicznych interfejsach API, można więc stworzyć własny system w ten sam sposób.
Możesz zastosować kilka metod:
- Przedłużam okres
MaterialTheme
o dodatkowe motywy wartości - Zastąpienie co najmniej jednego systemu Material Design (
Colors
,Typography
lubShapes
) implementacją niestandardową przy zachowaniu pozostałych. - Wdrażanie w pełni niestandardowego systemu projektowania w zamian za
MaterialTheme
Możesz też nadal używać komponentów Material w systemie projektowania niestandardowym. Jest to możliwe, ale należy pamiętać o kilku kwestiach, aby dostosować je do wybranej przez Ciebie metody.
Aby dowiedzieć się więcej o konstrukcjach i interfejsach API niższego poziomu, których używa MaterialTheme
i niestandardowych systemów projektowania znajdziesz w przewodniku Anatomia motywu w Compose.
Motyw rozszerzający
Tworzenie bliższych modeli w narzędziu Compose Material Motywy Material Design aby stały się proste i łatwe do pisania zgodnie z wytycznymi dotyczącymi materiału. Jest to jednak można rozszerzyć zestawy kolorów, typografii i kształtów przy użyciu dodatkowych .
Najprostszym sposobem jest dodanie właściwości rozszerzenia:
// Use with MaterialTheme.colorScheme.snackbarAction val ColorScheme.snackbarAction: Color @Composable get() = if (isSystemInDarkTheme()) Red300 else Red700 // Use with MaterialTheme.typography.textFieldInput val Typography.textFieldInput: TextStyle get() = TextStyle(/* ... */) // Use with MaterialTheme.shapes.card val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
Dzięki temu interfejsy API do korzystania z MaterialTheme
będą spójne. Oto przykład:
zdefiniowaną przez funkcję tworzenia wiadomości to
surfaceColorAtElevation
który określa kolor powierzchni, która ma zostać użyta w zależności od wysokości n.p.m.
Innym sposobem jest zdefiniowanie rozszerzonego motywu, który „zawija” MaterialTheme
i
jej wartości.
Załóżmy, że chcesz dodać dwa dodatkowe kolory – caution
i onCaution
,
żółty do działań, które są półniebezpieczne – przy jednoczesnym zachowaniu
istniejące kolory Material Design:
@Immutable data class ExtendedColors( val caution: Color, val onCaution: Color ) val LocalExtendedColors = staticCompositionLocalOf { ExtendedColors( caution = Color.Unspecified, onCaution = Color.Unspecified ) } @Composable fun ExtendedTheme( /* ... */ content: @Composable () -> Unit ) { val extendedColors = ExtendedColors( caution = Color(0xFFFFCC02), onCaution = Color(0xFF2C2D30) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ..., typography = ..., shapes = ... */ content = content ) } } // Use with eg. ExtendedTheme.colors.caution object ExtendedTheme { val colors: ExtendedColors @Composable get() = LocalExtendedColors.current }
Jest to podobne do interfejsów API do obsługi MaterialTheme
. Obsługuje też wiele motywów, ponieważ możesz zagnieżdżać ExtendedTheme
w taki sam sposób jak MaterialTheme
.
Korzystanie z komponentów Material Design
Podczas rozszerzania motywu Material Theme są zachowane dotychczasowe wartości MaterialTheme
, a elementy Material nadal mają odpowiednie wartości domyślne.
Jeśli chcesz używać rozszerzonych wartości w komponentach, owiń je we własne funkcje kompozytowe, ustawiając bezpośrednio wartości, które chcesz zmienić, i wyświetlając inne jako parametry dla komponentu zawierającego:
@Composable fun ExtendedButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = ExtendedTheme.colors.caution, contentColor = ExtendedTheme.colors.onCaution /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
Zastąpisz wtedy użycia funkcji Button
funkcją ExtendedButton
, gdzie:
odpowiednie.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
Zastąp podsystemy Material
Zamiast rozszerzać Motyw Material, możesz zastąpić co najmniej 1 system (Colors
, Typography
lub Shapes
) implementacją niestandardową, zachowując pozostałe.
Załóżmy, że chcesz zastąpić systemy typu i kształtu, zachowując system kolorów:
@Immutable data class ReplacementTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class ReplacementShapes( val component: Shape, val surface: Shape ) val LocalReplacementTypography = staticCompositionLocalOf { ReplacementTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalReplacementShapes = staticCompositionLocalOf { ReplacementShapes( component = RoundedCornerShape(ZeroCornerSize), surface = RoundedCornerShape(ZeroCornerSize) ) } @Composable fun ReplacementTheme( /* ... */ content: @Composable () -> Unit ) { val replacementTypography = ReplacementTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val replacementShapes = ReplacementShapes( component = RoundedCornerShape(percent = 50), surface = RoundedCornerShape(size = 40.dp) ) CompositionLocalProvider( LocalReplacementTypography provides replacementTypography, LocalReplacementShapes provides replacementShapes ) { MaterialTheme( /* colors = ... */ content = content ) } } // Use with eg. ReplacementTheme.typography.body object ReplacementTheme { val typography: ReplacementTypography @Composable get() = LocalReplacementTypography.current val shapes: ReplacementShapes @Composable get() = LocalReplacementShapes.current }
Korzystanie z komponentów Material Design
Po zastąpieniu co najmniej 1 systemu MaterialTheme
przy użyciu Material
mogą zawierać niepożądane wartości koloru, typu lub kształtu materiału.
Jeśli chcesz używać wartości zastępczych w komponentach, owiń je w własne funkcje składane, ustawiając bezpośrednio wartości dla odpowiedniego systemu i wyświetlając inne jako parametry dla zawierającego je komponentu składanego.
@Composable fun ReplacementButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( shape = ReplacementTheme.shapes.component, onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = ReplacementTheme.typography.body ) { content() } } ) }
Następnie w odpowiednich miejscach zastąpisz Button
wartością ReplacementButton
.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
Wdrożenie systemu w pełni niestandardowego
Możesz zastąpić motyw Material Theming w ramach w pełni niestandardowego systemu projektowania.
Pamiętaj, że MaterialTheme
udostępnia te systemy:
Colors
,Typography
iShapes
: systemy motywu Material DesignTextSelectionColors
: kolory użyte do zaznaczenia tekstu przezText
iTextField
Ripple
iRippleTheme
: implementacja materiałuIndication
Jeśli nadal chcesz używać komponentów Material Design, z tych systemów w niestandardowym motywie lub motywach, albo obsługuje systemy w aby uniknąć niepożądanego działania.
Jednak systemy projektowania nie ograniczają się do koncepcji, na których opiera się Material. Ty mogą modyfikować istniejące systemy i wprowadzać zupełnie nowe i typów, aby dostosować inne koncepcje do motywów.
W poniższym kodzie modelujemy niestandardowy system kolorów obejmujący gradienty
(List<Color>
), uwzględnij system typów, wprowadź nowy system wysokości względnej,
i wyklucz inne systemy dostarczane przez MaterialTheme
:
@Immutable data class CustomColors( val content: Color, val component: Color, val background: List<Color> ) @Immutable data class CustomTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class CustomElevation( val default: Dp, val pressed: Dp ) val LocalCustomColors = staticCompositionLocalOf { CustomColors( content = Color.Unspecified, component = Color.Unspecified, background = emptyList() ) } val LocalCustomTypography = staticCompositionLocalOf { CustomTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalCustomElevation = staticCompositionLocalOf { CustomElevation( default = Dp.Unspecified, pressed = Dp.Unspecified ) } @Composable fun CustomTheme( /* ... */ content: @Composable () -> Unit ) { val customColors = CustomColors( content = Color(0xFFDD0D3C), component = Color(0xFFC20029), background = listOf(Color.White, Color(0xFFF8BBD0)) ) val customTypography = CustomTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } // Use with eg. CustomTheme.elevation.small object CustomTheme { val colors: CustomColors @Composable get() = LocalCustomColors.current val typography: CustomTypography @Composable get() = LocalCustomTypography.current val elevation: CustomElevation @Composable get() = LocalCustomElevation.current }
Korzystanie z komponentów Material Design
Jeśli nie ma elementu MaterialTheme
, użyj komponentów Material w obecnej postaci,
niepożądanych kolorów, typów i kształtów oraz oznaczeń.
Jeśli chcesz użyć niestandardowych wartości w komponentach, umieść je w osobnym elemencie kompozycyjnym. bezpośrednio określając wartości dla danego układu innych jako parametry elementu kompozycyjnego.
Zalecamy, aby uzyskać dostęp do wartości ustawionych w motywie niestandardowym.
Jeśli w Twoim motywie nie ma informacji o usługach Color
, TextStyle
, Shape
ani innych systemach, możesz je zakodować na stałe.
@Composable fun CustomButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = CustomTheme.colors.component, contentColor = CustomTheme.colors.content, disabledContainerColor = CustomTheme.colors.content .copy(alpha = 0.12f) .compositeOver(CustomTheme.colors.component), disabledContentColor = CustomTheme.colors.content .copy(alpha = 0.38f) ), shape = ButtonShape, elevation = ButtonDefaults.elevatedButtonElevation( defaultElevation = CustomTheme.elevation.default, pressedElevation = CustomTheme.elevation.pressed /* disabledElevation = 0.dp */ ), onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = CustomTheme.typography.body ) { content() } } ) } val ButtonShape = RoundedCornerShape(percent = 50)
Jeśli wprowadzisz nowe typy klas, np. List<Color>
, aby reprezentować gradienty, lepiej będzie zaimplementować komponenty od podstaw, zamiast je opakowywać. Spójrzmy na przykład na
JetsnackButton
z próbki Jetsnack.
Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Material Design 3 w Compose
- Migracja z wersji Material 2 na wersję Material 3 w komponencie
- Składnia motywu w sekcji Tworzenie