SegmentedControl
SegmentedControl is the compact, always-visible alternative to a dropdown for a small set of mutually exclusive choices.
It works well when the user should be able to compare the available modes at a glance, such as switching between tabs, filters, or display modes. It is not a good fit for large or dynamic option sets.
Example
use fission::prelude::*;
use std::sync::Arc;
let node = SegmentedControl {
options: vec!["All".into(), "Unread".into(), "Flagged".into()],
selected_index: view.state.filter_mode,
on_change: Some(Arc::new(move |index| ActionEnvelope {
id: set_filter_mode_id,
payload: serde_json::to_vec(&SetFilterMode(index)).unwrap(),
})),
}
.build(ctx, view);
Each segment is just a button under the hood. The selected segment is derived from state on every build.
Field table
| Field | Type | Meaning | Notes / default behavior |
|---|---|---|---|
options | Vec<String> | Labels for each segment. | Required. Keep the set short. |
selected_index | usize | Which option is currently active. | Controlled by app state. |
on_change | Option<Arc<dyn Fn(usize) -> ActionEnvelope + Send + Sync>> | Closure that creates the action for a newly chosen index. | Called once per segment during build to prepare that segment's action. |
How it fits the state loop
The closure returns an ActionEnvelope for the chosen index. When the user presses a segment, that action is dispatched, the reducer updates the selected index in AppState, and the next build re-renders the control with the new active segment.
Because the closure runs during build() to prepare per-segment actions, keep it cheap and deterministic. It should package data, not do work.
Specific advice
Use segmented controls for short, peer-level choices. If labels start wrapping, if there are more than a handful of options, or if options may disappear behind scrolling, you probably want Select or Radio instead.