如何在React Native中实现点击后占位符移至顶部的文本输入组件
Hey there! I totally get what you're going for—this sleek UI pattern is called a floating label input, and it's super doable in React Native. Let me walk you through building it from scratch (no external libraries required, though I'll mention a shortcut too!)
Core Idea
We'll create a custom component that tracks two key states:
- Whether the input is focused
- If there's text already entered in the input
Based on these states, we'll animate a label (our "placeholder") from the center of the input up to the top, shrinking its font size and adjusting its color along the way. React Native's Animated API will handle the smooth transition.
Full Component Code
import React, { useState, useRef, useEffect } from 'react'; import { View, TextInput, Animated, StyleSheet } from 'react-native'; const FloatingLabelInput = ({ label, value, onChangeText, ...props }) => { const [isFocused, setIsFocused] = useState(false); const inputValueRef = useRef(value || ''); const labelAnimation = useRef(new Animated.Value(0)).current; // Update ref when external value changes useEffect(() => { inputValueRef.current = value || ''; // Trigger animation immediately if value exists on mount if (inputValueRef.current) { Animated.timing(labelAnimation, { toValue: 1, duration: 200, useNativeDriver: false, }).start(); } }, [value]); // Handle focus and animate label up const handleFocus = () => { setIsFocused(true); Animated.timing(labelAnimation, { toValue: 1, duration: 200, useNativeDriver: false, }).start(); }; // Handle blur: keep label up if input has content, else reset it const handleBlur = () => { const hasContent = inputValueRef.current !== ''; setIsFocused(hasContent); Animated.timing(labelAnimation, { toValue: hasContent ? 1 : 0, duration: 200, useNativeDriver: false, }).start(); }; // Map animation values to label styles const labelStyle = { transform: [ { translateY: labelAnimation.interpolate({ inputRange: [0, 1], outputRange: [15, -12], // Move from center to top }), }, ], fontSize: labelAnimation.interpolate({ inputRange: [0, 1], outputRange: [16, 12], // Shrink font size }), color: labelAnimation.interpolate({ inputRange: [0, 1], outputRange: ['#999', '#2196F3'], // Darken color when active }), }; return ( <View style={styles.container}> <Animated.Text style={[styles.label, labelStyle]}> {label} </Animated.Text> <TextInput style={styles.input} value={value} onChangeText={(text) => { inputValueRef.current = text; onChangeText(text); }} onFocus={handleFocus} onBlur={handleBlur} {...props} /> </View> ); }; const styles = StyleSheet.create({ container: { position: 'relative', marginVertical: 16, }, label: { position: 'absolute', left: 12, backgroundColor: '#fff', // Match your screen's background color paddingHorizontal: 4, }, input: { height: 48, borderWidth: 1, borderColor: '#ccc', borderRadius: 8, paddingHorizontal: 12, paddingTop: 16, // Make space for the floating label fontSize: 16, }, }); export default FloatingLabelInput;
Breakdown of Key Parts
- State & Refs: We use
useStatefor focus state anduseRefto track input content (so we can keep the label up even after the input loses focus if there's text inside). - Animation:
Animated.timingdrives smooth transitions, andinterpolatemaps our animation value (0 = inactive, 1 = active) to style changes like position, size, and color. - Input Padding: The
paddingTopon the input ensures typed text doesn't overlap with the floating label when it's at the top. - Label Background: Setting a background color on the label makes it "cover" the input border when floating up—match this to your screen's background for a clean look.
Shortcut: Use a UI Library
If you don't want to build this from scratch, most React Native UI libraries include this component out of the box:
- React Native Paper: Their
TextInputhasmode="outlined"ormode="flat"options that include floating labels by default. - NativeBase: The
Inputcomponent supports floating labels via thevariant="floating"prop.
内容的提问来源于stack exchange,提问作者MIike Eps

