# AvoidKeyboard

> A component that automatically adjusts its height to avoid keyboard overlap with smooth animations and cross-platform support.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/components/avoid-keyboard
- Markdown: https://ui.ahmedbna.com/docs/components/avoid-keyboard.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/avoid-keyboard.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/avoid-keyboard.json
- Install: `npx bna-ui add avoid-keyboard`
- npm dependencies: `react-native-reanimated`, `react-native-worklets`
- Registry dependencies: `useKeyboardHeight`
- Preview recording: https://demo.ahmedbna.com/0052-avoid-keyboard-demo.MP4

---

**Example:** Basic keyboard avoidance with animated height adjustment

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-demo.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight';
import React from 'react';

export function AvoidKeyboardDemo() {
  const { keyboardHeight, isKeyboardVisible, keyboardAnimationDuration } =
    useKeyboardHeight();

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text variant='title' style={{ marginBottom: 20 }}>
        Basic Keyboard Avoidance
      </Text>

      <Text variant='body' style={{ marginBottom: 30, opacity: 0.7 }}>
        Tap the input below to see the keyboard avoidance in action. The content
        will smoothly move up to keep the input visible.
      </Text>

      {/* Spacer to push input toward bottom */}

      <View style={{ flex: 1 }}>
        <Text>Keyboard Height: {keyboardHeight}</Text>
        <Text>Keyboard Visible: {isKeyboardVisible ? 'Yes' : 'No'}</Text>
        <Text>Animation Duration: {keyboardAnimationDuration}ms</Text>
      </View>

      <Input placeholder='Type your message here...' label='Message' />

      {/* This will create space to avoid the keyboard */}
      <AvoidKeyboard />
    </View>
  );
}
```

## Installation

### CLI

```bash
npx bna-ui add avoid-keyboard
```

### Manual

**1.** Install the following dependencies:

```bash
npx expo install react-native-reanimated
```

**2.** Copy and paste the following code into your project.

```tsx
// components/ui/avoid-keyboard.tsx
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight';
import { useEffect } from 'react';
import Animated, {
  Easing,
  useAnimatedStyle,
  useReducedMotion,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';

type Props = { offset?: number; duration?: number };

export const AvoidKeyboard = ({ offset = 0, duration = 0 }: Props) => {
  const { keyboardHeight, isKeyboardVisible, keyboardAnimationDuration } =
    useKeyboardHeight();
  const reduceMotion = useReducedMotion();

  // Shared value for the keyboard padding animation
  const keyboardValue = useSharedValue(0);

  // Update the shared value when keyboard height changes
  useEffect(() => {
    // Only add offset when keyboard is visible
    const targetHeight = isKeyboardVisible ? keyboardHeight + offset : 0;

    if (reduceMotion) {
      keyboardValue.value = targetHeight;
      return;
    }

    // Use different easing for show vs hide to match native behavior
    const easing = isKeyboardVisible
      ? Easing.out(Easing.quad) // Smooth out for keyboard show
      : Easing.in(Easing.quad); // Smooth in for keyboard hide

    keyboardValue.value = withTiming(targetHeight, {
      duration: keyboardAnimationDuration + duration,
      easing,
    });
  }, [
    keyboardHeight,
    keyboardAnimationDuration,
    isKeyboardVisible,
    offset,
    duration,
    reduceMotion,
  ]);

  // Animated style
  const keyboardMargin = useAnimatedStyle(() => {
    return {
      height: keyboardValue.value,
    };
  });

  return <Animated.View style={keyboardMargin} />;
};
```

**3.** Update the import paths to match your project setup.

## Usage

```tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
```

```tsx
<View style={{ flex: 1 }}>
  {/* Your content */}
  <TextInput placeholder='Type here...' />

  {/* This will push content up when keyboard appears */}
  <AvoidKeyboard />
</View>
```

## Examples

#### Basic Usage

**Example:** Simple keyboard avoidance with default settings

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-demo.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight';
import React from 'react';

export function AvoidKeyboardDemo() {
  const { keyboardHeight, isKeyboardVisible, keyboardAnimationDuration } =
    useKeyboardHeight();

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text variant='title' style={{ marginBottom: 20 }}>
        Basic Keyboard Avoidance
      </Text>

      <Text variant='body' style={{ marginBottom: 30, opacity: 0.7 }}>
        Tap the input below to see the keyboard avoidance in action. The content
        will smoothly move up to keep the input visible.
      </Text>

      {/* Spacer to push input toward bottom */}

      <View style={{ flex: 1 }}>
        <Text>Keyboard Height: {keyboardHeight}</Text>
        <Text>Keyboard Visible: {isKeyboardVisible ? 'Yes' : 'No'}</Text>
        <Text>Animation Duration: {keyboardAnimationDuration}ms</Text>
      </View>

      <Input placeholder='Type your message here...' label='Message' />

      {/* This will create space to avoid the keyboard */}
      <AvoidKeyboard />
    </View>
  );
}
```

#### With Offset

**Example:** Add extra spacing above the keyboard

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-offset.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';

export function AvoidKeyboardOffset() {
  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text variant='title' style={{ marginBottom: 20 }}>
        With Extra Offset
      </Text>

      <Text variant='body' style={{ marginBottom: 30, opacity: 0.7 }}>
        This example adds 40px of extra spacing above the keyboard for better
        visual separation.
      </Text>

      {/* Spacer to push input toward bottom */}
      <View style={{ flex: 1 }} />

      <Input
        placeholder='Notice the extra space above keyboard...'
        label='Message'
      />

      {/* Add 40px extra spacing above keyboard */}
      <AvoidKeyboard offset={40} />
    </View>
  );
}
```

#### Custom Duration

**Example:** Customize animation timing for different effects

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-duration.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function AvoidKeyboardDuration() {
  const [duration, setDuration] = useState(0);

  const durations = [
    { label: 'Default', value: 0 },
    { label: 'Fast (100ms)', value: 100 },
    { label: 'Slow (500ms)', value: 500 },
    { label: 'Very Slow (1000ms)', value: 1000 },
  ];

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text variant='title' style={{ marginBottom: 20 }}>
        Custom Animation Duration
      </Text>

      <Text variant='body' style={{ marginBottom: 20, opacity: 0.7 }}>
        Choose different animation speeds to see how it affects the keyboard
        avoidance:
      </Text>

      <View
        style={{
          flexDirection: 'row',
          flexWrap: 'wrap',
          gap: 8,
          marginBottom: 20,
        }}
      >
        {durations.map((item) => (
          <Button
            key={item.value}
            variant={duration === item.value ? 'default' : 'secondary'}
            size='sm'
            onPress={() => setDuration(item.value)}
          >
            {item.label}
          </Button>
        ))}
      </View>

      <Text variant='caption' style={{ marginBottom: 30, opacity: 0.6 }}>
        Current duration: {duration}ms extra
      </Text>

      {/* Spacer to push input toward bottom */}
      <View style={{ flex: 1 }} />

      <Input placeholder='Tap to test animation speed...' label='Test Input' />

      {/* Use custom duration */}
      <AvoidKeyboard duration={duration} />
    </View>
  );
}
```

#### Chat Interface

**Example:** Real-world chat interface example

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-chat.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { Send, SendHorizonal } from 'lucide-react-native';
import React, { useState } from 'react';
import { FlatList, Pressable } from 'react-native';

interface Message {
  id: string;
  text: string;
  isUser: boolean;
  timestamp: Date;
}

export function AvoidKeyboardChat() {
  const card = useColor('card');
  const blue = useColor('blue');

  const [messages, setMessages] = useState<Message[]>([
    {
      id: '1',
      text: 'Hey! How are you doing?',
      isUser: false,
      timestamp: new Date(Date.now() - 300000),
    },
    {
      id: '2',
      text: "Hi there! I'm doing great, thanks for asking!",
      isUser: true,
      timestamp: new Date(Date.now() - 240000),
    },
    {
      id: '3',
      text: "That's wonderful to hear! Any exciting plans for today?",
      isUser: false,
      timestamp: new Date(Date.now() - 180000),
    },
    {
      id: '4',
      text: "Actually yes! I'm working on some new React Native components.",
      isUser: true,
      timestamp: new Date(Date.now() - 120000),
    },
  ]);
  const [inputText, setInputText] = useState('');

  const sendMessage = () => {
    if (inputText.trim()) {
      const newMessage: Message = {
        id: Date.now().toString(),
        text: inputText.trim(),
        isUser: true,
        timestamp: new Date(),
      };
      setMessages((prev) => [...prev, newMessage]);
      setInputText('');

      // Simulate response after a delay
      setTimeout(() => {
        const responses = [
          'That sounds interesting!',
          'Tell me more about that.',
          "Cool! How's it going?",
          'Nice work!',
        ];
        const response: Message = {
          id: (Date.now() + 1).toString(),
          text: responses[Math.floor(Math.random() * responses.length)],
          isUser: false,
          timestamp: new Date(),
        };
        setMessages((prev) => [...prev, response]);
      }, 1000);
    }
  };

  const renderMessage = ({ item }: { item: Message }) => (
    <View
      style={{
        marginBottom: 12,
        alignItems: item.isUser ? 'flex-end' : 'flex-start',
      }}
    >
      <View
        style={{
          maxWidth: '80%',
          padding: 12,
          borderRadius: 16,
          backgroundColor: item.isUser ? blue : '#F2F2F7',
        }}
      >
        <Text
          style={{
            color: item.isUser ? 'white' : '#000',
            fontSize: 16,
          }}
        >
          {item.text}
        </Text>
        <Text
          style={{
            color: item.isUser ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.5)',
            fontSize: 12,
            marginTop: 4,
          }}
        >
          {item.timestamp.toLocaleTimeString([], {
            hour: '2-digit',
            minute: '2-digit',
          })}
        </Text>
      </View>
    </View>
  );

  return (
    <View style={{ flex: 1 }}>
      {/* Header */}
      <View
        style={{
          paddingHorizontal: 16,
          paddingBottom: 16,
        }}
      >
        <Text variant='title'>Chat Demo</Text>
        <Text variant='caption' style={{ opacity: 0.6, marginTop: 4 }}>
          Real-time chat with keyboard avoidance
        </Text>
      </View>

      {/* Messages */}
      <FlatList
        data={messages}
        renderItem={renderMessage}
        keyExtractor={(item) => item.id}
        style={{ flex: 1 }}
        contentContainerStyle={{ padding: 16 }}
        showsVerticalScrollIndicator={false}
      />

      {/* Input Area */}
      <View
        style={{
          flexDirection: 'row',
          padding: 16,
          gap: 12,
          backgroundColor: card,
        }}
      >
        <View style={{ flex: 1 }}>
          <Input
            value={inputText}
            onChangeText={setInputText}
            placeholder='Type a message...'
            variant='outline'
            onSubmitEditing={sendMessage}
            returnKeyType='send'
          />
        </View>
        <Button
          onPress={sendMessage}
          variant={inputText.trim() ? 'success' : 'outline'}
          size='icon'
        >
          <SendHorizonal size={20} color='white' />
        </Button>
      </View>

      {/* Keyboard avoidance with extra space for better UX */}
      <AvoidKeyboard />
    </View>
  );
}
```

#### Form Example

**Example:** Form with multiple inputs and keyboard avoidance

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-form.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { Mail, Lock, User, Phone, MessageSquare } from 'lucide-react-native';
import React, { useState } from 'react';
import { ScrollView } from 'react-native';

export function AvoidKeyboardForm() {
  const [formData, setFormData] = useState({
    first: '',
    last: '',
    email: '',
    phone: '',
    password: '',
    confrim: '',
    message: '',
  });

  const [errors, setErrors] = useState<Record<string, string>>({});

  const validateForm = () => {
    const newErrors: Record<string, string> = {};

    if (!formData.first.trim()) {
      newErrors.first = 'Name is required';
    }

    if (!formData.email.trim()) {
      newErrors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      newErrors.email = 'Please enter a valid email';
    }

    if (!formData.password.trim()) {
      newErrors.password = 'Password is required';
    } else if (formData.password.length < 6) {
      newErrors.password = 'Password must be at least 6 characters';
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = () => {
    if (validateForm()) {
      // Form is valid
      console.log('Form submitted:', formData);
      // Reset form
      setFormData({
        first: '',
        last: '',
        email: '',
        phone: '',
        password: '',
        confrim: '',
        message: '',
      });
      setErrors({});
    }
  };

  const updateField = (field: keyof typeof formData, value: string) => {
    setFormData((prev) => ({ ...prev, [field]: value }));
    // Clear error when user starts typing
    if (errors[field]) {
      setErrors((prev) => ({ ...prev, [field]: '' }));
    }
  };

  return (
    <View style={{ flex: 1 }}>
      {/* Header */}
      <View style={{ padding: 20 }}>
        <Text variant='title' style={{ marginBottom: 8 }}>
          Registration Form
        </Text>
        <Text variant='body' style={{ opacity: 0.7 }}>
          Fill out the form below. Notice how the keyboard avoidance keeps
          inputs visible.
        </Text>
      </View>

      {/* Form Content */}
      <ScrollView
        style={{ flex: 1 }}
        contentContainerStyle={{ padding: 20, gap: 16 }}
        keyboardShouldPersistTaps='handled'
      >
        <Input
          label='Frist Name'
          placeholder='Enter your first name'
          icon={User}
          value={formData.first}
          onChangeText={(value) => updateField('first', value)}
          error={errors.first}
        />

        <Input
          label='Last Name'
          placeholder='Enter your last name'
          icon={User}
          value={formData.last}
          onChangeText={(value) => updateField('last', value)}
          error={errors.last}
        />

        <Input
          label='Email'
          placeholder='Enter your email'
          icon={Mail}
          value={formData.email}
          onChangeText={(value) => updateField('email', value)}
          error={errors.email}
          keyboardType='email-address'
          autoCapitalize='none'
        />

        <Input
          label='Confirm'
          placeholder='Confirm your email'
          icon={Mail}
          value={formData.email}
          onChangeText={(value) => updateField('email', value)}
          error={errors.email}
          keyboardType='email-address'
          autoCapitalize='none'
        />

        <Input
          label='Phone'
          placeholder='Enter your phone number'
          icon={Phone}
          value={formData.phone}
          onChangeText={(value) => updateField('phone', value)}
          error={errors.phone}
          keyboardType='phone-pad'
        />

        <Input
          label='Password'
          placeholder='Create a password'
          icon={Lock}
          value={formData.password}
          onChangeText={(value) => updateField('password', value)}
          error={errors.password}
          secureTextEntry
        />

        <Input
          label='Confirm'
          placeholder='Confirm password'
          icon={Lock}
          value={formData.confrim}
          onChangeText={(value) => updateField('confrim', value)}
          error={errors.confrim}
          secureTextEntry
        />

        <Button onPress={handleSubmit} style={{ marginBottom: 20 }}>
          Create Account
        </Button>

        <Text variant='caption' style={{ textAlign: 'center', opacity: 0.6 }}>
          By creating an account, you agree to our Terms of Service and Privacy
          Policy.
        </Text>
      </ScrollView>

      {/* Keyboard avoidance for the form */}
      <AvoidKeyboard />
    </View>
  );
}
```

#### Playground

**Example:** Playground to test different configurations

```tsx
// components/demo/avoid-keyboard/avoid-keyboard-playground.tsx
import { AvoidKeyboard } from '@/components/ui/avoid-keyboard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight';
import { useColor } from '@/hooks/useColor';
import { Settings, Keyboard, Smartphone } from 'lucide-react-native';
import React, { useState } from 'react';
import { ScrollView, Switch } from 'react-native';

export function AvoidKeyboardPlayground() {
  const [message, setMessage] = useState('');
  const [offset, setOffset] = useState(20);
  const [duration, setDuration] = useState(0);
  const [showStats, setShowStats] = useState(false);

  const card = useColor('card');

  // Get keyboard stats for debugging
  const { keyboardHeight, isKeyboardVisible, keyboardAnimationDuration } =
    useKeyboardHeight();

  const presetOffsets = [0, 10, 20, 40, 60];
  const presetDurations = [0, 100, 250, 500];

  return (
    <View style={{ flex: 1 }}>
      {/* Controls */}
      <ScrollView
        style={{ flex: 1 }}
        contentContainerStyle={{ padding: 20 }}
        keyboardShouldPersistTaps='handled'
      >
        {/* Header */}
        <View
          style={{
            padding: 20,
          }}
        >
          <Text variant='title' style={{ marginBottom: 8 }}>
            AvoidKeyboard Playground
          </Text>
          <Text variant='body' style={{ opacity: 0.7 }}>
            Test different configurations and see real-time keyboard stats
          </Text>
        </View>

        {/* Debug Stats Toggle */}
        <View
          style={{
            flexDirection: 'row',
            alignItems: 'center',
            justifyContent: 'space-between',
            marginBottom: 20,
            padding: 16,
            backgroundColor: card,
            borderRadius: 8,
          }}
        >
          <View
            style={{
              flexDirection: 'row',
              alignItems: 'center',
              gap: 8,
            }}
          >
            <Settings size={16} color='#666' />
            <Text variant='body'>Show Keyboard Stats</Text>
          </View>
          <Switch value={showStats} onValueChange={setShowStats} />
        </View>

        {/* Keyboard Stats */}
        {showStats && (
          <View
            style={{
              marginBottom: 20,
              padding: 16,
              backgroundColor: card,
              borderRadius: 8,
            }}
          >
            <View
              style={{
                flexDirection: 'row',
                alignItems: 'center',
                marginBottom: 8,
                gap: 8,
              }}
            >
              <Keyboard
                size={16}
                color={isKeyboardVisible ? '#4CAF50' : '#666'}
              />
              <Text variant='subtitle' style={{ fontWeight: '600' }}>
                Keyboard Status
              </Text>
            </View>
            <Text variant='caption' style={{ marginBottom: 4 }}>
              Visible: {isKeyboardVisible ? '✅ Yes' : '❌ No'}
            </Text>
            <Text variant='caption' style={{ marginBottom: 4 }}>
              Height: {keyboardHeight}px
            </Text>
            <Text variant='caption'>
              Animation Duration: {keyboardAnimationDuration}ms
            </Text>
          </View>
        )}

        {/* Offset Controls */}
        <View style={{ marginBottom: 20 }}>
          <Text
            variant='subtitle'
            style={{ marginBottom: 12, fontWeight: '600' }}
          >
            Offset Configuration
          </Text>
          <Text variant='caption' style={{ marginBottom: 12, opacity: 0.7 }}>
            Extra space above keyboard: {offset}px
          </Text>

          <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
            {presetOffsets.map((value) => (
              <Button
                key={value}
                size='sm'
                variant={offset === value ? 'default' : 'secondary'}
                onPress={() => setOffset(value)}
              >
                {`${value}px`}
              </Button>
            ))}
          </View>
        </View>

        {/* Duration Controls */}
        <View style={{ marginBottom: 20 }}>
          <Text
            variant='subtitle'
            style={{ marginBottom: 12, fontWeight: '600' }}
          >
            Animation Duration
          </Text>
          <Text variant='caption' style={{ marginBottom: 12, opacity: 0.7 }}>
            Extra animation time: {duration}ms
          </Text>
          <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>
            {presetDurations.map((value) => (
              <Button
                key={value}
                variant={duration === value ? 'default' : 'secondary'}
                size='sm'
                onPress={() => setDuration(value)}
              >
                {value === 0 ? 'Default' : `${value}ms`}
              </Button>
            ))}
          </View>
        </View>

        {/* Usage Examples */}
        <View style={{ marginBottom: 20 }}>
          <Text
            variant='subtitle'
            style={{ marginBottom: 12, fontWeight: '600' }}
          >
            Code Example
          </Text>
          <View
            style={{
              padding: 16,
              backgroundColor: card,
              borderRadius: 8,
            }}
          >
            <Text style={{ fontFamily: 'monospace' }}>
              {`<AvoidKeyboard${offset > 0 ? ` offset={${offset}}` : ''}${
                duration > 0 ? ` duration={${duration}}` : ''
              } />`}
            </Text>
          </View>
        </View>

        {/* Spacer to push test input to bottom */}
        <View style={{ height: 100 }} />
      </ScrollView>

      {/* Test Input Area */}
      <View
        style={{
          padding: 20,
          backgroundColor: card,
          shadowColor: '#000',
          shadowOffset: { width: 0, height: -2 },
          shadowOpacity: 0.1,
          shadowRadius: 4,
          elevation: 8,
        }}
      >
        <View
          style={{
            flexDirection: 'row',
            alignItems: 'center',
            marginBottom: 12,
            gap: 8,
          }}
        >
          <Smartphone size={16} color='#666' />
          <Text variant='subtitle' style={{ fontWeight: '600' }}>
            Test Input
          </Text>
        </View>

        <Input
          value={message}
          onChangeText={setMessage}
          placeholder='Tap here to test keyboard avoidance...'
          variant='outline'
          type='textarea'
          rows={3}
        />

        <View style={{ flexDirection: 'row', gap: 8, marginTop: 12 }}>
          <Button
            variant='secondary'
            size='sm'
            onPress={() => setMessage('')}
            style={{ flex: 1 }}
          >
            Clear
          </Button>
          <Button
            size='sm'
            onPress={() =>
              setMessage(
                'This is a test message to see how the keyboard avoidance works with different configurations!'
              )
            }
            style={{ flex: 1 }}
          >
            Fill Text
          </Button>
        </View>
      </View>

      {/* The actual AvoidKeyboard component */}
      <AvoidKeyboard offset={offset} duration={duration} />
    </View>
  );
}
```

## API Reference

### AvoidKeyboard

Automatically adjusts height to avoid keyboard overlap with smooth animations.

| Prop       | Type     | Default | Description                                   |
| ---------- | -------- | ------- | --------------------------------------------- |
| `offset`   | `number` | `0`     | Additional spacing above the keyboard (in px) |
| `duration` | `number` | `0`     | Extra animation duration (in ms)              |

## Features

- **Cross-platform support**: Works on both iOS and Android
- **Smooth animations**: Uses Reanimated for 60fps animations
- **Smart timing**: Matches native keyboard animation duration
- **Flexible offset**: Add extra spacing as needed
- **Automatic cleanup**: Handles component unmounting gracefully
- **Screen rotation**: Adapts to orientation changes

## How It Works

The component uses the `useKeyboardHeight` hook to:

1. **Listen to keyboard events**: Tracks show/hide events on both platforms
2. **Measure keyboard height**: Gets accurate height measurements
3. **Animate smoothly**: Uses platform-appropriate easing curves
4. **Handle edge cases**: Manages screen rotation and invalid values

## Platform Differences

### iOS

- Uses `keyboardWillShow/Hide` for smoother animations
- Provides animation duration in keyboard events
- Better landscape keyboard height detection

### Android

- Uses `keyboardDidShow/Hide` events
- Falls back to default animation duration
- Handles software keyboard variations

## Best Practices

### Placement

```tsx
// ✅ Good - Place at bottom of your layout
<View style={{ flex: 1 }}>
  <ScrollView>
    {/* Your content */}
  </ScrollView>
  <TextInput />
  <AvoidKeyboard offset={20} />
</View>

// ❌ Avoid - Don't place in middle of content
<View>
  <TextInput />
  <AvoidKeyboard />
  <View>{/* More content */}</View>
</View>
```

### With ScrollView

```tsx
// ✅ Recommended pattern
<View style={{ flex: 1 }}>
  <ScrollView
    contentContainerStyle={{ flexGrow: 1 }}
    keyboardShouldPersistTaps='handled'
  >
    {/* Your content */}
  </ScrollView>
  <View style={{ padding: 16 }}>
    <TextInput />
  </View>
  <AvoidKeyboard offset={16} />
</View>
```

### Multiple Inputs

```tsx
// ✅ Single AvoidKeyboard for multiple inputs
<View style={{ flex: 1 }}>
  <TextInput placeholder='Name' />
  <TextInput placeholder='Email' />
  <TextInput placeholder='Message' />
  <AvoidKeyboard offset={20} />
</View>
```

### Performance

- Use a single `AvoidKeyboard` per screen
- Avoid nesting multiple instances
- Consider using `offset` instead of margin for spacing

## Troubleshooting

### Common Issues

**Keyboard not detected:**

- Ensure React Native Reanimated is properly installed
- Check if `useKeyboardHeight` hook is working correctly

**Animation feels choppy:**

- Verify Reanimated 2+ is installed
- Check if Hermes is enabled (recommended)

**Wrong height on Android:**

- Some Android keyboards report incorrect heights
- Consider using a small `offset` as buffer

**Landscape mode issues:**

- Component handles basic landscape detection
- For complex cases, listen to orientation changes

### Debug Mode

```tsx
// Add this for debugging
const { keyboardHeight, isKeyboardVisible } = useKeyboardHeight();

console.log('Keyboard:', { keyboardHeight, isKeyboardVisible });
```

## Accessibility

The AvoidKeyboard component enhances accessibility by:

- **Preventing content hiding**: Ensures form inputs remain visible
- **Maintaining focus**: Keeps focused elements in view
- **Supporting assistive tech**: Works with screen readers and voice control
- **Respecting user settings**: Honors system animation preferences

## Integration with Other Libraries

### React Navigation

```tsx
// Works seamlessly with React Navigation
function ChatScreen() {
  return (
    <View style={{ flex: 1 }}>
      <FlatList data={messages} />
      <TextInput />
      <AvoidKeyboard />
    </View>
  );
}
```

### KeyboardAvoidingView Alternative

```tsx
// Replace KeyboardAvoidingView with AvoidKeyboard
// ❌ Old way
<KeyboardAvoidingView behavior="padding">
  <TextInput />
</KeyboardAvoidingView>

// ✅ New way
<View style={{ flex: 1 }}>
  <TextInput />
  <AvoidKeyboard />
</View>
```

This component provides a more reliable and smoother alternative to React Native's built-in `KeyboardAvoidingView` with better cross-platform consistency.
