material-components-android: [Snackbar] Setting LayoutParams for SnackbarLayout doesn't work

Description:

There is no effect when trying to set LayoutParams for the SnackbarLayout (i.e. adding some margins). I tried to add margins to the Snackbar because I wanted it not to stick to the bottom edges of the screen. Instead, I wanted it to float just like shown in the Material design guidelines.

This bug does not occur on Library version 1.0.0. I’ve reproduced this issue with version 1.1.0 and also with the latest alpha (1.2.0-alpha05).

Expected behavior:

expected

Actual behavior:

actual

Source code:

Snackbar snackbar = Snackbar.make(getView(), "Test", Snackbar.LENGTH_LONG);
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbar.getView();
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) 
snackbarLayout.getLayoutParams();
layoutParams.setMargins(32, 0, 32, 32);
snackbarLayout.setLayoutParams(layoutParams);
snackbar.show();

Android API version: 29

Material Library version: 1.1.0 and 1.2.0-alpha05

Device: Samsung Galaxy S10 and Android Emulator (Pixel 2 API 29)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 18
  • Comments: 23 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Here is a way to achieve this using styles (for example, just a custom bottom margin):

<style name="Theme.App" parent="Theme.MaterialComponents.Light.NoActionBar">
    ...
    <item name="snackbarStyle">@style/Widget.App.Snackbar</item>
</style>

<style name="Widget.App.Snackbar" parent="Widget.MaterialComponents.Snackbar">
    <item name="android:layout_margin">@null</item>
    <!-- Use default Snackbar margins for top/left/right -->
    <item name="android:layout_marginTop">@dimen/mtrl_snackbar_margin</item>
    <item name="android:layout_marginLeft">@dimen/mtrl_snackbar_margin</item>
    <item name="android:layout_marginRight">@dimen/mtrl_snackbar_margin</item>
    <!-- Custom bottom margin, this could work for top/left/right too -->
    <item name="android:layout_marginBottom">100dp</item>
</style>

I temporally solved this problem by code below. Reflact field originalMargins in BaseTransientBottomBar, and set the bottom margin into this field. When it call BaseTransientBottomBar#updateMargins(), the real layout will be updated. It works fine on both 1.1.0 & 1.2.0 release version.

    private boolean fixSnackBarMarginBottomBug(Object snackBar, int height) {
        try {
            Class snackbarClass = Class.forName("com.google.android.material.snackbar.Snackbar");
            Field originalMarginsField = snackbarClass.getSuperclass().getDeclaredField("originalMargins");
            originalMarginsField.setAccessible(true);
            Rect fixedOriginalMargins = new Rect();
            fixedOriginalMargins.bottom = height;
            originalMarginsField.set(snackBar, fixedOriginalMargins);
            return true;
        } catch (IllegalAccessException | NoSuchFieldException | ClassNotFoundException e) {
            LogUtils.d(TAG, "fixSnackBarMarginBottomBug error.");
        }
        return false;
    }

Edited: Can confirm this issue. The last version where the margin behavior was working properly was with version 1.1.0-alpha10 1.1.0-beta01. Margin Issue first started showing up on 1.1.0-beta02.

Scanning through the commits on 1.1.0-beta02, this commit on 75e20b78fadf1f89d43ee71d9365e12d4d8f8213 seems to make changes to margin calculations changes.

Adding CoordinatorLayout or Frame Layout and then setting margin didn’t work for me

To tackle this problem use Drawable Background where use item to set Margin and shape to set desired Padding

container_snackbar.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--    Set Margin Here -->
    <item
        android:left="20dp"
        android:right="20dp"
        android:bottom="10dp"
        android:top="10dp">

        <!-- Insert your shape here: -->
        <shape android:shape="rectangle"  >
            <solid android:color="#FE4C4C" />

            <!-- Padding for inner text-->
            <padding android:left="25dp" android:right="10dp" android:bottom="10dp" android:top="10dp" />
            <corners android:radius="5dp" />

        </shape>
    </item>
</layer-list>

And then from Activity set that Drawable

MainActivity.java

Snackbar snack = Snackbar
                 .make(activity,"Hello World 🚀",Snackbar.LENGTH_INDEFINITE);
        snack.getView()
        .setBackground(ContextCompat.getDrawable(getApplicationContext(),R.drawable.contianer_snackbar));
        snack.show();

Result

@sylviestephanies @ssawchenko I resolver this issue just by adding this <style name="Theme.App" parent="Theme.MaterialComponents.Light.NoActionBar">

before i was using this <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

The issue should be fixed in 1.6.0 alpha03 already. Please let me know if this is still happening. : )

Last post solution seems to work thanks. But “snackbarStyle” will apply for every SnackBar in app. What if we need to have different snackbars with different margin?