على الرغم من أنّ Material هو نظام التصميم الذي ننصح به وأنّ Jetpack Compose يوفّر تنفيذًا لـ Material، لست ملزمًا باستخدامه. تم إنشاء Material بالكامل على واجهات برمجة تطبيقات عامة، لذا من الممكن إنشاء نظام تصميم خاص بك بالطريقة نفسها.
هناك عدة طرق يمكنك اتّباعها:
- يمكنك توسيع
MaterialThemeباستخدام قيم إضافية للمظاهر. - استبدال نظام واحد أو أكثر من أنظمة Material، مثل
ColorSchemeأوTypographyأوShapes، بعمليات تنفيذ مخصّصة مع الاحتفاظ بالأنظمة الأخرى - تنفيذ نظام تصميم مخصّص بالكامل ليحلّ محلّ
MaterialTheme
قد ترغب أيضًا في مواصلة استخدام مكونات Material مع نظام تصميم مخصص. يمكنك إجراء ذلك، ولكن هناك بعض النقاط التي يجب أخذها في الاعتبار لتناسب النهج الذي اتّبعته.
لمزيد من المعلومات حول البُنى وواجهات برمجة التطبيقات ذات المستوى الأدنى التي يستخدمها MaterialTheme
وأنظمة التصميم المخصّصة، يمكنك الاطّلاع على دليل بنية المظهر في Compose.
توسيع نطاق تخصيص التصميم المتعدد الأبعاد
تتّبع Compose 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
عند توسيع نطاق تخصيص التصميم المتعدد الأبعاد، يتم الاحتفاظ بقيم MaterialTheme الحالية
وتظل مكوّنات التصميم المتعدد الأبعاد تتضمّن الإعدادات التلقائية المناسبة.
إذا أردت استخدام القيم الموسّعة في المكوّنات، يمكنك تضمينها في الدوال المركّبة الخاصة بك، وضبط القيم التي تريد تغييرها مباشرةً، وعرض القيم الأخرى كمَعلمات للدالة المركّبة التي تحتوي عليها:
@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 الفرعية
بدلاً من توسيع نطاق تخصيص التصميم المتعدد الأبعاد، قد تحتاج إلى استبدال نظام واحد أو أكثر من الأنظمة (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 = { /* ... */ }) { /* ... */ } } }
تنفيذ نظام تصميم مخصّص بالكامل
قد تحتاج إلى استبدال "تخصيص التصميم المتعدد الأبعاد" بنظام تصميم مخصّص بالكامل.
لنفترض أنّ MaterialTheme يوفّر الأنظمة التالية:
-
ColorSchemeوTypographyوShapes: أنظمة تخصيص التصميم المتعدد الأبعاد -
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.
التطبيقات المقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعَّل
- Material Design 3 في Compose
- نقل البيانات من Material 2 إلى Material 3 في Compose
- بنية المظهر في Compose