Skip to content

Instantly share code, notes, and snippets.

@JorgeCastilloPrz
Last active December 11, 2018 14:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JorgeCastilloPrz/d3e7facd1c639c50da27ea9aa4f58c21 to your computer and use it in GitHub Desktop.
Save JorgeCastilloPrz/d3e7facd1c639c50da27ea9aa4f58c21 to your computer and use it in GitHub Desktop.
blogpost draft

Exploring Material Components

Initial thoughts

Not long ago a new version of the Material Design Guidelines was published by Google, and that came along with enhanced support for integration of the principles to the community. This included other interesting resources like some fancy sample projects mimicking real world apps called Material Studies, a brand new Sketch plugin for generating Material themes and styles, and a new Material Components library that intents to replace the old Design Support Library, among other initiatives.

As you probably have heard of, in terms of Android, Android Support Library 28 will be the last version deployed ever, and we'll move directly into the AndroidX APIs (JetPack) once for all.

On 47 Degrees we care about the quality of the products we work on for ourselves and our clients, so we assume it's a good time to start exploring this and get ahead of any problems.

For migrating your whole app to JetPack (androidx namespace) there are many different tutorials, starting with the official one and followed by some very interesting articles highlighting some caveats you might find along the process like this one by Mike Wolfson, and this other one by Dan Lew.

We'll probably write about that process at some point too, since we're gonna suffer that like anyone else.

Material Components

Let's focus on the new Material Design Components initiative for this post.

This is an effort by the Google team, not the Android one, since it's part of the global Material Design initiative, and there's support for different platforms.

There is a GitHub organization called @material-components, and an official website gathering all the docs about it. If you open the GitHub org you'll find out multiple implementations of the same concept for different platforms. There are repos for Android, iOS, Web, React (which is a wrapper for the web one), and Flutter (which by the way has been released some days ago).

We'll focus on the Android one, since that's the platform where we are starting to put it into practice.

What does it replace?

Material Components for Android is basically a direct replacement for the old design support library, so it provides all the components that previously belonged to the mentioned artifact (i.e: ChipView). The only difference is that those will now have a different packaging and depend on androidx APIs.

This library can also be considered a wrapper over AppCompat, so you'll also find a bunch of themes and styles into it that you can inherit from or apply to any of your AppCompat widgets.

When we say "wrapper" here we just mean it's transitively depending on AppCompat since all it's themes and styles inherit from the AppCompat ones.

Actually, you can try and totally remove the appcompat artifact dependency from your project if you want since it's transitively provided by the Material Components one. Anyways, note that it's also valid to explicitly provide it if you wanna enforce a given version of it. In case you do that, please remember to use the androidx one.

dependencies {
    implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion"
}

Note that Material Components it's not a replacement for AppCompat. AppCompat has additional responsibilities, like replacing widgets at compile time by their AppCompat equivalents. (Still, note that Material Components extends the AppCompatViewInflater which is the class in charge to do those replacements. The goal is to override some methods to provide proper Material themed component replacements (i.e: MaterialButton) for some standard widgets like Buttons, CheckBoxes and RadioButtons).

Integration

Material Components for Android was released some time ago, and there's a detailed guide on how to integrate the dependency on your project. It'll be enough to follow the steps there.

As mentioned before, Material Components library is tied to androidx. If you are not ready to switch to androidx yet, there are equivalent themes and widgets for all the Material Components ones into the design support 28.

Google intentionally deployed 1.0.0 for all their JetPack artifacts with equivalent public APIs to design support 28 so the new artifacts are essentially the same than the old ones, just with different namespaces (support vs androidx).

That will obviously not include any new widgets or styles released on newer versions of the Material Components artifact from now on, but it's a starting point for a migration. First you could upgrade your app to support 28, afterwards port everything to JetPack 1.0.0 APIs. It's also a good way to segregate your efforts and estimate properly.

Also note that compileSdkVersion requires to be 28 to use this library.

Components

The namespace is gonna be com.google.android.material for all the Material Components views. You must take care of not using any support namespaces for those. Look twice when adding a new one.

<com.google.android.material.textfield.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/textfield_label">

  <com.google.android.material.textfield.TextInputEditText
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>

For a complete list of the components available check the "components" side menu option on the official docs. You'll find there a really detailed page for each one of the components available up to date, also providing XML snippets to use them in your layouts.

All the components you'll find there (at least for 1.0.0) are the ones also provided by the design support 28. Also note that each one of the widgets there uses the com.google.android.material namespace, and the ones that don't are being replaced at compile time by their "Material" versions which also use it.

Theming

For theming, you'll be required to inherit your AppTheme from one of the MaterialComponents ones. The reasons for this are the same ones we had for inheriting from AppCompat themes until today.

There are equivalent "app-level" themes for all the AppCompat ones under the Material Components namespace, so the translation should be one to one, i.e:

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- ... -->
</style>

The themes available for this level are listed here.

You might not want to inherit from the MaterialComponent themes for some constraints on your project. For those cases, you got the Bridge themes instead. That's mostly thought for gradual migrations. You got more detailed explanations about Bridge themes plus snippets of how to use them here.

"Bridge themes inherit from AppCompat themes, but also define the new Material Components theme attributes for you."

They basically set defaults for attributes used in the MaterialComponents styles. There are equivalent Bridge themes available for each one of the MaterialComponents app level themes mentioned before.

As an ultimate fallback, and in case using Bridge themes is still a problem for you, you can keep inheriting from the AppCompat ones, and then manually add a bunch of material components attributes to the style.

After some words I personally had with the Google devs developing this library, this doesn't seem to be the ideal situation and it's not very maintainable either. The main use case for this third alternative would probably be if you're on the edge need to have full control of "what goes into your theme", instead of relying on the default Material Design attributes added by the MaterialComponents and Bridge themes.

Still, here you have the docs for that final alternative.

Also note that you always have the chance to manually apply themes per view, like:

<com.google.android.material.textfield.TextInputLayout
    style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/textfield_label">

  <com.google.android.material.textfield.TextInputEditText
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>

Material Components provides separate styles for each one of the components available in the library just in case you need them or you want to inherit from them or whatever. Note that this is not required if you're applying the suggested app level themes.

Even AppCompat ThemeOverlays have been ported over.

Since Material Components is a wrapper over AppCompat, you still require your activities to inherit from AppCompatActivity or at least use the AppCompatDelegate (in case you can't inherit) so all your widgets are themed properly using the corresponding MaterialComponents themes, which inherit from their corresponding AppCompat ones.

Note that there are not MaterialComponentsActivity or MaterialComponentsDelegate equivalents, you must keep using the AppCompat ones, at least for now. That's probably because Google folks didn't need to add any specifics on top of those.

If you don't do this, you'll neither get your standard widgets replaced by the AppCompat ones at compile time where needed and you might end up with stuff with the wrong styles and behaviors.

There's a complete guide about theming and all the new attributes available in the official docs. I highly recommend reading it.

You also have all the new styles for the components listed here most likely. There are also some styles which aren't the default ones which are listed in the package for each component, so it's hard to link the complete of them. The Google team is thinking about the possibility to add those to the docs of each component, which would be very handy, IMHO.

Material Components cheat sheet

That's probably the main question that arises when you start reading and thinking about all this. I think everything has been said already, but a cheatsheet for this would become quite handy, probably.

If your intention is to move to androidx namespace (JetPack), which you definitely should given that there's not gonna be more support libraries, you should always try to:

  • Ideally you should remove any support library dependencies from your build.gradle files. At least, you shouldn't have the design support one, as a starting point.

  • Inherit your app theme from one of the MaterialComponents ones, like Theme.MaterialComponents.Light.NoActionBar.

  • If you can't, double check if you could inherit from one of the Bridge themes.

  • If you can't either and you really need to stay inheriting from AppCompat themes while you gradually port your app, you can add all the required Material Components attributes required as specified in the Theming section. In case you forget about adding any of the required attributes, Material Components will throw an exception, so you should be safe.

  • Every time you want to add a new View to any of your layouts watch whether it is provided in the Material Components library (or in other words, it was previously provided in the design support one). If that's the case, use the Material Components equivalent. Always double check you don't have any support namespaces for those widgets. This is key so you keep the door open to remove the support design artifact if you haven't already.

  • Always use only androidx and com.google.android.material if possible. If you can't upgrade to androidx, you should use the old support lib version for everything. Never mix the two of them. You'll get exceptions thrown if you try to mix components from the two.

  • Every time you are about to use an AppCompat style (for a view or an app theme) double check there's isn't an equivalent one for Material Components. If that's the case, you should use it. Otherwise you'll get your views stylized using the old AppCompat themes.

  • Remember that AppCompat library is also going to be continued under androidx namespace, so you can keep using it. The dependency is androidx.appcompat:appcompat:version. Here's an example of it being used in the Sunflower sample app. As mentioned before, you'll get the dependency transitively by using the Material Components artifact, in case you don't want to explicitly declare the dependency.

What if I don't upgrade?

Android support libraries exist as an abstraction layer so you can support many different OS versions without changing the code base instead of relying on the version dependant features from the Android SDK. This also includes AppCompat.

If you keep targeting the old support libraries you'll lose all the new features released on JetPack as a natural continuation of those, in order to improve the mentioned abstraction layer. You'd basically be stuck on those APIs forever, or at least until you get a system update and therefore a new version of the Android SDK.

Final thoughts

I think it's more than clear now that we should start our migrations to the Material Components library sooner than later. On 47 Degrees we had some doubts about when and how to use this artifact and writing this summary was very helpful and enriching for us. Hopefully it also is for you and your team.

If you're interested on checking how the new Material Components look and behave in different scenarios, you can take a look at the Material Components Catalog App.

@cketcham
Copy link

cketcham commented Dec 6, 2018

Thanks for taking the time to summarize all this. It was interesting to see your perspective. Here's some of my comments:

Material Components for Android is basically a replacement for the old design support library.

It's actually really a direct replacement with the package rename and support for androidX

AppCompat has additional responsibilities (i.e: replacing widgets at compile time)

We also replace Button tags with MaterialButton, as well as checkbox and radio button for now (https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/theme/MaterialComponentsViewInflater.java). But the code for doing the replacement is all set up by AppCompat.

If you can't either and you really need to stay inheriting from AppCompat themes while you gradually port your app, you can add all the required Material Components attributes required as specified in the Theming section.

Maybe also mention that components will throw an exception if there are attributes that it needs that don't exist in the theme. In that case you'd just need to set that attribute to something in your theme.

Always double check you don't have any support namespaces for those widgets. This is key so you keep the door open to remove the support design artifact if you haven't already.

I think your app wouldn't compile if you try to use the old version of the library with the new version. Or it wouldn't compile if you are using the new version of the library but reference a widget with the support namespace.

@JorgeCastilloPrz
Copy link
Author

Thanks for the feedback @cketcham, it's always helpful to have the real devs behind the library giving feedback 👏.

About your last comment, couldn't you wrongly have both dependencies in your build.gradle and just use support design widgets? You mean you'd get a compile time error just by having both dependencies? I thought that was still possible, given they use totally different namespaces.

@cketcham
Copy link

cketcham commented Dec 7, 2018

Yeah you're right. I guess what I'm actually thinking of is if you try to use a class from appcompat or our library that depends on the other, you'll have a compile time error if the dependencies don't match. So you can't use androidx with the android.support.design, and you can't use the support lib from the new com.google.android.material packages. So it's best to use all androidx and com.google.android.material, or all the old support libraries. You wouldn't want to mix the two.

So in summary I think the suggestion should be to always use only androidx and com.google.android.material if you can. If you can't upgrade to androidx, you should use the old support lib version for everything.

@JorgeCastilloPrz
Copy link
Author

Makes sense. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment