درحالیکه Material سیستم طراحی توصیهشده ما است و Jetpack Compose پیادهسازی Material را ارائه میدهد، شما مجبور به استفاده از آن نیستید. Material کاملاً براساس میاناهای برنامهسازی کاربردی عمومی ساخته شده است، بنابراین میتوانید سیستم طراحی خودتان را به همین روش ایجاد کنید.
چند رویکرد وجود دارد که میتوانید اتخاذ کنید:
MaterialThemeرا با مقادیر زمینهسازی بیشتر گسترش دهید.- یک یا چند سیستم Material —
ColorScheme،Typography، یاShapes— را با پیادهسازیهای سفارشی جایگزین کنید و درعینحال دیگر سیستمها را حفظ کنید. - سیستم طراحی کاملاً سفارشی را پیادهسازی کنید تا جایگزین
MaterialThemeشود.
همچنین ممکن است بخواهید همچنان از مؤلفههای Material با سیستم طراحی سفارشی استفاده کنید. انجام این کار ممکن است اما نکاتی وجود دارد که باید برای مطابقت با رویکردی که اتخاذ کردهاید درنظر داشته باشید.
برای آشنایی بیشتر با ساختارها و میاناهای برنامهسازی کاربردی سطح پایینتر که توسط MaterialTheme
و سیستمهای طراحی سفارشی استفاده میشوند، راهنمای کالبدشناسی زمینه در Compose را بررسی کنید.
گسترش موضوعدهی Material
Compose Material از موضوعدهی Material بهطور دقیق مدلسازی میکند تا پیروی از دستورالعملهای Material ساده و ایمن ازنظر تایپی باشد. بااینحال، میتوان مجموعههای رنگ، طرح حروف، و شکل را با مقادیر اضافی گسترش داد. سادهترین رویکرد افزودن ویژگیهای افزونه است:
// 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)
این کار باعث سازگاری با میاناهای برنامهسازی کاربردی استفاده از MaterialTheme میشود. نمونهای از این
که توسط خود Compose تعریف شده است
surfaceColorAtElevation است،
که رنگ سطح را که باید بسته به
ارتفاع استفاده شود تعیین میکند.
رویکرد دیگر این است که زمینهای گسترده تعریف کنید که MaterialTheme و مقادیر آن را «دربرگیرد».
فرض کنید میخواهید دو رنگ دیگر اضافه کنید — caution و onCaution، رنگ زردی که برای کنشهای نیمهخطرناک استفاده میشود — درحالیکه رنگهای Material موجود را حفظ میکنید:
@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 }
این مورد مشابه میاناهای برنامهسازی کاربردی استفاده از MaterialTheme است. همچنین از چندین زمینه پشتیبانی میکند
زیرا میتوانید ExtendedTheme را به همان روش MaterialTheme تودرتو کنید.
استفاده از مؤلفههای Material
هنگام گسترش «موضوعدهی Material»، مقادیر MaterialTheme موجود حفظ میشوند
و مؤلفههای Material همچنان پیشفرضهای معقولی دارند.
اگر میخواهید از مقادیر گسترشیافته در مؤلفهها استفاده کنید، آنها را در توابع ترکیبپذیر خودتان قرار دهید، مقادیری را که میخواهید تغییر دهید مستقیماً تنظیم کنید، و سایر مقادیر را بهعنوان پارامتر در ترکیبپذیر حاوی آنها نمایش دهید:
@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 ) }
سپس میتوانید موارد استفاده از Button را با ExtendedButton در جاهای مناسب جایگزین کنید.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
جایگزین کردن زیرسیستمهای Material
بهجای گسترش دادن «موضوعدهی Material»، شاید بخواهید یک یا چند سیستم (ColorScheme، Typography، یا Shapes) را با پیادهسازی سفارشی جایگزین کنید و درعینحال بقیه را حفظ کنید.
فرض کنید میخواهید سیستمهای نوع و شکل را جایگزین کنید و درعینحال سیستم رنگ را حفظ کنید:
@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 }
استفاده از مؤلفههای Material
وقتی یک یا چند سیستم MaterialTheme جایگزین شده باشد، استفاده از مؤلفههای Material بههمان صورت ممکن است منجر به مقادیر ناخواسته رنگ، نوع، یا شکل Material شود.
اگر میخواهید از مقادیر جایگزین در مؤلفهها استفاده کنید، آنها را در توابع ترکیبپذیر خودتان قرار دهید، مقادیر را مستقیماً برای سیستم مربوطه تنظیم کنید، و دیگران را بهعنوان پارامترهایی برای ترکیبپذیر حاوی آنها نمایش دهید.
@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() } } ) }
سپس میتوانید موارد استفاده از Button را با ReplacementButton در جاهای مناسب جایگزین کنید.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
پیادهسازی سیستم طراحی کاملاً سفارشی
شاید بخواهید «موضوعدهی Material» را با سیستم طراحی کاملاً سفارشی جایگزین کنید.
درنظر داشته باشید که MaterialTheme سیستمهای زیر را ارائه میدهد:
ColorScheme،Typography، وShapes: سیستمهای موضوعدهی Material-
TextSelectionColors: رنگهای استفادهشده برای انتخاب نوشتار توسطTextوTextField ripple، ارائهشده بهعنوانLocalIndication: پیادهسازی مادیIndication
اگر میخواهید به استفاده از مؤلفههای Material ادامه دهید، باید برخی از این سیستمها را در زمینههای سفارشیتان جایگزین کنید یا سیستمها را در مؤلفههایتان مدیریت کنید تا از رفتار ناخواسته جلوگیری شود.
بااینحال، سیستمهای طراحی به مفاهیمی که Material به آنها متکی است محدود نمیشوند. میتوانید سیستمهای موجود را اصلاح کنید و سیستمهای کاملاً جدیدی را — با کلاسها و انواع جدید — معرفی کنید تا مفاهیم دیگر را با زمینهها سازگار کنید.
در کد زیر، سیستم رنگ سفارشیای را مدلسازی میکنیم که شامل گرادیانها (List<Color>) است،
سیستم نوع را اضافه میکنیم، سیستم ارتفاع جدیدی را معرفی میکنیم،
و سیستمهای دیگری را که 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 }
استفاده از مؤلفههای Material
وقتی هیچ MaterialTheme وجود نداشته باشد، استفاده از مؤلفههای Material بههمان صورت که هستند منجر به
مقادیر رنگ، نوع، و شکل Material و رفتار نشانگر ناخواسته خواهد شد.
اگر میخواهید از مقادیر سفارشی در مؤلفهها استفاده کنید، آنها را در توابع ترکیبپذیر خودتان قرار دهید، مقادیر را مستقیماً برای سیستم مربوطه تنظیم کنید، و دیگران را بهعنوان پارامترهایی برای ترکیبپذیر حاوی آن نشان دهید.
توصیه میکنیم به مقادیری که از طرح زمینه سفارشیتان تنظیم کردهاید دسترسی پیدا کنید.
یا اگر زمینهتان Color، TextStyle، Shape، یا
سیستمهای دیگر را ارائه نمیدهد، میتوانید آنها را کدگذاری ثابت کنید.
@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)
اگر انواع کلاس جدیدی معرفی کردهاید — مثل List<Color> برای نمایش
گرادیانها — بهتر است مؤلفهها را از ابتدا پیادهسازی کنید بهجای اینکه
آنها را بستهبندی کنید. برای مثال، به
JetsnackButton
از نمونه Jetsnack نگاه کنید.
توصیهشده برای شما
- توجه: وقتی جاوا اسکریپت خاموش باشد، نوشتار پیوند نمایش داده میشود
- Material Design 3 در Compose
- انتقال از Material 2 به Material 3 در Compose
- کالبدشناسی زمینه در Compose