Skip to content

Persistent Footer

The Persistent Footer is a footer that is designed to be placed at the bottom of its parent container. Like the navigation, bar it provides immediate actions to the user, however, the key difference is that the navigation bar is strictly used for navigation, whereas the footer supports other important actions (as well as navigation if needed).

The footer supports 1-3 actions on small screens and 1-4 actions on larger screens. The actions are described in order of significance: primary button, secondary button, tertiary button (on large screens only), helper text, and an overflow icon button.

Anatomy

Structurally, the primary button is constrained to be displayed at the end of the footer and is a filled button, by default, to establish emphasis over the rest of the actions.

Persistent Footer Example 1

There are three action modes that determine how the Persistent Footer should be laid out, with ActionMode.RelatedActions being the default action mode.

  • ActionMode.RelatedActions, which aligns the buttons at the end of the footer
  • ActionMode.OpposingActions, which aligns the buttons on opposite ends of the footer
  • ActionMode.SingleActionFillMaxWidth, which displays a single button that expands to the width of the footer

The following is an example of a Persistent Footer set with ActionMode.SingleActionFillMaxWidth, which supports one action that fills the entire width of the footer.

Persistent Footer Example 2

Usage

The Persistent Footer and its variations can be implemented with a single API. Note that all parameters are optional with the button actions being any type of Composable. Theoretically, any Composable can be passed into the Persistent Footer, but buttons are strictly recommended by design:

@Composable
fun PersistentFooter(
    modifier: Modifier = Modifier,
    primaryButton: (@Composable () -> Unit)? = null,
    actionMode: ActionMode = ActionMode.RelatedActions,
    secondaryButton: (@Composable () -> Unit)? = null,
    tertiaryButton: (@Composable () -> Unit)? = null,
    overflowMenuButton: (@Composable () -> Unit)? = null,
    helperText: (@Composable () -> Unit)? = null,
    divider: (@Composable () -> Unit)? = null,
    elevation: Dp = 3.dp,
    colors: PersistentFooterColors = PersistentFooterDefaults.colors(),
    styles: PersistentFooterStyles = PersistentFooterDefaults.styles()
)

The Persistent Footer with ActionMode.RelatedActions supports a primary button, secondary button, tertiary button (only on larger screens), and an overflow button. Developers can display all or none of the buttons. In this example, the FioriFilledButton and FioriTonalButton are being passed as the Composable parameters. It is important to note that the overflow menu button must be provided by developers.

PersistentFooter(
    primaryButton = {
        FioriFilledButton(
            buttonContent = FioriButtonContent(
                title = primaryButtonText
            ),
            onClick = { }
        )
    },
    secondaryButton = {
        FioriTonalButton(
            buttonContent = FioriButtonContent(title = secondaryButtonText),
            onClick = { }
        )
    },
    overflowMenuButton = {
        //The overflow button must be supplied by the application
        OverflowActionButton()
    },
    modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars),
)

Persistent Footer with related actions:

Persistent Footer Example 3

The following example shows an example of the primary, secondary, and tertiary button as icon buttons. On larger screens, the overflow button is aligned with the primary, secondary, and tertiary buttons.

PersistentFooter(
    primaryButton = {
        FioriStandardIconButton(onClick = { }) { modifier, tint ->
            Icon(
                painter = painterResource(id = R.drawable.ic_sap_icon_accept),
                contentDescription = "icon button",
                modifier = modifier,
                tint = tint
            )
        }
    },
    actionMode = ActionMode.RelatedActions,
    secondaryButton = {
        FioriStandardIconButton(onClick = { }) { modifier, tint ->
            Icon(
                painter = painterResource(id = R.drawable.ic_sap_icon_decline),
                contentDescription = "icon button",
                modifier = modifier,
                tint = tint
            )
        }
    },
    tertiaryButton = {
        FioriStandardIconButton(onClick = { }) { modifier, tint ->
            Icon(
                painter = painterResource(id = R.drawable.ic_sap_icon_save),
                contentDescription = "icon button",
                modifier = modifier,
                tint = tint
            )
        }
    },
    overflowMenuButton = {
        OverflowActionButton()
    },
    modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars),
)

Icon buttons displayed on a larger screen with ActionMode.RelatedActions:

Persistent Footer Example 4

The Persistent Footer with ActionMode.Opposing supports a primary button, secondary button, and a helper text. Developers can display all or none of the components. The HelperText Composable is provided by the SDK and is the recommended Text Composable to use with the Persistent Footer:

@Composable
fun HelperText(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = PersistentFooterDefaults.colors().helperTextColor().value,
    textStyle: TextStyle = PersistentFooterDefaults.textStyles().helperTextStyle().value
)

In this example, the FioriTextButton and HelperText are being passed as the Composable parameters. The secondary button has a negative semantic applied to it:

PersistentFooter(
    primaryButton = {
        FioriTextButton(
            buttonContent = FioriButtonContent(
                title = primaryButtonText
            ),
            onClick = { footerToSet = 28 }
        )
    },
    actionMode = ActionMode.OpposingActions,
    secondaryButton = {
        FioriTextButton(
            buttonContent = FioriButtonContent(
                title = secondaryButtonText
            ),
            onClick = { footerToSet = 0 },
            semanticType = FioriButtonSemanticType.Negative
        )
    },
    helperText = { HelperText(text = "Helper Text") },
    modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars)
)

Persistent Footer with opposing actions and a helper text:

Label-only Tonal Button

The Persistent Footer with ActionMode.SingleActionFillMaxWidth supports only the primary button and fills the entire width of the footer. Note that the Modifier parameter is mandatory for the footer to display correctly:

PersistentFooter(
    primaryButton = {
        FioriFilledButton(
            modifier = Modifier.fillMaxWidth(),
            buttonContent = FioriButtonContent(
                title = primaryButtonText,
                icon = acceptIcon,
                displayIconAtStart = true
            ),
            onClick = { }
        )
    },
    actionMode = ActionMode.SingleActionFillMaxWidth,
    modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars)
)

Persistent Footer with a single action that fills the width of the footer:

Label-only Text Button


Last update: June 15, 2023