Building DotShredzilla (Part 2): A Glass Design System with Two Palettes
In Part 1 I cleaned up DotShredzilla's sign-in flow. The app worked, but every screen still looked like it came from a different project: one used gradients, another plain Material cards, a third its own hard-coded colors. For version 1.4.0 I redesigned the whole app, page by page, with two rules: It should feel premium, with a translucent "glass" look in light and dark mode. No screen gets its own private copy of a component. If two screens need a card, they use the same card. DotShredzilla is an Android workout tracker from FreeRave (DotSuite), written in Kotlin with Jetpack Compose and Firebase. The source is available under Apache License 2.0: github.com/kareem2099/Shredzilla. One palette object instead of scattered colors The app asks for a profile during onboarding, and I wanted the interface to feel different for male and female profiles. Combined with light and dark mode, that is six looks, plus a neutral "brand" palette before a profile exists. Instead of branching on gender inside screens, every color decision lives in one data class: data class GlassPalette( val isDark: Boolean, val backgroundTop: Color, val backgroundBottom: Color, val orbPrimary: Color, val orbSecondary: Color, val accent: Color, val accentGradient: List, val onAccent: Color, val fill: Color, val border: Color, val content: Color, val contentMuted: Color, val error: Color, ) The theme provides the right palette through a CompositionLocal, and screens read it with MaterialTheme.glass. Switching profile or dark mode swaps one object, and every screen follows. The palette change is animated, so picking a profile during onboarding fades the whole app into its new colors instead of snapping. A small kit of shared components Once colors were centralized, the components followed: GlassBackground: the gradient backdrop with two slowly drifting "orbs", drawn once at the app root so switching tabs never flashes a new background. glassSurface(): a modifier for the frosted fill and hairline border, used by cards, buttons, chips and the bottom bar. GlassCard, GlassPrimaryButton, GlassTopBar, GlassDialog, GlassBottomSheet, GlassSettingsRow and a few selection controls. Every redesigned screen is built from these. When I later changed the corner radius or the border alpha, it changed everywhere at once, and the screens kept only their own state and layout. Two details took a few attempts: Translucent dialogs leak. A frosted dialog over a busy screen let the text underneath show through. Dialogs and banners now draw an opaque gradient first and the glass effect on top. Swipe-to-delete left ghosts. The old list removed a row the moment you swiped. If you then cancelled the confirmation, the row was gone from the screen but not from the data. Now the swipe only asks to delete. The row springs back, and deletion happens only after confirmation. Onboarding, rebuilt from scratch The original onboarding was five plain forms. I replaced all of them: Profile, with a live preview: the screen recolors as you choose, so you see the palette before committing. Body details, using ruler pickers for age, height and weight instead of text fields. Rest time between sets, with sensible presets. Starting exercises, so the library isn't empty on day one. Weekly goal: how many days a week you plan to train. Each step saves as you go. If someone closes the app halfway through, the next launch resumes at the first unfinished step instead of starting over. That turned up a crash: starting a nested navigation graph directly at a deep route isn't allowed, so the onboarding graph now receives the resumed step as its own start destination. Edge-to-edge, insets and the keyboard Going edge-to-edge made the glass backgrounds run behind the status and navigation bars, which is what makes the look work. It also exposed every inset bug in the app: Double padding at the top. The outer Scaffold was adding status-bar padding, and each screen's top bar added it again. The fix was contentWindowInsets = WindowInsets(0.dp) on the container, letting each top bar own its inset. The keyboard covered errors. On the register screen, the "passwords don't match" message appeared behind the keyboard. The activity now uses adjustResize, and fields with an error bring themselves into view. Status bar icons. With transparent bars, only the icon color is left to decide, and it now follows the app's theme rather than the system's. Navigation that matches modern apps The old app hid Settings behind a menu. The new bottom bar is a floating pill with Sets, Today and Settings, like Telegram or Reddit. Only those three tab roots show the bar; every pushed page (an exercise, a list, a settings sub-page) gets the full screen and a back button in its top bar. Settings itself became grouped glass rows (Account, Units, Rest time, Reminders, Theme), and each sub-page uses one shared GlassSubpageScaffold. What I'd keep doing Put every color in one palette object, even when there are only two themes at the start. Write the shared component the second time you need it, not the fifth. Test edge-to-edge with the keyboard open and with a long error message, not just on a clean screen. The next part moves from looks to the gym floor: what happened when I actually trained with the app and found it needed offline logging, plates and one-sided sets. What's the first thing you notice in a fitness app's design: the colors, the speed of logging a set, or the charts? Google Play: DotShredzilla Source: github.com/kareem2099/Shredzilla Project: DotSuite
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to