diff --git a/FabricExample/src/screens/Examples/ReanimatedChatFlatList/index.tsx b/FabricExample/src/screens/Examples/ReanimatedChatFlatList/index.tsx
index 64e801064b..f58e291326 100644
--- a/FabricExample/src/screens/Examples/ReanimatedChatFlatList/index.tsx
+++ b/FabricExample/src/screens/Examples/ReanimatedChatFlatList/index.tsx
@@ -7,7 +7,10 @@ import {
TouchableOpacity,
View,
} from "react-native";
-import { KeyboardAvoidingView } from "react-native-keyboard-controller";
+import {
+ KeyboardAvoidingView,
+ KeyboardLayout,
+} from "react-native-keyboard-controller";
import Message from "../../../components/Message";
import { history } from "../../../components/Message/data";
@@ -30,31 +33,33 @@ function ReanimatedChatFlatList() {
return (
<>
-
-
- ref.current?.scrollToEnd()}
- >
-
- ↑
-
-
+
+
+
+
+ ref.current?.scrollToEnd()}
+ >
+
+ ↑
+
+
>
);
diff --git a/package.json b/package.json
index d6575ff572..c76394c24d 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
"registry": "https://registry.npmjs.org/"
},
"dependencies": {
+ "react-freeze": "^1.0.4",
"react-native-is-edge-to-edge": "^1.2.1"
},
"devDependencies": {
diff --git a/src/components/KeyboardLayout/index.tsx b/src/components/KeyboardLayout/index.tsx
new file mode 100644
index 0000000000..b47a2eb233
--- /dev/null
+++ b/src/components/KeyboardLayout/index.tsx
@@ -0,0 +1,111 @@
+import React, { useCallback, useEffect, useState } from "react";
+import { Freeze } from "react-freeze";
+import { StyleSheet, View } from "react-native";
+
+import { KeyboardEvents } from "../../bindings";
+
+// Incomplete type, all accessible properties available at:
+// react-native/Libraries/Components/View/фReactNativeViewViewConfig.js
+interface ViewConfig extends View {
+ viewConfig: {
+ validAttributes: {
+ style: {
+ display: boolean | null;
+ };
+ };
+ };
+ _viewConfig: {
+ validAttributes: {
+ style: {
+ display: boolean | null;
+ };
+ };
+ };
+}
+
+/**
+ * A component that skips rendering its children when the keyboard is animating.
+ * Skipping these updates can help to deliver smoother animations.
+ *
+ * @param props - Properties of the component.
+ * @param props.children - Children of the component.
+ * @returns Children that skips rendering while keyboard is animating.
+ * @example
+ * ```
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+const KeyboardLayout = (props: { children: React.ReactNode }) => {
+ const { children } = props;
+ const [isFrozen, setFrozen] = useState(false);
+
+ const handleRef = useCallback((ref: ViewConfig) => {
+ // Workaround is necessary to prevent React Native from hiding frozen screens.
+ // See this PR: https://github.com/grahammendick/navigation/pull/860
+ if (ref?.viewConfig?.validAttributes?.style) {
+ ref.viewConfig.validAttributes.style = {
+ ...ref.viewConfig.validAttributes.style,
+ display: null,
+ };
+ } else if (ref?._viewConfig?.validAttributes?.style) {
+ ref._viewConfig.validAttributes.style = {
+ ...ref._viewConfig.validAttributes.style,
+ display: null,
+ };
+ }
+ }, []);
+
+ useEffect(() => {
+ const willShowListener = KeyboardEvents.addListener(
+ "keyboardWillShow",
+ () => {
+ setFrozen(true);
+ },
+ );
+ const didHideListener = KeyboardEvents.addListener(
+ "keyboardDidHide",
+ () => {
+ setFrozen(false);
+ },
+ );
+ const willHideListener = KeyboardEvents.addListener(
+ "keyboardWillHide",
+ () => {
+ setFrozen(true);
+ },
+ );
+ const didShowListener = KeyboardEvents.addListener(
+ "keyboardDidShow",
+ () => {
+ setFrozen(false);
+ },
+ );
+
+ return () => {
+ willShowListener.remove();
+ didHideListener.remove();
+ willHideListener.remove();
+ didShowListener.remove();
+ };
+ }, []);
+
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ flex: {
+ flex: 1,
+ },
+});
+
+export default KeyboardLayout;
diff --git a/src/components/index.ts b/src/components/index.ts
index e868519b32..de9ad75d52 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -5,6 +5,7 @@ export {
default as KeyboardToolbar,
DefaultKeyboardToolbarTheme,
} from "./KeyboardToolbar";
+export { default as KeyboardLayout } from "./KeyboardLayout";
export type { KeyboardAvoidingViewProps } from "./KeyboardAvoidingView";
export type { KeyboardStickyViewProps } from "./KeyboardStickyView";
export type { KeyboardAwareScrollViewProps } from "./KeyboardAwareScrollView";
diff --git a/src/index.ts b/src/index.ts
index 9093708b5e..8798cdce3e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,6 +7,7 @@ export * from "./module";
export * from "./types";
export {
+ KeyboardLayout,
KeyboardAvoidingView,
KeyboardStickyView,
KeyboardAwareScrollView,
diff --git a/yarn.lock b/yarn.lock
index 43a2c7dbcb..99544c90c8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7330,6 +7330,11 @@ react-error-boundary@^3.1.0:
dependencies:
"@babel/runtime" "^7.12.5"
+react-freeze@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.4.tgz#cbbea2762b0368b05cbe407ddc9d518c57c6f3ad"
+ integrity sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==
+
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"