ללא מנוע עיצוב (Theming Engine), כל שינוי עיצובי באפליקציית white-label דורש בנייה מחדש ושחרור. אחד הלקוחות שלנו השקיע 72 שעות עבודה בחודש בעדכון ערכות נושא עבור 15 מותגים. לאחר הטמעת מנוע עיצוב, המשימה הצטמצמה לשעה אחת: פשוט העלאת קובץ JSON חדש לשרת. תוצאה: חיסכון של 98% בזמן וגמישות מלאה. יש לנו ניסיון של 6+ שנים בפיתוח white-label וסיפקנו למעלה מ-30 פרויקטים עם עיצוב דינמי. אם אתם צריכים לנהל בצורה גמישה את המראה של עשרות מותגים, צרו קשר — נציע פתרון.
כיצד מנוע עיצוב משפר אפליקציות White-Label?
מנוע עיצוב הוא מערכת המנהלת פרמטרים חזותיים של אפליקציה באמצעות אסימוני עיצוב. כל הצבעים, הגופנים, הגדלים, רדיוסי הפינות והצללים מועברים לקובץ JSON שנטען בעת ההפעלה או מוחלף בזמן ריצה. זה מאפשר החלפת ערכת נושא בזמן ריצה ללא בנייה מחדש, תומך במאות מותגים בבנייה אחת, ומתאים את הממשק להעדפות המשתמש. תצורה אחת לכל המותגים מפשטת את התחזוקה.
מבנה התצורה (דוגמה עבור דייר "מותג B")
{ "tenant": "brand_b",
"version": "2",
"colors": {
"primary": "#1A73E8",
"primary_variant": "#1557B0",
"secondary": "#FB8C00",
"background": "#FFFFFF",
"surface": "#F5F5F5",
"error": "#B00020",
"on_primary": "#FFFFFF",
"on_secondary": "#000000"
},
"typography": {
"font_family": "Inter",
"scale_factor": 1.0
},
"shape": {
"card_corner_radius": 12,
"button_corner_radius": 8,
"input_corner_radius": 4
},
"assets": {
"logo_url": "https://cdn.brand-b.com/logo.png",
"splash_bg_color": "#1A73E8"
}
} השוואה בין גישה סטטית לדינמית
| קריטריון | ערכת נושא סטטית (xcconfig/flavors) | ערכת נושא דינמית (מנוע עיצוב) |
|---|---|---|
| שינוי ערכת נושא ללא שחרור | לא | כן |
| מספר דיירים | עד 10 | בלתי מוגבל |
| תמיכה בהחלפת ערכת נושא בזמן ריצה | לא | כן |
| מורכבות הטמעה | נמוכה | בינונית |
| גמישות עיצובית | מוגבלת | גבוהה |
זמן הטמעה משוער לפי פלטפורמה
| פלטפורמה | פרויקט חדש | ריפקטורינג של פרויקט קיים |
|---|---|---|
| iOS (SwiftUI) | שבועיים | 3–5 שבועות |
| Android (Compose) | שבועיים | 3–5 שבועות |
| Flutter | 3 שבועות | 4–6 שבועות |
| React Native | 3 שבועות | 4–6 שבועות |
כיצד ליישם ערכת נושא דינמית ב-Jetpack Compose?
Jetpack Compose מקל משמעותית על עיצוב דינמי בהשוואה ל-XML: { "tenant": "brand_b", "version": "2", "colors": { "primary": "#1A73E8", "primary_variant": "#1557B0", "secondary": "#FB8C00", "background": "#FFFFFF", "surface": "#F5F5F5", "error": "#B00020", "on_primary": "#FFFFFF", "on_secondary": "#000000" }, "typography": { "font_family": "Inter", "scale_factor": 1.0 }, "shape": { "card_corner_radius": 12, "button_corner_radius": 8, "input_corner_radius": 4 }, "assets": { "logo_url": "https://cdn.brand-b.com/logo.png", "splash_bg_color": "#1A73E8" } } מקבל MaterialTheme ו-ColorScheme כפרמטרים ומחיל אותם על כל עץ הרכיבים.
// Загрузка и парсинг темы в Compose
@Composable
fun TenantThemedApp(theme: TenantTheme, content: @Composable () -> Unit) {
val colorScheme = lightColorScheme(
primary = Color(android.graphics.Color.parseColor(theme.colors.primary)),
primaryContainer = Color(android.graphics.Color.parseColor(theme.colors.primaryVariant)),
secondary = Color(android.graphics.Color.parseColor(theme.colors.secondary)),
background = Color(android.graphics.Color.parseColor(theme.colors.background)),
surface = Color(android.graphics.Color.parseColor(theme.colors.surface)),
error = Color(android.graphics.Color.parseColor(theme.colors.error))
)
val shapes = Shapes(
small = RoundedCornerShape(theme.shapes.inputCornerRadius.dp),
medium = RoundedCornerShape(theme.shapes.cardCornerRadius.dp),
large = RoundedCornerShape(theme.shapes.buttonCornerRadius.dp)
)
MaterialTheme(colorScheme = colorScheme, shapes = shapes, content = content)
}
// Использование в Activity
setContent {
val theme by themeViewModel.tenantTheme.collectAsState()
TenantThemedApp(theme = theme) {
AppNavHost()
}
}כאשר Typography משתנה ב-ViewModel, כל הממשק מצויר מחדש אוטומטית. טעינת גופנים בזמן ריצה מתבצעת באמצעות Downloadable Fonts API; גופנים נשמרים במטמון לאחר הטעינה הראשונה, מה שמבטל עיכובי הפעלה. בפועל, החלפת ערכת נושא אורכת פחות משתי שניות, בעוד ששחרור סטטי אורך 24 שעות.
Flutter ו-React Native
Flutter: // Загрузка и парсинг темы в Compose @Composable fun TenantThemedApp(theme: TenantTheme, content: @Composable () -> Unit) { val colorScheme = lightColorScheme( primary = Color(android.graphics.Color.parseColor(theme.colors.primary)), primaryContainer = Color(android.graphics.Color.parseColor(theme.colors.primaryVariant)), secondary = Color(android.graphics.Color.parseColor(theme.colors.secondary)), background = Color(android.graphics.Color.parseColor(theme.colors.background)), surface = Color(android.graphics.Color.parseColor(theme.colors.surface)), error = Color(android.graphics.Color.parseColor(theme.colors.error)) ) val shapes = Shapes( small = RoundedCornerShape(theme.shapes.inputCornerRadius.dp), medium = RoundedCornerShape(theme.shapes.cardCornerRadius.dp), large = RoundedCornerShape(theme.shapes.buttonCornerRadius.dp) ) MaterialTheme(colorScheme = colorScheme, shapes = shapes, content = content) } // Использование в Activity setContent { val theme by themeViewModel.tenantTheme.collectAsState() TenantThemedApp(theme = theme) { AppNavHost() } } ב-tenantTheme מוגדר עם פרמטרים בדומה ל-Compose. לשליטה מלאה, השתמשו ב-ThemeData או ב-Riverpod provider עם אובייקט הערכת הנושא. טעינת גופנים בזמן ריצה באמצעות MaterialApp API.
React Native: InheritedWidget דרך React Context API; FontLoader נקרא עם אסימונים מהקונטקסט. טעינה חוזרת חמה של ערכת הנושא ללא הפעלה מחדש מושגת באמצעות ThemeContext ברכיבים.
מדוע דפוס Stale-While-Revalidate חשוב לטעינת ערכת נושא?
Stale-while-revalidate (מתועד בתיעוד MDN) הוא אסטרטגיית מטמון המבטיחה עדכניות נתונים ללא עיכובים. הצג את ערכת הנושא השמורה מיד, עדכן ברקע. זה מבטל המתנה בעת ההפעלה ומונע ממשק שבור בשגיאות רשת.
class ThemeStore: ObservableObject {
@Published var currentTheme: TenantTheme = .default
func loadTheme(tenantId: String) async {
do {
if let cached = ThemeCache.load(tenantId: tenantId) {
await MainActor.run {
currentTheme = cached
}
}
let dto = try await api.fetchTheme(tenantId: tenantId)
let theme = TenantTheme(from: dto)
ThemeCache.save(theme, tenantId: tenantId)
await MainActor.run {
currentTheme = theme
}
} catch {
// Fallback на дефолтную тему, не крэшимся
}
}
}טעינה סטנדרטית ללא דפוס זה הייתה גורמת למסך ריק למשך 200–300 אלפיות שנייה. השוואה: ערכת נושא סטטית אורכת 24 שעות לשחרור, דינמית מתחלפת בשתי שניות — הפרש של פי 43,200.
כיצד גרסת ערכת נושא מונעת ממשק שבור?
כאשר החוזה מתעדכן (נוסף אסימון חדש), ערכות נושא ישנות שמורות עשויות להחסיר שדה זה. אם גרסת ערכת הנושא נמוכה מהגרסה המינימלית הנתמכת, אנו משתמשים בערכת נושא ברירת מחדל מובנית. זה מגן מפני הצגת ממשק שבור.
תהליך העבודה
- ביקורת ממשק: מלאי את כל הצבעים, הגופנים והרדיוסים באפליקציה, זיהוי ערכים מקודדים.
- עיצוב סכמת אסימונים יחד עם המעצב: אילו פרמטרים שונים בין מותגים.
- הטמעת ThemeProvider, יישום מבוסס סביבה, טעינה מה-API.
- ריפקטורינג לרכיבים: החלפת ערכים מקודדים באסימונים, כיסוי בבדיקות חזותיות (Paparazzi לאנדרואיד, SwiftUI Previews ל-iOS).
- בדיקת החלפת ערכת נושא בזמן ריצה: כל הרכיבים מצוירים מחדש כראוי, גופנים נטענים ללא הבהובים.
הערכות זמנים
מנוע עיצוב לאפליקציה חדשה מאפס (Compose או SwiftUI) — 2–3 שבועות. ריפקטורינג לאפליקציה קיימת עם צבעים מקודדים — תלוי בגודל הקוד, בדרך כלל 3–6 שבועות. העלות מחושבת באופן פרטני.
רשימת בדיקה להטמעת מנוע עיצוב
- ביקורת ממשק: מלאי את כל הצבעים, הגופנים והרדיוסים
- סכמת אסימוני עיצוב עם המעצב
- הטמעת ThemeProvider ומטעין ערכת נושא
- ריפקטורינג לרכיבים: החלפת ערכים מקודדים באסימונים
- הגדרת מטמון וטיפול בשגיאות
- בדיקת החלפת ערכת נושא: כל הרכיבים מצוירים מחדש כראוי
- תיעוד הוספת דיירים חדשים
מוכנים להטמיע מנוע עיצוב באפליקציית ה-white-label שלכם? קבלו ייעוץ מומחה — ננתח את הממשק שלכם ונציע את הפתרון האופטימלי.







