react-native-universal-keyboard-aware-scrollview 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +134 -242
  2. package/android/build.gradle +2 -17
  3. package/package.json +8 -14
  4. package/react-native-universal-keyboard-aware-scrollview.podspec +3 -12
  5. package/src/components/KeyboardAwareScrollView.tsx +193 -84
  6. package/src/hooks/useKeyboard.ts +78 -73
  7. package/android/app/build.gradle +0 -182
  8. package/android/app/debug.keystore +0 -0
  9. package/android/app/proguard-rules.pro +0 -14
  10. package/android/app/src/debug/AndroidManifest.xml +0 -7
  11. package/android/app/src/debugOptimized/AndroidManifest.xml +0 -7
  12. package/android/app/src/main/AndroidManifest.xml +0 -25
  13. package/android/app/src/main/java/com/anonymous/reactnativeuniversalkeyboardawarescrollview/MainActivity.kt +0 -61
  14. package/android/app/src/main/java/com/anonymous/reactnativeuniversalkeyboardawarescrollview/MainApplication.kt +0 -56
  15. package/android/app/src/main/res/drawable/ic_launcher_background.xml +0 -6
  16. package/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -37
  17. package/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
  18. package/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
  19. package/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
  20. package/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
  21. package/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
  22. package/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
  23. package/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
  24. package/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  25. package/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
  26. package/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  27. package/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  28. package/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
  29. package/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  30. package/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  31. package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
  32. package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  33. package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  34. package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
  35. package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  36. package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  37. package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
  38. package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  39. package/android/app/src/main/res/values/colors.xml +0 -6
  40. package/android/app/src/main/res/values/strings.xml +0 -5
  41. package/android/app/src/main/res/values/styles.xml +0 -11
  42. package/android/app/src/main/res/values-night/colors.xml +0 -1
  43. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  44. package/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  45. package/android/gradle.properties +0 -65
  46. package/android/gradlew +0 -251
  47. package/android/gradlew.bat +0 -94
  48. package/android/settings.gradle +0 -39
  49. package/ios/.xcode.env +0 -11
  50. package/ios/Podfile +0 -60
  51. package/ios/Podfile.lock +0 -2001
  52. package/ios/Podfile.properties.json +0 -5
  53. package/ios/reactnativeuniversalkeyboardawarescrollview/AppDelegate.swift +0 -70
  54. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  55. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/AppIcon.appiconset/Contents.json +0 -14
  56. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/Contents.json +0 -6
  57. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenBackground.colorset/Contents.json +0 -20
  58. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +0 -23
  59. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
  60. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
  61. package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
  62. package/ios/reactnativeuniversalkeyboardawarescrollview/Info.plist +0 -76
  63. package/ios/reactnativeuniversalkeyboardawarescrollview/PrivacyInfo.xcprivacy +0 -48
  64. package/ios/reactnativeuniversalkeyboardawarescrollview/SplashScreen.storyboard +0 -48
  65. package/ios/reactnativeuniversalkeyboardawarescrollview/Supporting/Expo.plist +0 -12
  66. package/ios/reactnativeuniversalkeyboardawarescrollview/reactnativeuniversalkeyboardawarescrollview-Bridging-Header.h +0 -3
  67. package/ios/reactnativeuniversalkeyboardawarescrollview/reactnativeuniversalkeyboardawarescrollview.entitlements +0 -5
  68. package/ios/reactnativeuniversalkeyboardawarescrollview.xcodeproj/project.pbxproj +0 -540
  69. package/ios/reactnativeuniversalkeyboardawarescrollview.xcodeproj/xcshareddata/xcschemes/reactnativeuniversalkeyboardawarescrollview.xcscheme +0 -88
  70. package/ios/reactnativeuniversalkeyboardawarescrollview.xcworkspace/contents.xcworkspacedata +0 -10
package/README.md CHANGED
@@ -2,57 +2,43 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/react-native-universal-keyboard-aware-scrollview.svg)](https://www.npmjs.com/package/react-native-universal-keyboard-aware-scrollview)
4
4
  [![license](https://img.shields.io/npm/l/react-native-universal-keyboard-aware-scrollview.svg)](https://github.com/AetherTechDev/react-native-universal-keyboard-aware-scrollview/blob/main/LICENSE)
5
- [![platform](https://img.shields.io/badge/platform-iOS%20%7C%20Android-lightgrey.svg)](https://reactnative.dev/)
6
5
 
7
6
  A **universal keyboard-aware ScrollView** for React Native that **works correctly** in:
8
7
  - ✅ Normal screens
9
8
  - ✅ React Native Modal
10
- - ✅ BottomSheet components
9
+ - ✅ BottomSheet components
10
+ - ✅ Multiline TextInput (textarea)
11
11
  - ✅ Any overlay or dialog scenario
12
12
 
13
- This package uses **native keyboard listeners** on both Android and iOS for reliable keyboard detection, solving the common issues that plague other keyboard-aware libraries.
13
+ ## 🔥 Platform Support
14
14
 
15
- ## 🎯 Why This Package?
16
-
17
- Existing keyboard-aware solutions often fail in these scenarios:
18
-
19
- | Issue | Other Libraries | This Package |
20
- |-------|----------------|--------------|
21
- | Keyboard overlaps input in Modal | ❌ | ✅ |
22
- | Glitchy animations on Android | ❌ | ✅ |
23
- | Incorrect height inside BottomSheet | ❌ | ✅ |
24
- | Race conditions with keyboard events | ❌ | ✅ |
25
- | Inconsistent behavior iOS vs Android | ❌ | ✅ |
26
-
27
- ### How We Solve These Issues
28
-
29
- 1. **Native Keyboard Detection**: Instead of relying solely on React Native's JavaScript keyboard events, we use platform-native APIs:
30
- - **iOS**: `UIKeyboardWillChangeFrameNotification` for accurate keyboard frame tracking
31
- - **Android**: `ViewTreeObserver.OnGlobalLayoutListener` with visible window frame measurement
32
-
33
- 2. **Modal-Aware Architecture**: Our native implementations measure keyboard height relative to the actual visible window, not the root activity/view controller. This means keyboard detection works correctly even when views are presented modally.
34
-
35
- 3. **Unified Event System**: Events are emitted from native → JavaScript through a consistent interface, eliminating timing issues and race conditions.
15
+ | Platform | Supported |
16
+ |----------|-----------|
17
+ | React Native CLI | |
18
+ | Expo (Development Build) | ✅ |
19
+ | Expo Go | (requires native code) |
36
20
 
37
21
  ## 📦 Installation
38
22
 
39
23
  ```bash
40
- # Using npm
41
24
  npm install react-native-universal-keyboard-aware-scrollview
42
-
43
- # Using yarn
44
- yarn add react-native-universal-keyboard-aware-scrollview
45
25
  ```
46
26
 
47
27
  ### iOS Setup
48
-
49
28
  ```bash
50
29
  cd ios && pod install
51
30
  ```
52
31
 
53
32
  ### Android Setup
33
+ No additional setup required - auto-linking handles everything.
54
34
 
55
- No additional setup required. The native module is automatically linked.
35
+ ### Expo (Development Build)
36
+ ```bash
37
+ npx expo prebuild
38
+ npx expo run:ios # or run:android
39
+ ```
40
+
41
+ > **Note:** This package uses native modules, so it won't work in Expo Go. Use a development build instead.
56
42
 
57
43
  ## 🚀 Quick Start
58
44
 
@@ -60,59 +46,57 @@ No additional setup required. The native module is automatically linked.
60
46
 
61
47
  ```tsx
62
48
  import React from 'react';
63
- import { TextInput, View, StyleSheet } from 'react-native';
49
+ import { TextInput, StyleSheet, SafeAreaView } from 'react-native';
64
50
  import { KeyboardAwareScrollView } from 'react-native-universal-keyboard-aware-scrollview';
65
51
 
66
- function MyForm() {
52
+ export default function App() {
67
53
  return (
68
- <KeyboardAwareScrollView style={styles.container}>
69
- <TextInput placeholder="Name" style={styles.input} />
70
- <TextInput placeholder="Email" style={styles.input} />
71
- <TextInput placeholder="Phone" style={styles.input} />
72
- <TextInput
73
- placeholder="Message"
74
- style={[styles.input, styles.multiline]}
75
- multiline
76
- />
77
- </KeyboardAwareScrollView>
54
+ <SafeAreaView style={styles.container}>
55
+ <KeyboardAwareScrollView style={styles.scroll}>
56
+ <TextInput placeholder="Name" style={styles.input} />
57
+ <TextInput placeholder="Email" style={styles.input} />
58
+ <TextInput placeholder="Phone" style={styles.input} />
59
+ <TextInput
60
+ placeholder="Message"
61
+ style={[styles.input, styles.multiline]}
62
+ multiline
63
+ numberOfLines={5}
64
+ />
65
+ </KeyboardAwareScrollView>
66
+ </SafeAreaView>
78
67
  );
79
68
  }
80
69
 
81
70
  const styles = StyleSheet.create({
82
- container: {
83
- flex: 1,
84
- },
71
+ container: { flex: 1 },
72
+ scroll: { flex: 1, padding: 16 },
85
73
  input: {
86
74
  borderWidth: 1,
87
75
  borderColor: '#ccc',
88
76
  borderRadius: 8,
89
77
  padding: 12,
90
78
  marginBottom: 16,
91
- marginHorizontal: 16,
92
- },
93
- multiline: {
94
- height: 100,
95
- textAlignVertical: 'top',
96
79
  },
80
+ multiline: { height: 120, textAlignVertical: 'top' },
97
81
  });
98
82
  ```
99
83
 
100
- ### Usage Inside Modal
84
+ ### Usage Inside Modal (The Key Feature!)
101
85
 
102
86
  ```tsx
103
87
  import React, { useState } from 'react';
104
- import { Modal, TextInput, View, Button, StyleSheet } from 'react-native';
88
+ import { Modal, TextInput, View, Button, StyleSheet, SafeAreaView } from 'react-native';
105
89
  import { KeyboardAwareScrollView } from 'react-native-universal-keyboard-aware-scrollview';
106
90
 
107
- function ModalForm() {
91
+ export default function ModalExample() {
108
92
  const [visible, setVisible] = useState(false);
109
93
 
110
94
  return (
111
95
  <>
112
- <Button title="Open Form" onPress={() => setVisible(true)} />
96
+ <Button title="Open Modal" onPress={() => setVisible(true)} />
113
97
 
114
98
  <Modal visible={visible} animationType="slide">
115
- <View style={styles.modalContainer}>
99
+ <SafeAreaView style={styles.container}>
116
100
  <KeyboardAwareScrollView
117
101
  insideModal={true}
118
102
  extraScrollHeight={20}
@@ -122,123 +106,111 @@ function ModalForm() {
122
106
  <TextInput
123
107
  placeholder="Message"
124
108
  style={[styles.input, styles.multiline]}
125
- multiline
109
+ multiline
110
+ numberOfLines={4}
126
111
  />
127
112
  </KeyboardAwareScrollView>
128
-
129
113
  <Button title="Close" onPress={() => setVisible(false)} />
130
- </View>
114
+ </SafeAreaView>
131
115
  </Modal>
132
116
  </>
133
117
  );
134
118
  }
119
+
120
+ const styles = StyleSheet.create({
121
+ container: { flex: 1 },
122
+ input: {
123
+ borderWidth: 1,
124
+ borderColor: '#ccc',
125
+ borderRadius: 8,
126
+ padding: 12,
127
+ margin: 16,
128
+ },
129
+ multiline: { height: 100, textAlignVertical: 'top' },
130
+ });
135
131
  ```
136
132
 
137
- ### Using the Hook Directly
133
+ ### Multiline TextInput (Textarea) Support
134
+
135
+ The component automatically detects multiline TextInputs and provides extra scroll space:
136
+
137
+ ```tsx
138
+ <KeyboardAwareScrollView
139
+ extraHeightForMultiline={100} // Extra space for multiline inputs
140
+ >
141
+ <TextInput
142
+ placeholder="Write your message..."
143
+ multiline
144
+ numberOfLines={6}
145
+ style={{ height: 150, textAlignVertical: 'top' }}
146
+ />
147
+ </KeyboardAwareScrollView>
148
+ ```
149
+
150
+ ### Using the Hook
138
151
 
139
152
  ```tsx
140
- import React from 'react';
141
- import { View, TextInput, StyleSheet } from 'react-native';
142
153
  import { useKeyboard } from 'react-native-universal-keyboard-aware-scrollview';
143
154
 
144
- function CustomKeyboardHandling() {
155
+ function MyComponent() {
145
156
  const {
146
157
  keyboardHeight,
147
158
  isKeyboardVisible,
148
159
  dismissKeyboard
149
- } = useKeyboard({
150
- onKeyboardDidShow: (event) => {
151
- console.log('Keyboard shown with height:', event.height);
152
- },
153
- onKeyboardDidHide: () => {
154
- console.log('Keyboard hidden');
155
- },
156
- });
160
+ } = useKeyboard();
157
161
 
158
162
  return (
159
- <View style={[styles.container, { paddingBottom: keyboardHeight }]}>
160
- <TextInput placeholder="Type here..." style={styles.input} />
163
+ <View style={{ paddingBottom: keyboardHeight }}>
164
+ <TextInput placeholder="Type here..." />
161
165
  {isKeyboardVisible && (
162
- <Button title="Dismiss Keyboard" onPress={dismissKeyboard} />
166
+ <Button title="Dismiss" onPress={dismissKeyboard} />
163
167
  )}
164
168
  </View>
165
169
  );
166
170
  }
167
171
  ```
168
172
 
169
- ## 📖 API Reference
170
-
171
- ### `KeyboardAwareScrollView`
172
-
173
- A ScrollView that automatically adjusts for the keyboard.
173
+ ## 📖 API
174
174
 
175
- #### Props
175
+ ### `KeyboardAwareScrollView` Props
176
176
 
177
177
  | Prop | Type | Default | Description |
178
178
  |------|------|---------|-------------|
179
- | `enableOnAndroid` | `boolean` | `true` | Enable keyboard handling on Android |
180
- | `enableOnIOS` | `boolean` | `true` | Enable keyboard handling on iOS |
181
- | `extraScrollHeight` | `number` | `20` | Extra scroll space above keyboard |
182
- | `extraHeight` | `number` | `75` | Extra space for focused element |
179
+ | `enableOnAndroid` | `boolean` | `true` | Enable on Android |
180
+ | `enableOnIOS` | `boolean` | `true` | Enable on iOS |
181
+ | `extraScrollHeight` | `number` | `20` | Extra scroll above keyboard |
182
+ | `extraHeight` | `number` | `75` | Extra space for focused input |
183
+ | `extraHeightForMultiline` | `number` | `100` | Extra space for multiline inputs |
184
+ | `insideModal` | `boolean` | `false` | **Set to true when inside a Modal** |
183
185
  | `enableAnimation` | `boolean` | `true` | Animate height changes |
184
- | `animationDuration` | `number` | `250` | Animation duration in ms |
185
- | `enableAutoScrollToFocused` | `boolean` | `true` | Auto-scroll when keyboard shows |
186
- | `resetScrollToCoords` | `{x, y} \| null` | `null` | Reset scroll position on keyboard hide |
187
- | `keyboardShouldPersistTaps` | `string` | `'handled'` | Keyboard dismiss behavior |
188
- | `insideModal` | `boolean` | `false` | Optimize for modal usage |
189
- | `enableKeyboardSpacer` | `boolean` | `true` | Use spacer view approach |
190
- | `useContentInset` | `boolean` | `true` (iOS) | Use content inset vs padding |
186
+ | `keyboardShouldPersistTaps` | `string` | `'handled'` | Tap behavior |
187
+ | `enableAutoScrollToFocused` | `boolean` | `true` | Auto-scroll to focused input |
191
188
 
192
- #### Ref Methods
189
+ ### `useKeyboard` Hook
193
190
 
194
191
  ```tsx
195
- const scrollViewRef = useRef<KeyboardAwareScrollViewRef>(null);
196
-
197
- // Available methods:
198
- scrollViewRef.current?.scrollTo({ x: 0, y: 100, animated: true });
199
- scrollViewRef.current?.scrollToEnd({ animated: true });
200
- scrollViewRef.current?.scrollToFocusedInput(inputRef);
201
- scrollViewRef.current?.getScrollResponder();
202
- scrollViewRef.current?.dismissKeyboard();
192
+ const {
193
+ keyboardHeight, // Current keyboard height (number)
194
+ isKeyboardVisible, // Is keyboard showing (boolean)
195
+ dismissKeyboard, // Function to dismiss keyboard
196
+ screenHeight, // Screen height
197
+ safeAreaBottom, // Safe area inset (iOS)
198
+ } = useKeyboard(options);
203
199
  ```
204
200
 
205
- ### `useKeyboard` Hook
206
-
207
- A hook for tracking keyboard state.
208
-
209
201
  #### Options
210
202
 
211
203
  ```tsx
212
- const keyboard = useKeyboard({
213
- enableOnAndroid: true, // Enable on Android
214
- enableOnIOS: true, // Enable on iOS
215
- useNativeEvents: true, // Use native module events
216
- animated: true, // Animate height changes
217
- onKeyboardWillShow: (e) => {}, // iOS only
218
- onKeyboardWillHide: (e) => {}, // iOS only
219
- onKeyboardDidShow: (e) => {},
220
- onKeyboardDidHide: (e) => {},
221
- onKeyboardHeightChange: (h) => {},
204
+ useKeyboard({
205
+ enableOnAndroid: true,
206
+ enableOnIOS: true,
207
+ onKeyboardDidShow: (event) => {},
208
+ onKeyboardDidHide: (event) => {},
209
+ onKeyboardHeightChange: (height) => {},
222
210
  });
223
211
  ```
224
212
 
225
- #### Return Value
226
-
227
- ```tsx
228
- interface UseKeyboardReturn {
229
- keyboardHeight: number; // Current keyboard height
230
- isKeyboardVisible: boolean; // Is keyboard visible
231
- isAnimating: boolean; // Is keyboard animating
232
- dismissKeyboard: () => Promise<void>;
233
- screenHeight: number; // Screen height
234
- animationDuration: number; // Animation duration
235
- safeAreaBottom: number; // Safe area inset (iOS)
236
- }
237
- ```
238
-
239
- ### `KeyboardController`
240
-
241
- Static utility class for keyboard control.
213
+ ### `KeyboardController` Utility
242
214
 
243
215
  ```tsx
244
216
  import { KeyboardController } from 'react-native-universal-keyboard-aware-scrollview';
@@ -246,142 +218,62 @@ import { KeyboardController } from 'react-native-universal-keyboard-aware-scroll
246
218
  // Dismiss keyboard
247
219
  await KeyboardController.dismiss();
248
220
 
249
- // Get keyboard height
221
+ // Get current height
250
222
  const height = await KeyboardController.getHeight();
251
223
 
252
- // Check visibility
253
- const isVisible = await KeyboardController.isVisible();
254
-
255
- // Check if native module is available
256
- const hasNative = KeyboardController.isNativeModuleAvailable();
224
+ // Check if visible
225
+ const visible = await KeyboardController.isVisible();
257
226
  ```
258
227
 
259
- ## 🔧 Expo Support
260
-
261
- This package works with Expo, but **requires a development build** (not Expo Go).
262
-
263
- ### Expo Bare Workflow
264
-
265
- Works out of the box. Install and use normally.
266
-
267
- ### Expo Managed Workflow
268
-
269
- 1. Install the package:
270
- ```bash
271
- npx expo install react-native-universal-keyboard-aware-scrollview
272
- ```
273
-
274
- 2. Create a development build:
275
- ```bash
276
- npx expo prebuild
277
- npx expo run:ios # or run:android
278
- ```
279
-
280
- 3. Use in your app as normal.
281
-
282
- > **Note**: This package contains native code, so it cannot run in Expo Go. You must use a development build or EAS Build.
283
-
284
- ## 🏗 Architecture
285
-
286
- ### Native Implementation Details
287
-
288
- #### Android (Kotlin)
228
+ ## 🎯 Why This Package?
289
229
 
290
- ```
291
- ┌─────────────────────────────────────┐
292
- │ UniversalKeyboardModule │
293
- ├─────────────────────────────────────┤
294
- │ ViewTreeObserver │
295
- │ OnGlobalLayoutListener │
296
- │ ↓ │
297
- │ getWindowVisibleDisplayFrame() │
298
- │ ↓ │
299
- │ Calculate keyboard height │
300
- │ ↓ │
301
- │ Emit via DeviceEventEmitter │
302
- └─────────────────────────────────────┘
303
- ```
230
+ | Problem | Other Libraries | This Package |
231
+ |---------|----------------|--------------|
232
+ | Keyboard overlaps input in Modal | ❌ | ✅ |
233
+ | Multiline TextInput not working | ❌ | ✅ |
234
+ | Android resize glitches | ❌ | ✅ |
235
+ | Inconsistent heights | ❌ | ✅ |
236
+ | Race conditions | ❌ | ✅ |
304
237
 
305
- The Android implementation uses `ViewTreeObserver.OnGlobalLayoutListener` to detect layout changes. When the keyboard appears, the visible display frame shrinks. We calculate the keyboard height by comparing the visible frame with the full screen height.
238
+ ### Technical Implementation
306
239
 
307
- This approach works in modals because we attach the listener to the content view, which is always affected by keyboard appearance regardless of the view hierarchy.
240
+ **iOS:** Uses `UIKeyboardWillChangeFrameNotification` for accurate keyboard frame detection.
308
241
 
309
- #### iOS (Objective-C)
242
+ **Android:** Uses `ViewTreeObserver.OnGlobalLayoutListener` to measure visible window frame.
310
243
 
311
- ```
312
- ┌─────────────────────────────────────┐
313
- │ UniversalKeyboard │
314
- ├─────────────────────────────────────┤
315
- │ NSNotificationCenter │
316
- │ UIKeyboardWillChangeFrameNotification│
317
- │ ↓ │
318
- │ Extract keyboard frame from userInfo│
319
- │ ↓ │
320
- │ Calculate height from screen bottom │
321
- │ ↓ │
322
- │ Emit via RCTEventEmitter │
323
- └─────────────────────────────────────┘
324
- ```
325
-
326
- The iOS implementation uses `UIKeyboardWillChangeFrameNotification`, which provides the most accurate keyboard frame information. This notification fires for all keyboard changes including interactive dismiss gestures.
327
-
328
- We calculate keyboard height as `screenHeight - keyboardFrame.origin.y`, which works correctly in all presentation contexts including modals.
244
+ Both approaches work correctly inside modals because they measure the actual visible area, not the root view.
329
245
 
330
246
  ## 🐛 Troubleshooting
331
247
 
332
- ### "Native module not found" Error
333
-
334
- This usually means the native module isn't linked:
335
-
336
- **iOS:**
337
- ```bash
338
- cd ios && pod install --repo-update
339
- ```
248
+ ### "Native module not found"
340
249
 
341
- **Android:**
342
- Clean and rebuild:
343
250
  ```bash
344
- cd android && ./gradlew clean
345
- cd .. && npx react-native run-android
346
- ```
347
-
348
- ### Keyboard still overlaps in Modal
251
+ # iOS
252
+ cd ios && pod install --repo-update && cd ..
349
253
 
350
- Make sure you set `insideModal={true}`:
351
-
352
- ```tsx
353
- <KeyboardAwareScrollView insideModal={true}>
354
- {/* content */}
355
- </KeyboardAwareScrollView>
254
+ # Android - clean build
255
+ cd android && ./gradlew clean && cd ..
356
256
  ```
357
257
 
358
- ### Animation is jumpy on Android
359
-
360
- Try adjusting the animation settings:
258
+ ### Expo Go not working
361
259
 
362
- ```tsx
363
- <KeyboardAwareScrollView
364
- enableAnimation={true}
365
- animationDuration={300}
366
- >
260
+ This package requires native code. Use:
261
+ ```bash
262
+ npx expo prebuild
263
+ npx expo run:ios # or npx expo run:android
367
264
  ```
368
265
 
369
- Or disable animation entirely:
266
+ ### Multiline TextInput not scrolling properly
370
267
 
268
+ Make sure you're using the `multiline` prop:
371
269
  ```tsx
372
- <KeyboardAwareScrollView enableAnimation={false}>
270
+ <TextInput
271
+ multiline // Required!
272
+ numberOfLines={4}
273
+ style={{ height: 100 }}
274
+ />
373
275
  ```
374
276
 
375
277
  ## 📄 License
376
278
 
377
279
  MIT © [Vijay Kishan](https://github.com/AetherTechDev)
378
-
379
- ## 🤝 Contributing
380
-
381
- Contributions are welcome! Please read our [contributing guidelines](CONTRIBUTING.md) first.
382
-
383
- ## 📞 Support
384
-
385
- - 🐛 [Report bugs](https://github.com/AetherTechDev/react-native-universal-keyboard-aware-scrollview/issues)
386
- - 💡 [Request features](https://github.com/AetherTechDev/react-native-universal-keyboard-aware-scrollview/issues)
387
- - 📧 Email: vijay@aethertech.dev
@@ -14,10 +14,6 @@ buildscript {
14
14
  }
15
15
  }
16
16
 
17
- def isNewArchitectureEnabled() {
18
- return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
- }
20
-
21
17
  apply plugin: 'com.android.library'
22
18
  apply plugin: 'kotlin-android'
23
19
 
@@ -32,14 +28,6 @@ android {
32
28
  defaultConfig {
33
29
  minSdkVersion safeExtGet('minSdkVersion', 21)
34
30
  targetSdkVersion safeExtGet('targetSdkVersion', 34)
35
- versionCode 1
36
- versionName "1.0"
37
-
38
- buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
39
- }
40
-
41
- buildFeatures {
42
- buildConfig true
43
31
  }
44
32
 
45
33
  buildTypes {
@@ -63,11 +51,7 @@ android {
63
51
 
64
52
  sourceSets {
65
53
  main {
66
- if (isNewArchitectureEnabled()) {
67
- java.srcDirs += ['src/newarch/java']
68
- } else {
69
- java.srcDirs += ['src/oldarch/java']
70
- }
54
+ java.srcDirs = ['src/main/java']
71
55
  }
72
56
  }
73
57
  }
@@ -85,5 +69,6 @@ repositories {
85
69
 
86
70
  dependencies {
87
71
  implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '1.9.10')}"
72
+ //noinspection GradleDynamicVersion
88
73
  implementation "com.facebook.react:react-android:+"
89
74
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-universal-keyboard-aware-scrollview",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A universal keyboard-aware ScrollView for React Native that works correctly in normal screens, modals, and bottom sheets on both Android and iOS",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -8,15 +8,17 @@
8
8
  "source": "src/index.ts",
9
9
  "files": [
10
10
  "src",
11
- "android",
12
- "ios",
11
+ "android/build.gradle",
12
+ "android/src",
13
+ "ios/UniversalKeyboard.h",
14
+ "ios/UniversalKeyboard.m",
13
15
  "react-native.config.js",
14
16
  "react-native-universal-keyboard-aware-scrollview.podspec",
15
- "README.md"
17
+ "README.md",
18
+ "LICENSE"
16
19
  ],
17
20
  "scripts": {
18
- "typescript": "tsc --noEmit",
19
- "lint": "eslint \"**/*.{js,ts,tsx}\""
21
+ "typescript": "tsc --noEmit"
20
22
  },
21
23
  "keywords": [
22
24
  "react-native",
@@ -47,14 +49,6 @@
47
49
  "react": ">=16.8.0",
48
50
  "react-native": ">=0.60.0"
49
51
  },
50
- "peerDependenciesMeta": {
51
- "react": {
52
- "optional": false
53
- },
54
- "react-native": {
55
- "optional": false
56
- }
57
- },
58
52
  "engines": {
59
53
  "node": ">=16"
60
54
  }
@@ -13,20 +13,11 @@ Pod::Spec.new do |s|
13
13
  DESC
14
14
  s.homepage = package['homepage']
15
15
  s.license = package['license']
16
- s.author = package['author']
16
+ s.author = { "Vijay Kishan" => "vijay@aethertech.dev" }
17
17
  s.platforms = { :ios => "12.0" }
18
- s.source = { :git => package['repository']['url'], :tag => "v#{s.version}" }
18
+ s.source = { :git => "https://github.com/AetherTechDev/react-native-universal-keyboard-aware-scrollview.git", :tag => "v#{s.version}" }
19
19
 
20
- s.source_files = "ios/**/*.{h,m,mm,swift}"
21
-
22
- # Use frameworks for Swift support
23
- s.pod_target_xcconfig = {
24
- 'DEFINES_MODULE' => 'YES',
25
- 'SWIFT_OPTIMIZATION_LEVEL' => '-Onone'
26
- }
27
-
28
- # React Native dependency
29
- install_modules_dependencies(s) if defined?(install_modules_dependencies)
20
+ s.source_files = "ios/*.{h,m}"
30
21
 
31
22
  s.dependency "React-Core"
32
23
  end