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

MaterialSplitButton can only hold MaterialButtons.

Error message

MaterialSplitButton can only hold MaterialButtons.

What it means

MaterialSplitButton is a two-button container (leading action + trailing 'add' chevron) whose implementation depends on every child being a MaterialButton: it casts children to MaterialButton in addView to set checkability, accessibility class name, shape and margins (MaterialSplitButton.java:107). addView therefore throws IllegalArgumentException immediately when any non-MaterialButton view is added, including generic Button subclasses that are not MaterialButtons.

Source

Thrown at lib/java/com/google/android/material/button/MaterialSplitButton.java:99

  public MaterialSplitButton(@NonNull Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, R.attr.materialSplitButtonStyle);
  }

  public MaterialSplitButton(
      @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);
  }

  /**
   * This override prohibits Views other than {@link MaterialButton} to be added where the leading
   * {@link MaterialButton} has either an icon and/or a label and the trailing {@link
   * MaterialButton} has an animated vector drawable as an icon. It also makes updates to the add
   * button shape and margins.
   */
  @Override
  public void addView(@NonNull View child, int index, @Nullable ViewGroup.LayoutParams params) {
    if (!(child instanceof MaterialButton)) {
      throw new IllegalArgumentException("MaterialSplitButton can only hold MaterialButtons.");
    }
    if (getChildCount() > REQUIRED_BUTTON_COUNT) {
      throw new IllegalArgumentException("MaterialSplitButton can only hold two MaterialButtons.");
    }

    MaterialButton buttonChild = (MaterialButton) child;
    super.addView(child, index, params);
    if (indexOfChild(child) == 1) {
      buttonChild.setCheckable(true);
      buttonChild.setA11yClassName(Button.class.getName());
      // Set initial content description based on checked state when focused.
      ViewCompat.setStateDescription(
          buttonChild,
          getResources()
              .getString(
                  buttonChild.isChecked()
                      ? R.string.mtrl_button_expanded_content_description
                      : R.string.mtrl_button_collapsed_content_description));

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Use fully-qualified <com.google.android.material.button.MaterialButton> elements as the only children of MaterialSplitButton.
  2. If you added views in code, replace addView(new X(...)) with addView(new MaterialButton(context, null, attr)) or inflate a MaterialButton.
  3. Do not put intermediate containers (LinearLayout, Space) inside the split button — it accepts exactly two MaterialButtons, nothing else.

Example fix

<!-- before -->
<com.google.android.material.button.MaterialSplitButton ...>
  <Button android:text="Save" ... />
</com.google.android.material.button.MaterialSplitButton>

<!-- after -->
<com.google.android.material.button.MaterialSplitButton ...>
  <com.google.android.material.button.MaterialButton
      android:text="Save" ... />
  <com.google.android.material.button.MaterialButton
      app:icon="@drawable/ic_chevron" ... />
</com.google.android.material.button.MaterialSplitButton>
Defensive patterns

Strategy: type-guard

Validate before calling

if (child instanceof MaterialButton) {
  splitButton.addView(child);
}

Type guard

static boolean isValidSplitChild(View v) {
  return v instanceof MaterialButton;
}

Try / catch

try { splitButton.addView(view); } catch (IllegalArgumentException e) { throw new IllegalStateException("MaterialSplitButton children must be MaterialButton", e); }

Prevention

When it happens

Trigger: Inflating or programmatically adding a plain Button, ImageButton, TextView or any custom View into a MaterialSplitButton — e.g. <Button> children in XML, or splitButton.addView(new ImageView(context)). Even AppCompatButton is rejected because the instanceof check is against MaterialButton.

Common situations: Writing the split button in XML and using the platform <Button> tag out of habit (the same mistake people make inside MaterialButtonToggleGroup). XML inflation calls addView for each child, so the crash occurs at setContentView/inflate time with a stack trace rooted in LayoutInflater.

Related errors


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