material-components/material-components-android · error · IllegalArgumentException

iconGravity cannot have the same alignment as…

Error message

iconGravity cannot have the same alignment as secondaryIconGravity

What it means

MaterialButton supports an optional secondary icon (e.g. for dropdown/chevron usage). The primary iconGravity and secondaryIconGravity must not resolve to the same alignment (start/end/top) when both icons are set, otherwise the two drawables would be stacked in the same compound-drawable slot. validateIconGravity enforces this with IllegalArgumentException whenever both icons exist and areIconsGravitySameAlignment() is true.

Solutions

  1. Give the two gravities different alignments, e.g. iconGravity="start" with secondaryIconGravity="end" (the typical icon + chevron pattern).
  2. If only one icon is needed, remove the secondary icon (unset app:secondaryIcon / secondaryIcon null) so validation passes regardless of gravity values.
  3. When setting gravities programmatically, keep a mapping/constant pair that is guaranteed distinct.

Example fix

<!-- before -->
<com.google.android.material.button.MaterialButton
    app:icon="@drawable/ic_send"
    app:iconGravity="start"
    app:secondaryIcon="@drawable/ic_chevron_down"
    app:secondaryIconGravity="start" ... /> <!-- throws -->

<!-- after -->
<com.google.android.material.button.MaterialButton
    app:icon="@drawable/ic_send"
    app:iconGravity="start"
    app:secondaryIcon="@drawable/ic_chevron_down"
    app:secondaryIconGravity="end" ... />
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the gravities differ whenever both icons are present
if (icon != null && secondaryIcon != null) {
  android.util.Log.d(TAG, "primary=" + iconGravity + " secondary=" + secondaryIconGravity);
  // require: not (start&start), (end&end), or (top&top)
}

Type guard

private static boolean gravitiesConflict(int primaryGravity, int secondaryGravity) {
  return primaryGravity == secondaryGravity; // same start/end/top alignment
}
// usage: if (!gravitiesConflict(iconGravity, secondaryIconGravity)) button.setSecondaryIcon(chevron);

Prevention

When it happens

Trigger: Setting both icon and secondaryIcon on the same MaterialButton with app:iconGravity and app:secondaryIconGravity both resolving to start, end, or top; changing gravity attributes in XML or calling the gravity setters so both align identically.

Common situations: Adding a secondary icon (e.g. a chevron) to an existing button that already has iconGravity="start" and copying the same value for secondaryIconGravity; configuring gravity from a shared style/theme so both attributes inherit the same alignment; programmatically toggling gravities at runtime without keeping them distinct.

Related errors


AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14). Data as JSON: /api/errors/bcc4466155a63fcb. Report an issue: GitHub.

Appendix: source

Thrown at lib/java/com/google/android/material/button/MaterialButton.java:1258

    boolean hasIconChanged =
        (isIconStart() && drawableStart != icon)
            || (isIconEnd() && drawableEnd != icon)
            || (isIconTop() && drawableTop != icon);
    // Force icon update if needsIconReset otherwise updated it only if icon has changed.
    if (needsIconReset || hasIconChanged) {
      if (isIconStart()) {
        setCompoundDrawablesRelative(icon, getUpdatedIconFor(1), getUpdatedIconFor(2), null);
      } else if (isIconEnd()) {
        setCompoundDrawablesRelative(getUpdatedIconFor(0), getUpdatedIconFor(1), icon, null);
      } else if (isIconTop()) {
        setCompoundDrawablesRelative(getUpdatedIconFor(0), icon, getUpdatedIconFor(2), null);
      }
    }
  }

  private void validateIconGravity() {
    if (icon != null && secondaryIcon != null && areIconsGravitySameAlignment()) {
      throw new IllegalArgumentException(
          "iconGravity cannot have the same alignment as secondaryIconGravity");
    }
  }

  private boolean areIconsGravitySameAlignment() {
    return (isIconStart() && isSecondaryIconStart())
        || (isIconEnd() && isSecondaryIconEnd())
        || (isIconTop() && isSecondaryIconTop());
  }

  @Nullable
  private Drawable getUpdatedIconFor(int position) {
    switch (position) {
      case 0: // start
        return (secondaryIcon != null && isSecondaryIconStart()) ? secondaryIcon : null;
      case 1: // top
        return (secondaryIcon != null && isSecondaryIconTop()) ? secondaryIcon : null;
      case 2: // end

View on GitHub (pinned to ac7e18efee)