Reflex 组件实战:用 rx.segmented_control 构建互斥选项切换器(Radix Segmented Control 完全指南)
【免费下载链接】reflex🕸️ Web apps in pure Python 🐍项目地址: https://gitcode.com/GitHub_Trending/re/reflex
Segmented Control(分段控制器)是 Reflex 内置的 Radix Themes 组件,它把一组互斥选项(如「收件箱 / 草稿 / 已发送」)组合成一个高亮当前选中项的控件,帮助用户在预定义值和视图之间快速切换。本指南以 docs/library/disclosure/segmented_control.md 为核心,结合仓库中 segmented_control.py 的源码实现与集成测试,带你掌握它的全部 props、状态绑定、动态渲染与源码级实现细节,读完即可在纯 Python 的 Reflex 应用中落地使用。
Segmented Control 是什么
Segmented Control 用一组相邻的分段按钮呈现互斥选项——同一时刻只有一个选项处于激活状态,视觉上由一块滑动的指示器(indicator)标出当前选中项。相比下拉菜单(dropdown)或散落的多个按钮,它把相关选项在视觉上归组,当前状态一目了然,交互也更直接、可访问。
在 Reflex 中,该组件由两部分构成:
rx.segmented_control.root:外层容器,负责分组、当前值、样式与事件;rx.segmented_control.item:单个分段,定义标签(label)和唯一标识(value)。
两者在源码中分别对应SegmentedControlRoot与SegmentedControlItem两个类,并通过SegmentedControl命名空间以root/item静态方法暴露,定义见 segmented_control.py。该模块通过 mappings.py 中的懒加载映射按需导入,不会拖慢应用启动。
基本用法:root 组合 item
创建一个 Segmented Control 只需要在rx.segmented_control.root内放入若干rx.segmented_control.item。每个 item 需要一个人类可读的标签和唯一value:
import reflex as rx def segmented_demo() -> rx.Component: return rx.segmented_control.root( rx.segmented_control.item("Home", value="home"), rx.segmented_control.item("About", value="about"), rx.segmented_control.item("Test", value="test"), )root负责把 items 组合成整体;item的value是组件内部使用的唯一标识,当用户选中某个分段时,该 value 会通过事件回调传递出来(详见下一节)。
用 State 绑定选中值:on_change 与 value
要让控件真正「活」起来,需要把它接入 Reflex 的 State。文档中的基础示例定义了一个状态类,用control变量保存当前选中值,并用事件处理器set_control接收用户的选择:
import reflex as rx class SegmentedState(rx.State): """The app state.""" control: str = "test" @rx.event def set_control(self, value: str | list[str]): self.control = value注意set_control的参数类型是str | list[str]:Segmented Control 在type="multiple"模式下会一次性回传多个选中值,因此事件处理器的签名需要同时兼容单选与多选。这与源码中on_value_change事件处理函数的签名value: Var[str | list[str]]完全一致,见 segmented_control.py。
接着把控件与状态双向绑定:
def basic_example() -> rx.Component: return rx.vstack( rx.segmented_control.root( rx.segmented_control.item("Home", value="home"), rx.segmented_control.item("About", value="about"), rx.segmented_control.item("Test", value="test"), on_change=SegmentedState.set_control, value=SegmentedState.control, ), rx.card( rx.text(SegmentedState.control, align="left"), rx.text(SegmentedState.control, align="center"), rx.text(SegmentedState.control, align="right"), width="100%", ), )在这个示例中:
on_change指定回调函数:用户切换分段时调用SegmentedState.set_control,把新选中的 value 写入control状态变量;value指定当前选中的分段,与SegmentedState.control绑定,实现受控组件——状态变化会驱动控件重新渲染,卡片中三段文字也会同步显示当前值。
也就是说,on_change负责「用户 → 状态」的数据流,value负责「状态 → 控件」的回显,二者配合才能保证 UI 与状态始终一致。
完整 props 参考
rx.segmented_control.root支持的配置项在 SegmentedControlRoot 中以 field 形式声明,汇总如下:
| 属性 | 类型 | 说明 |
|---|---|---|
size | "1" \| "2" \| "3"(支持响应式值) | 控件尺寸,"1"最小、"3"最大 |
variant | "classic" \| "surface" | 视觉风格:classic为经典边框样式,surface为表面填充样式 |
type | "single" \| "multiple" | 选择模式:single单选(默认),multiple可多选 |
color_scheme | 强调色字面量(如"tomato"、"blue"、"indigo"等) | 覆盖主题的强调色,用于选中态高亮 |
radius | "none" \| "small" \| "medium" \| "large" \| "full" | 圆角大小 |
default_value | str \| Sequence[str] | 非受控模式下的默认选中值 |
value | str \| Sequence[str] | 受控模式下的当前选中值 |
on_change | 事件处理器 | 处理onChange事件,回传str \| list[str] |
其中color_scheme可用的强调色集合(tomato、red、ruby、crimson、pink、plum、violet、iris、indigo等)定义在 base.py 的 LiteralAccentColor;radius的字面量类型与 Radix Themes 的LiteralRadius保持一致,见同一文件的 base.py。
rx.segmented_control.item则只有一个核心属性value(字符串,作为该分段的唯一标识),并且仅允许作为SegmentedControlRoot的子组件出现——源码通过_valid_parents = ["SegmentedControlRoot"]约束了父子关系,见 segmented_control.py。
单选与多选
默认type="single"时,一次只能选中一个分段;当需要允许同时选择多个值时,设置type="multiple",此时on_change会以列表形式回传所有选中项的 value,事件处理器可这样处理:
import reflex as rx class MultiState(rx.State): selected: list[str] = [] @rx.event def on_select(self, value: str | list[str]): self.selected = value if isinstance(value, list) else [value] def multi_example() -> rx.Component: return rx.segmented_control.root( rx.segmented_control.item("Python", value="py"), rx.segmented_control.item("JavaScript", value="js"), rx.segmented_control.item("Rust", value="rs"), type="multiple", on_change=MultiState.on_select, value=MultiState.selected, )需要说明的是,源码注释指出:type="multiple"时不会注入滑动指示器的自定义样式(单个滑动指示器无法表达多个选中态),Radix 默认的 nth-child 规则依然生效,详见 segmented_control.py。
动态渲染:结合 rx.foreach
当选项是动态数据时,可以用rx.foreach在root内批量生成item。仓库集成测试 test_appearance.py 中有一个用 11 个选项渲染控件的完整示例:
import reflex as rx class SegmentedState(rx.State): options: list[str] = [str(i) for i in range(1, 12)] control: str = "1" @rx.event def set_control(self, value: str | list[str]): self.control = value if isinstance(value, str) else value[0] def many_items_example() -> rx.Component: return rx.segmented_control.root( rx.foreach( SegmentedState.options, lambda label: rx.segmented_control.item(label, value=label), ), on_change=SegmentedState.set_control, value=SegmentedState.control, )源码中的_collect_item_values专门处理了这种场景:它会识别「单个rx.foreach生成 items」或「扁平列表的SegmentedControlItem」两种子组件形态,并提取出有序的 value 列表,用于后续的指示器定位计算;其余形态则回退到 Radix 默认行为,见 segmented_control.py。
源码级深入:超过 10 个选项的指示器修复
SegmentedControlRoot.create与add_style是理解该组件底层原理的关键。Radix Themes 3.3.0 在radix-ui/themes#730中硬编码了最多 10 个 item 的指示器宽度 / 位移 CSS 规则,当选项超过 10 个时指示器会塌缩为零宽度。Reflex 通过注入 CSS 自定义属性绕过此限制:
- 在
create中(非multiple模式下),若能从子组件中收集到 value 列表且提供了value或default_value,就把--rx-sc-count(item 总数)与--rx-sc-idx(选中项索引)写入根节点的 style,见 segmented_control.py; add_style检测到这些自定义属性后,覆盖指示器样式,用calc()计算宽度与位移:width: calc(100% / var(--rx-sc-count))、transform: translateX(calc(var(--rx-sc-idx) * 100%)),使指示器对任意数量的 item 都正确对齐,见 segmented_control.py。
集成测试test_segmented_control_indicator_with_11_items专门验证了这一点:它渲染 11 个 item,点击第 11 个后断言指示器可见、宽度大于 0,且与第 11 项的 x 坐标误差小于 2px,见 test_appearance.py。
也就是说,日常使用 ≤10 个选项时无需关心此细节(Radix 默认规则即可胜任);选项更多时,Reflex 的自动修复保证了指示器依然正确工作。
实战案例:颜色模式切换器
仓库的集成测试还提供了一个非常实用的真实用例——用 Segmented Control 做主题颜色模式切换(system/light/dark),item 内直接放置图标:
import reflex as rx from reflex_base.style import color_mode, resolved_color_mode, set_color_mode def color_toggle_example() -> rx.Component: return rx.box( rx.segmented_control.root( rx.segmented_control.item( rx.icon(tag="monitor", size=20), value="system", ), rx.segmented_control.item( rx.icon(tag="sun", size=20), value="light", ), rx.segmented_control.item( rx.icon(tag="moon", size=20), value="dark", ), on_change=set_color_mode, variant="classic", radius="large", value=color_mode, ), rx.text(color_mode, id="current_color_mode"), rx.text(resolved_color_mode, id="resolved_color_mode"), )该示例展示了几个进阶要点,源码见 test_appearance.py:
- item 内容不限于纯文本:可以放入
rx.icon等任意组件作为分段标签; - 样式组合:
variant="classic"配合radius="large"塑造更柔和的视觉; - 绑定系统级状态:
on_change=set_color_mode直接使用 Reflex 内置的颜色模式切换函数,value=color_mode回显当前模式,构建出开箱即用的主题切换控件。
小结
rx.segmented_control是 Reflex 中实现互斥选项切换的首选组件:root负责分组与状态同步,item定义具体选项,on_change与value完成与 State 的双向绑定,size、variant、type、color_scheme、radius提供了丰富的定制空间。结合rx.foreach可以动态渲染任意数量的选项,而底层源码对超过 10 个选项的指示器修复,则保证了它在边界场景下依然稳定可用。无论是页面内的视图切换、筛选条件选择,还是主题模式切换,都可以用这一个组件优雅实现。
【免费下载链接】reflex🕸️ Web apps in pure Python 🐍项目地址: https://gitcode.com/GitHub_Trending/re/reflex
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考