Skip to main content

MaterialBanner

Purpose​

A message across the top of the content that stays until it is answered.

Signature​

pub struct MaterialBanner<Msg = crate::callback::Callback> { .. }

impl MaterialBanner {
pub fn new(content: impl Into<String>) -> Self;
pub fn with_content(content: impl Widget<Msg> + 'static) -> Self;
}

Example​

Rendered preview of the MaterialBanner demo

From frus-examples/widgets/src/features/feedback/widgets/material_banner.rs:

//! `MaterialBanner` — a message across the top of the content that stays until one of its
//! actions is taken. Between a snack bar (gone in seconds) and a dialog (blocks
//! everything): for something important that can wait, like "you are offline".

use frus::{
Align, Button, Color, Column, Icon, Icons, MaterialBanner, State, StateContext, StatefulWidget,
Text, TextStyle, Variant, Widget,
};

use crate::common::shared::{section, sections};

pub fn example() -> Box<dyn Widget> {
Box::new(BannerDemo.into_widget())
}

struct BannerDemo;

struct BannerDemoState {
offline: bool,
update: bool,
}

impl StatefulWidget for BannerDemo {
type State = BannerDemoState;

fn create_state(&self) -> BannerDemoState {
BannerDemoState {
offline: true,
update: true,
}
}
}

impl State for BannerDemoState {
type Widget = BannerDemo;

fn build(&self, cx: &StateContext<Self>) -> Box<dyn Widget> {
// A banner is in the flow, not over it: showing it is simply building it.
let offline: Box<dyn Widget> = if self.offline {
MaterialBanner::new("You are offline. Changes will sync when you reconnect.")
.leading(Icon::new(Icons::CLOUD_OFF))
.action(
Button::new("Dismiss")
.variant(Variant::Text)
.on_press(cx.callback(|s: &mut BannerDemoState| s.offline = false)),
)
.action(
Button::new("Retry")
.variant(Variant::Text)
.on_press(cx.callback(|s: &mut BannerDemoState| s.offline = false)),
)
.build()
} else {
Box::new(
Button::new("Show again")
.variant(Variant::Outlined)
.on_press(cx.callback(|s: &mut BannerDemoState| s.offline = true)),
)
};

// Colours, padding and where the actions go are yours to change.
let update: Box<dyn Widget> = if self.update {
MaterialBanner::new("A new version is ready.")
.leading(Icon::new(Icons::DOWNLOAD).color(Color::hex("#065F46")))
.background(Color::hex("#D1FAE5"))
.content_text_style(TextStyle::new(14.0).color(Color::hex("#064E3B")))
.force_actions_below(true)
.action(
Button::new("Later")
.variant(Variant::Text)
.on_press(cx.callback(|s: &mut BannerDemoState| s.update = false)),
)
.action(
Button::new("Update")
.on_press(cx.callback(|s: &mut BannerDemoState| s.update = false)),
)
.build()
} else {
Box::new(
Button::new("Show again")
.variant(Variant::Outlined)
.on_press(cx.callback(|s: &mut BannerDemoState| s.update = true)),
)
};

sections(vec![
Box::new(section("Actions beside the message", offline)),
Box::new(section(
"Actions below, custom colours",
Column::new()
.spacing(8.0)
.cross_axis_alignment(Align::Stretch)
.child_boxed(update)
.child(Text::new("The content goes on under the banner.")),
)),
])
}
}

Properties​

MethodArgumentEffect
.leading()leading: impl Widget<Msg> + 'staticA glyph before the message.
.action()action: impl Widget<Msg> + 'staticAdds an action.
.force_actions_below()force: boolPuts the actions on a line of their own even when there is only one.
.content_text_style()style: TextStyleWhat the message is set in. bodyMedium by default.
.background()color: ColorThe banner's colour. A low container tone by default.
.surface_tint()color: ColorWhat the surface is tinted towards for its elevation.
.shadow_color()color: ColorThe shadow's colour. Unset, there is none.
.divider_color()color: ColorThe rule along the bottom, which is drawn only when the banner is flat — see Self::elevation.
.elevation()elevation: f32How far off the page it sits. 0 by default — see BANNER_ELEVATION.
.padding()padding: InsetsThe padding around the message row.
.margin()margin: InsetsThe margin around the banner.
.leading_padding()padding: InsetsWhat the leading slot is held off the message by.
.min_action_bar_height()height: f32The shortest the actions bar gets. 52 by default.

Behaviour​

A message across the top of the screen with the actions that answer it.

It sits in the flow, not over it: a banner is put above the content it is about, usually as the first child of a screen's column. Nothing hides it on a timer, which is the whole difference from a snack bar — an action does.

See also​

Source: crates/frus-widgets/src/banner.rs @ e6fba33f5b42