ResizableSplitView Component
A React Native component that creates a resizable split-screen interface with smooth gesture-based resizing. The component allows users to drag a handle to resize the top and bottom sections dynamically. It is a new ui approach for mobile apps.
Core Concept
The component uses a shared value (topSectionHeight
) to control the height of the top section, which automatically adjusts the bottom section height. The resizing is handled through pan gestures on a drag handle positioned between the sections.
Basic Structure
<GestureHandlerRootView style={{ flex: 1 }}> <SafeAreaView style={styles.container}> {/* Top Section - Resizable */} <Animated.View style={[styles.topSection, topSectionAnimatedStyle]}> {/* Your top content */} </Animated.View> {/* Drag Handle */} <GestureDetector gesture={panGesture}> <Animated.View style={styles.dragHandleContainer}> <Animated.View style={styles.dragHandle} /> </Animated.View> </GestureDetector> {/* Bottom Section - Auto-adjusting */} <Animated.View style={[styles.bottomSection, bottomSectionAnimatedStyle]}> {/* Your bottom content */} </Animated.View> </SafeAreaView> </GestureHandlerRootView>
Key Constants
const { height: SCREEN_HEIGHT } = Dimensions.get('window'); const HEADER_HEIGHT = 60; const MIN_SECTION_HEIGHT = 100; const MAX_TOP_SECTION_HEIGHT = SCREEN_HEIGHT * 0.7; const DEFAULT_TOP_SECTION_HEIGHT = SCREEN_HEIGHT * 0.45;
Shared Values
const topSectionHeight = useSharedValue(DEFAULT_TOP_SECTION_HEIGHT); const isDragging = useSharedValue(false); const startY = useSharedValue(0);
Pan Gesture Logic
The pan gesture handles three phases:
Start: Records the initial height and sets dragging state to true Update: Calculates new height based on finger movement, constrained between min and max values End: Determines snap position based on velocity and current height, then animates to target
Snap Logic:
- High velocity (>800px/s): Snaps to next/previous position
- Low velocity: Snaps to whichever position is closest (minimized, default, or maximized)
Animated Styles Explained
1. Top Section Height Animation (topSectionAnimatedStyle
)
What it does: Controls the height of the top section in real-time
How it works: Directly maps the topSectionHeight
shared value to the height style
When it updates: Every time the user drags the handle or when snapping occurs
const topSectionAnimatedStyle = useAnimatedStyle(() => ({ height: topSectionHeight.value, // Direct height control }));
2. Bottom Section Height Animation (bottomSectionAnimatedStyle
)
What it does: Automatically adjusts the bottom section height to fill remaining space How it works: Calculates remaining height by subtracting top section height from total screen height When it updates: Automatically when top section height changes
const bottomSectionAnimatedStyle = useAnimatedStyle(() => ({ height: SCREEN_HEIGHT - HEADER_HEIGHT - topSectionHeight.value - 100, // This ensures bottom section always fills remaining space }));
Content Transition Animations (Optional)
If you want content to change based on section size, you can add these animations:
Compact Cards Animation
What it does: Shows/hides compact content when section is minimized How it works: Fades out and scales down as section expands
Expanded Cards Animation
What it does: Shows/hides expanded content when section is expanded How it works: Fades in and scales up as section expands
Three Snap Positions
- Minimized:
MIN_SECTION_HEIGHT
(100px) - Default:
DEFAULT_TOP_SECTION_HEIGHT
(45% of screen) - Maximized:
MAX_TOP_SECTION_HEIGHT
(70% of screen)
Velocity-Based Snapping
- High velocity swipe (> 800px/s): Snaps to next/previous position
- Low velocity: Snaps to nearest position based on current height
Height Thresholds
- VELOCITY_THRESHOLD: 800px/s
- HEIGHT_THRESHOLD: 50px
Animation Configuration
const ANIMATION_CONFIG = { damping: 25, // Controls bounce (lower = more bouncy) stiffness: 300, // Controls speed (higher = faster) mass: 0.8, // Controls inertia (higher = more inertia) overshootClamping: false, restDisplacementThreshold: 0.01, restSpeedThreshold: 0.01, };
Change Snap Positions
const MIN_SECTION_HEIGHT = 80; const DEFAULT_TOP_SECTION_HEIGHT = SCREEN_HEIGHT * 0.5; const MAX_TOP_SECTION_HEIGHT = SCREEN_HEIGHT * 0.8;
Adjust Animation Feel
const ANIMATION_CONFIG = { damping: 20, // More bouncy stiffness: 400, // Faster mass: 1.0, // More inertia };
Modify Velocity Thresholds
const VELOCITY_THRESHOLD = 600; // Lower threshold const HEIGHT_THRESHOLD = 30; // Smaller threshold
Key Dependencies
react-native-reanimated
: For shared values and animationsreact-native-gesture-handler
: For pan gesture handlingreact-native-safe-area-context
: For safe area handling