Ignite Button 组件完全指南:基于 Pressable 的可定制按钮实战
Ignite Button 组件完全指南基于 Pressable 的可定制按钮实战【免费下载链接】igniteInfinite Reds battle-tested React Native project boilerplate, along with a CLI, component/model generators, and more! 9 years of continuous development and counting.项目地址: https://gitcode.com/GitHub_Trending/ig/ignite本文是 Ignite React Native 样板工程内置Button组件的权威使用手册。Button是对 React NativePressable的封装组件支持文本国际化、内置/自定义样式预设、按压态与禁用态样式、左右装饰组件Accessory等能力广泛用于 Ignite 工程中的登录、表单提交、列表操作等所有需要用户交互的场景。读完本文你将掌握Button的全部 Props 用法、自定义预设的实现方式以及其底层样式系统ThemedStyle的工作原理可以直接在自己的 Ignite 工程中落地使用。Button 组件是什么Button是 Ignite 内置组件库见 Components.md中的核心交互组件。官方文档将其定位为Pressable组件的一个包装器wrapper——任何可以传给Pressable的 prop 都可以传给Button并被原样转发Button在Pressable内部渲染一个携带指定文本的 Text 组件或任意children同时允许你指定按钮的预设样式preset并可以分别覆盖Pressable容器样式和Text文本样式。从源码实现来看boilerplate/app/components/Button.tsxButton的组件签名与内部结构非常清晰export function Button(props: ButtonProps) { // 解构出 tx/text/txOptions/style/pressedStyle/textStyle 等专有 props // 其余 props如 onPress通过 ...rest 转发给 Pressable const preset: Presets props.preset ?? default return ( Pressable style{$viewStyle} accessibilityRolebutton accessibilityState{{ disabled: !!disabled }} {...rest} disabled{disabled} {(state) ( {!!LeftAccessory ( LeftAccessory style{$leftAccessoryStyle} pressableState{state} disabled{disabled} / )} Text tx{tx} text{text} txOptions{txOptions} style{$textStyle(state)} {children} /Text {!!RightAccessory ( RightAccessory style{$rightAccessoryStyle} pressableState{state} disabled{disabled} / )} / )} /Pressable ) }几个值得注意的源码细节ButtonProps extends PressablePropsButton.tsx#L25因此onPress、onLongPress、hitSlop、testID等所有Pressable属性都天然可用Pressable的style接受一个接收状态的回调函数Button利用这一机制在按压时动态叠加样式$viewStyle/$textStyle函数组件固定设置了accessibilityRolebutton和accessibilityState{{ disabled: !!disabled }}开箱即用地满足无障碍a11y要求。一个完整的用法示例Button textClick It txbutton:clickIt presetdefault onPress{() Alert.alert(pressed)} style{[{ paddingVertical: 100 }, { borderRadius: 0 }]} pressedStyle{[{ backgroundColor: red }, { borderRadius: 0 }]} textStyle{[{ fontSize: 20 }, { color: #a511dc }]} pressedTextStyle{[{ fontSize: 20 }, { color: #a51111 }]} RightAccessory{(props) Icon iconcheck /} LeftAccessory{(props) Icon iconclose /} /Props 一览Prop类型默认值说明textstring—按钮要渲染的文本与tx、children三选一txTxKeyPath—i18n 翻译键用于国际化文本txOptionsTOptions—传给 i18n 的插值选项见 Text.tsxchildrenReactNode—自定义内容替代默认Textpresetdefault \| filled \| reverseddefault预设样式styleStylePropViewStyle—覆盖Pressable容器样式pressedStyleStylePropViewStyle—按压态容器样式disabledStyleStylePropViewStyle—禁用态容器样式textStyleStylePropTextStyle—覆盖文本样式pressedTextStyleStylePropTextStyle—按压态文本样式disabledTextStyleStylePropTextStyle—禁用态文本样式LeftAccessoryComponentTypeButtonAccessoryProps—文本左侧装饰组件RightAccessoryComponentTypeButtonAccessoryProps—文本右侧装饰组件disabledboolean—禁用按压行为并应用禁用样式以上各 prop 的完整类型定义可查阅 Button.tsx#L25-L86 中的ButtonProps接口。三种传内容的方式text、tx与childrenButton支持三种方式向按钮传入内容三者必选其一否则按钮将没有可见内容1.text直接传入文本Button textClick me /2.tx传入翻译键国际化Button txbutton:clickMe /这是 Ignite 应用最推荐的方式。翻译键的查找由 Text.tsx 完成const i18nText tx translate(tx, txOptions) const content i18nText || text || children翻译键的优先级是txtextchildren。如果你需要插值例如在按钮文本中显示用户名可以搭配txOptionsButton txwelcome:greeting txOptions{{ name: currentUser.name }} /3.children传入自定义内容当需要更复杂的按钮内容时可以用children完全替换默认的Text渲染Button TextClick me/Text /Buttonchildren支持任意 React 节点甚至可以嵌套多个带不同 preset 的Text来构造富文本按钮示例见 DemoButton.tsx#L99-L111。预设样式Preset内置预设preset是一个可选 prop用于快速切换按钮的整体视觉风格。内置三种预设定义于 Button.tsx#L17default默认白底neutral100、1px 灰色边框neutral400filled浅灰填充neutral300无边框reversed深色填充neutral800文本为白色neutral100。Button presetdefault txbutton:clickMe / Button presetfilled textFilled / Button presetreversed textReversed /三种预设的按压态也有对应变化default按压变neutral200filled变neutral400reversed变neutral700文本在按压时统一将透明度降为0.9详见$pressedViewPresets与$pressedTextPresetsButton.tsx#L238-L248。自定义预设内置预设不满足需求时可以轻松扩展。做法是在 boilerplate/app/components/Button.tsx 中向$viewPresets、$textPresets、$pressedViewPresets、$pressedTextPresets四个对象各添加一个同名的 key然后把这个名字传给presetprop 即可。例如新增一个红色的danger危险操作预设const $viewPresets { // ... danger: [$baseViewStyle, { backgroundColor: colors.palette.angry500 }] as StylePropViewStyle, } const $textPresets: RecordPresets, StylePropTextStyle { // ... danger: [$baseTextStyle, { color: colors.palette.angry500 }] as StylePropTextStyle, } const $pressedViewPresets: RecordPresets, StylePropViewStyle { // ... danger: { backgroundColor: colors.palette.angry500 }, } const $pressedTextPresets: RecordPresets, StylePropTextStyle { angry: { opacity: 0.7 }, }之后即可这样使用Button presetdanger textDelete /提示自定义预设中引用的colors.palette.angry500来自 boilerplate/app/theme/colors.ts该文件定义了完整的颜色调色板palette。注意四个 preset 对象必须保持 key 一致否则 TypeScript 会在RecordPresets, ...类型约束下报错。样式覆盖体系六个样式 PropsButton将样式拆分为容器View 文本Text两个维度每个维度又区分常态 / 按压态 / 禁用态共计六个样式 prop。所有覆盖值都会叠加在预设样式之上优先级高于 preset 中定义的样式。容器样式style、pressedStyle、disabledStyle{/* 常态覆盖 Pressable 的 padding 与圆角 */} Button style{{ paddingVertical: 20, borderRadius: 10 }} / {/* 按压态按下时背景变红 */} Button pressedStyle{{ backgroundColor: red }} / {/* 禁用态禁用时半透明 */} Button disabledStyle{{ opacity: 0.5 }} /文本样式textStyle、pressedTextStyle、disabledTextStyleButton textStyle{{ fontSize: 20, color: #a511dc }} / Button pressedTextStyle{{ fontSize: 20, color: #a51111 }} / Button disabled disabledTextStyle{{ fontSize: 20, color: #000000 }} /这些覆盖在源码中如何生效关键在$viewStyle与$textStyle两个内部函数Button.tsx#L128-L148function $viewStyle({ pressed }: PressableStateCallbackType): StylePropViewStyle { return [ themed($viewPresets[preset]), // ① 预设基础样式 $viewStyleOverride, // ② style 覆盖 !!pressed themed([$pressedViewPresets[preset], $pressedViewStyleOverride]), // ③ 按压态 !!disabled $disabledViewStyleOverride, // ④ 禁用态 ] }样式按预设 → 显式覆盖 → 按压态 → 禁用态的顺序拼成数组传入PressableReact Native 会按数组顺序进行样式合并后出现的样式覆盖先出现的样式因此各覆盖 prop 的优先级高于 preset。值得注意的是这里的样式并不是普通的 StyleSheet 对象而是ThemedStyle主题函数——见下文主题系统一节。Accessory左右装饰组件LeftAccessory与RightAccessory允许你在按钮文本的两侧渲染装饰内容如图标它们可以是 React 组件也可以是返回 React 组件的函数会通过pressableStateprop 接收到Pressable的按压状态从而可以做出按下时变化的响应式渲染可以通过styleprop 使用默认的 accessory 样式即$leftAccessoryStyle/$rightAccessoryStyle分别在文本两侧添加marginStart/marginEnd: spacing.xs间距见 Button.tsx#L201-L208。基于按压状态渲染Button LeftAccessory{(props) ( Icon containerStyle{props.style} size{props.pressableState.pressed ? 50 : 40} iconcheck / )} / Button RightAccessory{(props) ( Icon containerStyle{props.style} size{props.pressableState.pressed ? 50 : 40} iconcheck / )} /用 useMemo 消除闪烁如果 accessory 在某个 prop 或 state 变化时出现闪烁重新渲染可以用useMemo将其记忆化。accessory 的函数签名类型为ButtonAccessoryProps定义于 Button.tsx#L19-L23包含style、pressableState与disabled三个字段Button LeftAccessory{useMemo( () function LeftIcon(props: ButtonAccessoryProps) { return Icon icon{props.pressableState.pressed ? view : hidden} / }, [], )} /在 Ignite 的展示厅Showroom示例中accessory 还被用来实现更具创意的效果例如在按钮右侧渲染一个绝对定位的色块并在按压时通过tintColor改变颜色见 DemoButton.tsx#L141-L164。禁用状态disableddisabled是可选 prop会被透传给底层的Pressable当值为真时Pressable的按压行为被禁用对应 React Native 的 Pressable#disabled 语义同时会更新Pressable的accessibilityState源码中即accessibilityState{{ disabled: !!disabled }}让屏幕阅读器等无障碍工具正确感知禁用状态如果设置了LeftAccessory/RightAccessorydisabled也会一并传给它们通过ButtonAccessoryProps.disabled方便装饰组件同步呈现禁用外观若设置了disabledStyle与disabledTextStyle禁用时会自动套用。Button disabled disabledStyle{{ opacity: 0.5 }} disabledTextStyle{{ fontSize: 20, color: #000000 }} Disabled /ButtonShowroom 的禁用示例DemoButton.tsx#L167-L230展示了标准 / filled / reversed 三种预设下的禁用态、禁用 accessory 样式以及禁用文本样式的组合用法。源码级剖析Button 的默认样式与主题系统基础样式定义Button的所有预设都构建在$baseViewStyle与$baseTextStyle两个基础样式之上Button.tsx#L181-L199const $baseViewStyle: ThemedStyleViewStyle ({ spacing }) ({ minHeight: 56, // 最小高度 56保证可点击区域足够大 borderRadius: 4, justifyContent: center, alignItems: center, paddingVertical: spacing.sm, paddingHorizontal: spacing.sm, overflow: hidden, // 裁剪超出圆角的子内容如装饰色块 }) const $baseTextStyle: ThemedStyleTextStyle ({ typography }) ({ fontSize: 16, lineHeight: 20, fontFamily: typography.primary.medium, // 默认使用 Space Grotesk Medium textAlign: center, flexShrink: 1, flexGrow: 0, zIndex: 2, })三个内置预设的具体组成Button.tsx#L210-L248const $viewPresets: RecordPresets, ThemedStyleArrayViewStyle { default: [ $styles.row, // { flexDirection: row }让文本与 accessory 水平排列 $baseViewStyle, ({ colors }) ({ borderWidth: 1, borderColor: colors.palette.neutral400, backgroundColor: colors.palette.neutral100, }), ], filled: [ $styles.row, $baseViewStyle, ({ colors }) ({ backgroundColor: colors.palette.neutral300 }), ], reversed: [ $styles.row, $baseViewStyle, ({ colors }) ({ backgroundColor: colors.palette.neutral800 }), ], }其中$styles.row来自 boilerplate/app/theme/styles.ts$styles是样板工程提供的一组跨组件复用的全局样式常量。ThemedStyle 主题函数机制上文代码中的ThemedStyleT本质是一个(theme: Theme) T的函数类型定义见 boilerplate/app/theme/types.ts#L52-L57。Button通过useAppTheme()拿到themed函数来自 boilerplate/app/theme/context.tsx把样式函数解析为实际样式对象const { themed } useAppTheme()themed的实现会摊平样式数组逐个调用其中的函数式样式再合并为单一对象context.tsx#L108-L122。这意味着所有预设样式都同时适配浅色/深色主题——ThemeProvider会根据系统主题或用户设置切换lightTheme/darkTheme见 boilerplate/app/theme/theme.ts颜色取值统一走 colors.ts 定义的调色板保证设计规范一致自定义样式如 DemoButton 中的$customButtonStyle同样可以写成ThemedStyleViewStyle函数再用themed(...)解析后传入styleprop。在 Showroom 中查看真实效果样板工程自带组件展示厅Showroom可以在其中直接交互体验Button的全部能力DemoButton.tsx。它覆盖了四个用例分组Presetsdefault/filled/reversed三种预设的视觉对比Passing Contenttextprop、txprop、children、左右 Accessory、嵌套 Text、多行文本六种传内容方式Styling自定义容器样式、文本样式、Accessory 样式以及按压态样式的组合演示Disabling标准 / filled / reversed 三种预设的禁用态以及禁用态下 Accessory 与文本样式的定制。启动应用后进入 DemoShowroomScreen 的 Button 用例即可查看。每个用例的文案描述定义在 boilerplate/app/i18n/demo-en.ts#L161-L201是理解各功能点的良好参考。最佳实践小结内容传递优先用tx配合 Ignite 的 i18n 体系Text 组件文档按钮文案可以轻松多语言化先用 preset再局部覆盖内置三种预设已覆盖大多数场景需要定制时优先通过style/textStyle等覆盖 prop 微调避免复制粘贴大量样式新增视觉风格用自定义 preset同一套危险/成功/主色按钮在多个页面复用时扩展$viewPresets等四个对象比逐处写样式更可维护Accessory 响应按压利用pressableState.pressed可以做出高反馈感的图标按钮如有闪烁再用useMemo记忆化始终考虑禁用态为disabled场景配置disabledStyle与disabledTextStyle提升表单场景的可用性与无障碍表现。Button与 Ignite 的其他内置组件Card、TextField、Header等共同构成了一套轻量级设计系统完整组件清单与文档入口见 Components.md。【免费下载链接】igniteInfinite Reds battle-tested React Native project boilerplate, along with a CLI, component/model generators, and more! 9 years of continuous development and counting.项目地址: https://gitcode.com/GitHub_Trending/ig/ignite创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考