Skip to main content

Drawer

Drawer is a full-height side panel that appears above the main screen.

Use it when you need temporary space for navigation, filters, settings, or an inspector without changing the main route. The important architectural rule is that the drawer does not decide when it is open. Your app state owns that decision through is_open. The widget's job is to render the panel into the modal portal layer, add a backdrop, and dispatch your dismiss action when the backdrop is tapped.

This makes the behavior predictable. A button dispatches an action, the reducer flips show_filters to true, and the next build() pass includes an open drawer. Closing the drawer is the same loop in reverse.

Do not use a drawer for critical confirmations or short blocking decisions. That is usually a Modal. Also do not use it for tiny anchored choices next to a button. That is what Popover or flyout are for.

Example

use fission::core::{Handler, WidgetNodeId};
use fission::prelude::*;

let drawer = Drawer {
id: WidgetNodeId::explicit("filters_drawer"),
side: DrawerSide::Right,
is_open: view.state.show_filters,
on_dismiss: Some(ctx.bind(
ToggleFilters(false),
reduce_with!((|state: &mut AppState, action: ToggleFilters, _| {
state.show_filters = action.0;
})),
)),
width: Some(360.0),
content: Box::new(
VStack {
spacing: Some(16.0),
children: vec![filter_form.build(ctx, view)],
}
.into_node(),
),
}
.build(ctx, view);

The reducer owns show_filters. The drawer itself only reads that state and emits on_dismiss when the user taps the backdrop.

Field table

FieldTypeMeaningNotes / default behavior
idWidgetNodeIdStable identity for the portal entry.Required. Give each drawer a stable id so the runtime can track it consistently.
sideDrawerSideWhich horizontal edge the panel attaches to.Use Left or Right.
is_openboolWhether the drawer should be visible this frame.When false, the widget returns an inert spacer and registers no portal.
on_dismissOption<ActionEnvelope>Action dispatched when the backdrop is tapped.Defaults to None. If you omit it, the backdrop still covers the screen but does not close the drawer for you.
contentBox<Node>The panel body.Build the full drawer user interface here.
widthOption<f32>Requested panel width in logical pixels.Defaults to 300.0. The runtime clamps it to the current viewport so the drawer still fits on smaller screens.

Layout and overlay behavior

When open, Drawer registers a portal in PortalLayer::Modal. The shell wraps portal content in a viewport-sized container, so the drawer always measures against the whole screen instead of the inline spot where the widget was declared.

The panel itself is pinned to the chosen edge and stretches from top to bottom. The backdrop covers the full viewport behind it. This means the screen under the drawer is visually present but not the place where the user should continue interacting.

Specific advice

Put only temporary, secondary user interface in a drawer. If people need to finish a task before they can continue, use a modal dialog instead. If the panel is always visible on large screens, consider a normal layout widget such as SplitView, Row, or Column instead of opening and closing an overlay.

Also note that the current implementation does not animate the drawer sliding in or out by itself. It appears and disappears as state changes. If motion is important to your product, treat that as an explicit design decision and test the chosen pattern carefully.

DrawerSide, Modal, Popover, Portal, and SafeArea.