Back to Components

Resizable Split View

Screen Views
7 months ago

About

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

tsx
<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

tsx
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

tsx
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

tsx
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

tsx
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

  1. Minimized: MIN_SECTION_HEIGHT (100px)
  2. Default: DEFAULT_TOP_SECTION_HEIGHT (45% of screen)
  3. 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

tsx
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

tsx
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

tsx
const ANIMATION_CONFIG = { damping: 20, // More bouncy stiffness: 400, // Faster mass: 1.0, // More inertia };

Modify Velocity Thresholds

tsx
const VELOCITY_THRESHOLD = 600; // Lower threshold const HEIGHT_THRESHOLD = 30; // Smaller threshold

Key Dependencies

  • react-native-reanimated: For shared values and animations
  • react-native-gesture-handler: For pan gesture handling
  • react-native-safe-area-context: For safe area handling

Posted by

E

eren

@eren