Adopting MDC and best theming practices on Simple Android app

8 minute read
Simple 3 Column

Even though Material design, Google's one-all be-all system was introduced back in 2014, Google made quite a few changes to it with the introduction of Material Design Component. These changes included updates made to typography, component styles and support for newer components. For example, the new MDC changed the original type system.

Simple blog Asset 1
Simple blog Asset 2

Back in 2018, when the Simple app was being designed for the Android platform, the team initially adopted the original Material design system guidelines. But with the introduction of MDC, we began to gradually transition to the new MDC guidelines as well, to make use of the newer components that came with it.

As we started using the new MDC designs in the app, which still was based on the original Android Design Support library, we noticed certain inconsistencies from the designs to the actual implementation. While most of these inconsistencies could be addressed by changing some attributes in the components, it was not an ideal solution.

For example, in order to support the center icon button, that was a part of the MaterialButton from MDC, we had to create a custom button to support it. This kind of overhead to add a single button slowed down the development time considerably. And given that Google had already decided to move away from the support library, it meant we would keep piling on the technical debt while playing catch-up. We knew it was time to move on to better things!

public class CenterIconButton extends AppCompatButton {
	/* implementation */
}

To make our migration to MDC even more worthwhile for Simple, we decided to refactor along the way and adopt the best practices in setting up themes and styles in Android. In the long run, this helped us resolve some of the prominent issues in our existing code like discoverability and eased onboarding of new engineers.

1. Migrating to a Bridge theme

Android Design Support Library → MDC 1.0.0 (Bridge theme)

Simple is an app that is regularly used by healthcare workers for critical patient care. We are always making improvements to make the lives of our users easier. For that reason, we never hold back any releases while being cautious about any change that might affect patient care.

With that in mind, the migration had to be an incremental process where the team regularly evaluated their final approach to make necessary adjustments before releasing.

The first step was to allow the team to use the Material Design Components wherever needed, and with as little change to the existing theme as possible. As shown below, we added the Material Theme and adapted it to the app's existing style structure to achieve this. We called this the bridge theme, which eventually helped us walk from the support library to the final Material Theming. The PR for this migration can be found here.

What's exciting is also that we reached out to Nick Rout, a developer relations engineer at Google, who kindly reviewed our plan for migration and adoption of the best practices.

<?xml version="1.0" encoding="utf-8"?>
<resources>

  <style name="Clinic.V2" />

  <style name="Clinic.V2.Theme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
    <item name="colorPrimary">@color/color_primary</item>
    <item name="colorPrimaryDark">@color/color_primary_dark</item>
    <item name="colorAccent">@color/color_accent</item>
    <item name="colorError">@color/color_error</item>
    <item name="toolbarStyle">@style/Clinic.V2.ToolbarStyle</item>
    <item name="alertDialogTheme">@style/Clinic.V2.DialogStyle</item>
    <item name="android:windowBackground">@color/window_background</item>

    <!-- New MaterialComponents Attributes -->
    <item name="colorPrimaryVariant">@color/color_primary_dark</item>
    <item name="scrimBackground">@color/scrim_color</item>
    <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>
    <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>
    <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>
    <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>
    <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>
    <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>
    <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
    <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>
    <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>
    <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
    <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
    <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>
    <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>
  </style>
</resources>

2. Always be refactoring: Best Practices

Once the bridge theme was set up, the next step was to plan out how the theming structure should look like and then implement those changes. It should be noted that we wanted to simplify the theming structure to ensure we follow some of the best practices for Android themes and styles.

  • Better naming practices: Using dot notation for naming made it really easy for navigation and to find the themes/styles.

For example, if we wanted to have a theme for an activity based on our app them, previously we would have done something like this:

<style name="NewActivityTheme" parent="Theme.Simple">

But we wanted to avoid this approach and go with dot notation so that we could start extending the styles and themes:

<style name="Theme.Simple.NewActivity">

This made it easier to navigate through the base style/theme that the team was extending and it was also effortless to group component styles to their specific group, such as:

<style name="Widget.Simple.TextField.PinEntry">
<style name="Widget.Simple.TextField.PinEntry.Otp">
<style name="Widget.Simple.TextField.Large.MeasurementInput">

These changes allowed us to group variants based on family, and even reduce the number of styles we have in the project and simpler to search. Currently, we have these groups:

<!-- for app themes -->
<style name="Theme.Simple.*">
<!-- for theme overlays-->
<style name="ThemeOverlay.Simple.*">
<!-- for styles-->
<style name="Widget.Simple.*">

  • Modularised Theming: We wanted to start using theme attributes instead of direct color references for most of the widely used components. We can now use theme overlays to change color schemes, other than primary, for different parts of the app. This makes our theming a lot more modular. We can also switch the brand colors overnight if we want to in the future, just by updating the colors in the app theme (Theme.Simple ).

For example, all toolbars pick colorPrimary as a background with colorOnPrimary text color. But in some cases, we have a toolbar with a different color than colorPrimary, as shown below:

Simple&#x20;blog&#x20;Asset&#x20;3

For this, we used theme overlay as shown below:

<!-- colorSurface and colorOnSurface from main theme -->
 <style name="ThemeOverlay.Simple.SurfacePrimary" parent="">
   <item name="colorPrimary">?attr/colorSurface</item>
   <item name="colorOnPrimary">?attr/colorOnSurface</item>
 </style>

We can then simply apply the new theme to AppBarLayout() and it will pick the colors from the theme overlay instead of the app theme:

<!-- Appbar style is applied using default style -->
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.Simple.SurfacePrimary" />

This particular practice allowed us to remove styles that were overriding all the theme colors, and use theme overlays instead. We even use it now for changing attributes specific to a widget or a screen:

<!-- Widget specific theme overlays -->
<style name="ThemeOverlay.Simple.TextField" parent="">
<!-- ... -->
</style>
<style name="ThemeOverlay.Simple.TabLayout" parent="">
<!-- ... -->
</style>
<!-- Screen specific theme overlays -->
<style name="ThemeOverlay.Simple.SplashScreen" parent="">
<!-- ... -->
</style>
<style name="ThemeOverlay.Simple.OnboardingScreen" parent="">
<!-- ... -->
</style>

Improved design hand-off: The engineers and designers collaborated to work towards a better approach for the design hand-off process. Some of the key changes we made were in organising the Figma file. Previously, the colors were defined as blue1, blue2, etc. in Figma and the same naming convention was followed in the Android XML as well. We changed the conventions to make them consistent with the Android theme attributes too and followed a similar approach for the text appearances as well.

Simple&#x20;blog&#x20;Asset&#x20;4

Setting up these layers like typography, colors and components helped share a common vocabulary between the designers and engineers.

  • Automated code standardization: As a final step, we set up lint rules to make it easier for the team to follow these best practices and not rely on one person to find and resolve these issues. Currently, only a few lint rules have been set up to ensure the usage of text appearance theme attributes, correct components, and tags. But as next steps, the team will continue to add more and more lint rules to ensure a proper design system implementation.

3. Final step: Material Theming

Material Theming refers to the customization of your Material Design app to better reflect your product’s brand.

Material Theming consists of three main actions that would help us set up a robust theming structure with default style for MDC and other platform components.

  1. Customizing the theme: As part of re-organising the Figma file, we extracted top-level aspects like colors, types, and shapes to a single place
  2. Applying theme across the design mocks: We then used the top-level attributes across the screens and designs
  3. Using in code: We set up our theme to support the top-level foundational aspects of color, type & shape.

Our final theme looks like this right now, and gives us consistent components every time, no matter who creates them:

Base theme with all the default styles for MDC and platform components, text appearances & shape appearances:

<style name="Base.Theme.Simple" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- Dialog Themes -->
    <item name="alertDialogTheme">@style/ThemeOverlay.Simple.Dialog.Alert</item>
    <item name="android:datePickerDialogTheme">@style/ThemeOverlay.Simple.Dialog</item>
    <item name="materialAlertDialogTheme">@style/ThemeOverlay.Simple.MaterialAlertDialog</item>
    <item name="bottomSheetDialogTheme">@style/ThemeOverlay.Simple.BottomSheetDialog</item>

    <!-- Framework, AppCompat, Design or Material Widget Styles -->
    <item name="appBarLayoutStyle">@style/Widget.Simple.AppBarLayout</item>
    <item name="borderlessButtonStyle">@style/Widget.Simple.Button.TextButton</item>
    <item name="checkboxStyle">@style/Widget.Simple.CompoundButton.CheckBox</item>
    <item name="materialButtonStyle">@style/Widget.Simple.Button</item>
    <item name="materialButtonOutlinedStyle">@style/Widget.Simple.Button.OutlinedButton</item>
    <item name="materialCardViewStyle">@style/Widget.Simple.CardView</item>
    <item name="radioButtonStyle">@style/Widget.Simple.CompoundButton.RadioButton</item>
    <item name="switchStyle">@style/Widget.Simple.CompoundButton.Switch</item>
    <item name="tabStyle">@style/Widget.Simple.TabLayout</item>
    <item name="textInputStyle">@style/Widget.Simple.TextField.Layout</item>
    <item name="toolbarStyle">@style/Widget.Simple.Toolbar</item>

    <!-- Text Appearances -->
    <item name="textAppearanceHeadline3">@style/TextAppearance.Simple.Headline3</item>
    <item name="textAppearanceHeadline4">@style/TextAppearance.Simple.Headline4</item>
    <item name="textAppearanceHeadline5">@style/TextAppearance.Simple.Headline5</item>
    <item name="textAppearanceHeadline5Numeric">@style/TextAppearance.Simple.Headline5.Numeric</item>
    <item name="textAppearanceHeadline6">@style/TextAppearance.Simple.Headline6</item>
    <item name="textAppearanceHeadline6Numeric">@style/TextAppearance.Simple.Headline6.Numeric</item>
    <item name="textAppearanceSubtitle1">@style/TextAppearance.Simple.Subtitle1</item>
    <item name="textAppearanceSubtitle1Medium">@style/TextAppearance.Simple.Subtitle1.Medium</item>
    <item name="textAppearanceSubtitle2">@style/TextAppearance.Simple.Subtitle2</item>
    <item name="textAppearanceBody0">@style/TextAppearance.Simple.Body0</item>
    <item name="textAppearanceBody0Medium">@style/TextAppearance.Simple.Body0.Medium</item>
    <item name="textAppearanceBody0Numeric">@style/TextAppearance.Simple.Body0.Numeric</item>
    <item name="textAppearanceBody1">@style/TextAppearance.Simple.Body1</item>
    <item name="textAppearanceBody1Numeric">@style/TextAppearance.Simple.Body1.Numeric</item>
    <item name="textAppearanceBody2">@style/TextAppearance.Simple.Body2</item>
    <item name="textAppearanceBody2Numeric">@style/TextAppearance.Simple.Body2.Numeric</item>
    <item name="textAppearanceBody2Bold">@style/TextAppearance.Simple.Body2.Bold</item>
    <item name="textAppearanceButton">@style/TextAppearance.Simple.Button</item>
    <item name="textAppearanceButtonBig">@style/TextAppearance.Simple.Button.Big</item>
    <item name="textAppearanceTag">@style/TextAppearance.Simple.Tag</item>
    <item name="textAppearanceCaption">@style/TextAppearance.Simple.Caption</item>

    <!-- Shape Appearances -->
    <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.Simple.SmallComponent</item>
    <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.Simple.MediumComponent</item>
    <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.Simple.LargeComponent</item>
  </style>

App theme with app colors and custom attributes:

<style name="Theme.Simple" parent="Base.Theme.Simple">
    <item name="colorPrimary">@color/simple_light_blue_500</item>
    <item name="colorPrimaryVariant">@color/simple_light_blue_100</item>
    <item name="colorOnPrimary">@color/white</item>

    <item name="colorSecondary">@color/simple_green_500</item>
    <item name="colorSecondaryVariant">@color/simple_green_100</item>

    <item name="colorSurface">@color/white</item>
    <item name="colorOnSurface">@color/simple_dark_grey</item>

    <item name="colorError">@color/simple_red_500</item>
    <item name="colorOnError">@color/white</item>

    <item name="scrimBackground">@color/scrim_color</item>
    <item name="colorOnBackground">@color/simple_dark_grey</item>

    <item name="android:statusBarColor">@color/simple_dark_blue_900</item>
    <item name="android:colorBackground">@color/simple_light_grey</item>
    <item name="android:windowBackground">@color/simple_light_grey</item>
    <item name="android:backgroundDimAmount">0.32</item>
    <item name="android:listDivider">@drawable/divider</item>

    <!-- Custom Attrs -->
    <item name="colorToolbarPrimary">@color/simple_dark_blue_800</item>
    <item name="colorToolbarPrimaryVariant">@color/simple_dark_blue_900</item>
    <item name="colorOnToolbarPrimary">@color/white</item>
  </style>

What changed as a result of this migration?

Migrating the Simple app to Material Theming helped the team save time in building a consistent user experience for the Simple app. The best practices that we established along the way helped improve how the team now collaborates and how engineers consume every design. Simplifying the styles helped us achieve the design standards we aim and strive for for as a team.

What lies in the future for us are the possibilities to explore Simple's own design system, and we're even looking at doing so with Jetpack Compose.


More resources: