Gradient Button
Button featuring a static linear gradient background. Shows a subtle overlay on press and supports loading/disabled states.
reanimatedbuttongradientlinear-gradient
Installation
Step 1: Install Dependencies
npx expo install expo-linear-gradient
Step 2: Create the GradientButton
Create a new file (e.g., src/components/GradientButton.tsx
) and copy the following code:
GradientButton.tsx
import { LinearGradient } from "expo-linear-gradient";
import { ReactElement } from "react";
import { ActivityIndicator, Pressable, StyleSheet, Text, View } from "react-native";
export type AnimatedGradientBackgroundButtonProps = {
onPress: () => void;
buttonColorOne: string;
buttonColorTwo: string;
textColor: string;
title: string;
accessibilityHint?: string;
accessibilityLabel?: string;
Icon?: ReactElement;
isDisabled?: boolean;
isLoading?: boolean;
}
const HEIGHT = 42;
export const GradientButton = ({
buttonColorOne,
buttonColorTwo,
textColor,
accessibilityHint,
accessibilityLabel,
Icon,
isDisabled = false,
isLoading = false,
onPress,
title,
}: AnimatedGradientBackgroundButtonProps) => {
return (
<Pressable
accessibilityHint={accessibilityHint}
accessibilityLabel={accessibilityLabel}
accessibilityRole="button"
accessibilityState={{
busy: isLoading,
disabled: isDisabled || isLoading,
}}
disabled={isDisabled || isLoading}
onPress={onPress}
>
{({ pressed }) => (
<View
// Removed onLayout
style={[
styles.outerContainer,
// Apply opacity for disabled state to the whole container
{ opacity: isDisabled ? 0.5 : 1 },
]}
>
{/* Static LinearGradient as the background */}
<LinearGradient
colors={[buttonColorOne, buttonColorTwo]}
end={{ x: 1, y: 1 }}
start={{ x: 0, y: 1 }}
style={styles.linearGradient} // Use simplified style
/>
<View
style={[
styles.contentContainer,
{
// Apply press effect by changing background (overlay)
opacity: pressed && !isDisabled ? 0.5 : 1,
},
]}
>
{isLoading ? (
<ActivityIndicator color={textColor} size={18} /> // Changed color to contrast potentially lighter gradients
) : (
<>
{Icon}
<Text numberOfLines={1} style={[styles.title, {color: textColor}]}>
{title}
</Text>
</>
)}
</View>
</View>
)}
</Pressable>
);
};
const styles = StyleSheet.create({
contentContainer: {
alignItems: "center",
flexDirection: "row",
gap: 8,
height: HEIGHT,
justifyContent: "center",
paddingHorizontal: 12,
paddingVertical: 8,
position: "absolute",
width: "100%",
top: 0,
left: 0,
zIndex: 1,
},
linearGradient: {
height: HEIGHT,
width: '100%', // Gradient just needs to fill its container
},
// This is the main Pressable container, handles border radius and overflow
outerContainer: {
borderRadius: 8,
overflow: "hidden",
width: "100%",
height: HEIGHT, // Set height here directly
position: 'relative', // Needed for absolute positioning of contentContainer
},
title: {
flexShrink: 1,
fontSize: 18,
fontWeight: "600",
},
});
Usage
App.tsx
import React, { useState } from 'react';
import { View, StyleSheet, Alert } from 'react-native';
import { GradientButton } from "@/components/ui/Buttons/GradientButton";
export default function App() {
const [isLoading, setIsLoading] = useState(false);
const handleLoadingPress = () => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
}, 2500);
};
return (
<GradientButton
buttonColorOne={colors.AuxColorThree}
buttonColorTwo={colors.AuxColorTwo}
textColor={colors.Neutral0}
onPress={() => {}}
title="Gradient Action"
/>
);}
Props
Prop | Type | Default | Required | Description |
---|---|---|---|---|
title | string | Yes | Text label displayed on the button. | |
onPress | () => void | Yes | Function called when the button is pressed. | |
Icon | ReactElement | No | Optional icon element displayed left of the title. | |
isDisabled | boolean | false | No | Disables button interaction and styles it. |
isLoading | boolean | false | No | Shows loading indicator instead of title/icon. |
accessibilityHint | string | No | Accessibility hint for screen readers. | |
accessibilityLabel | string | No | Main accessibility label for the button. |