Skip to content

fix: enabled prop behavior for KeyboardStickyView (fabric arch)#1312

Merged
kirillzyusko merged 1 commit intomainfrom
fix/ksv-enabled-prop-fabric
Feb 21, 2026
Merged

fix: enabled prop behavior for KeyboardStickyView (fabric arch)#1312
kirillzyusko merged 1 commit intomainfrom
fix/ksv-enabled-prop-fabric

Conversation

@kirillzyusko
Copy link
Copy Markdown
Owner

@kirillzyusko kirillzyusko commented Feb 15, 2026

📜 Description

Fixed enabled behavior for KeyboardStickyView on fabric.

💡 Motivation and Context

It looks like on fabric when we detach animated value we "break" graph of native driver animations (sine we don't use Animated value anymore) and animations becomes just frozen (element is not getting moved to its initial position).

To overcome this problem I replaced plain closed value with Animated one:

Animated.add(Animated.multiply(height, 0), closed);

Effectively we still consume height, but we multiply it by 0 (we get 0 after that) and after that we make additions 0 + closed as animated value. As a result we always switch between animated values and we always use height value (it was critical, if we stop to use it we get again the same bug).

I used this code for testing:

import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Button, Text, View } from "react-native";
import {
  KeyboardAwareScrollView,
  KeyboardController,
  KeyboardStickyView,
} from "react-native-keyboard-controller";
import { useSafeAreaInsets } from "react-native-safe-area-context";

import TextInput from "../../../components/TextInput";

import { styles } from "./styles";

import type { ExamplesStackParamList } from "../../../navigation/ExamplesStack";
import type { StackScreenProps } from "@react-navigation/stack";
import type { LayoutChangeEvent } from "react-native";

type Props = StackScreenProps<ExamplesStackParamList>;

const variants = ["v1", "v2", "v3"] as const;

type Variant = (typeof variants)[number];

export default function AwareScrollViewStickyFooter({ navigation }: Props) {
  const { bottom } = useSafeAreaInsets();
  const [footerHeight, setFooterHeight] = useState(0);
  const [additionalHeight, setAdditionalHeight] = useState(0);
  const [variant, setVariant] = useState<Variant>("v1");

  const handleLayout = useCallback((evt: LayoutChangeEvent) => {
    setFooterHeight(evt.nativeEvent.layout.height);
  }, []);
  const offset = useMemo(
    () => ({ closed: 0, opened: variant === "v1" ? 0 : bottom }),
    [bottom, variant],
  );
  const offsetV3 = useMemo(
    () => ({ closed: -50, opened: bottom - 25 }),
    [bottom],
  );

  const [enabled, setEnabled] = useState(true);

  useEffect(() => {
    navigation.setOptions({
      headerRight: () => (
        <Text
          style={styles.header}
          onPress={() => {
            const index = variants.indexOf(variant);

            setVariant(variants[index === variants.length - 1 ? 0 : index + 1]);
          }}
        >
          {variant}
        </Text>
      ),
    });
  }, [variant]);

  const v1v2 = variant === "v1" || variant === "v2";

  return (
    <View
      style={[
        styles.pageContainer,
        { paddingBottom: variant === "v1" ? 0 : bottom },
      ]}
    >
      <KeyboardAwareScrollView
        bottomOffset={(v1v2 ? footerHeight + additionalHeight : 0) + 50}
        contentContainerStyle={styles.content}
        keyboardShouldPersistTaps="handled"
        style={styles.container}
        testID="aware_scroll_sticky_view_scroll_container"
      >
        {new Array(10).fill(0).map((_, i) => (
          <TextInput
            key={i}
            keyboardType={i % 2 === 0 ? "numeric" : "default"}
            placeholder={`TextInput#${i}`}
          />
        ))}
      </KeyboardAwareScrollView>
      {v1v2 && (
        <KeyboardStickyView offset={offset} enabled={enabled}>
          <View
            style={{ height: additionalHeight, backgroundColor: "magenta" }}
          />
          <View style={styles.footer} onLayout={handleLayout}>
            <Text style={styles.footerText}>A mocked sticky footer</Text>
            <TextInput placeholder="Amount" style={styles.inputInFooter} />
            <Button
              testID="toggle_height"
              title="Toggle height"
              onPress={() => {
                setEnabled(false);
                setTimeout(() => {
KeyboardController.dismiss();
                }, 500)
              }}
            />
          </View>
        </KeyboardStickyView>
      )}
      {variant === "v3" && (
        <KeyboardStickyView offset={offsetV3}>
          <View style={styles.circle} />
        </KeyboardStickyView>
      )}
    </View>
  );
}

📢 Changelog

JS

  • don't break animated graph;

🤔 How Has This Been Tested?

Tested in example project manually.

📸 Screenshots (if appropriate):

Before After
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-02-20.at.21.51.48.mp4
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-02-20.at.21.53.04.mp4

📝 Checklist

  • CI successfully passed
  • I added new mocks and corresponding unit-tests if library API was changed

@kirillzyusko kirillzyusko self-assigned this Feb 15, 2026
@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🏭 fabric Changes specific to new (fabric/jsi) architecture KeyboardStickyView 🩹 Anything related to KeyboardStickyView component labels Feb 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor

📊 Package size report

Current size Target Size Difference
243975 bytes 243663 bytes 312 bytes 📈

@kirillzyusko kirillzyusko marked this pull request as ready for review February 20, 2026 20:53
@kirillzyusko kirillzyusko merged commit d1fb606 into main Feb 21, 2026
16 checks passed
@kirillzyusko kirillzyusko deleted the fix/ksv-enabled-prop-fabric branch February 21, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working 🏭 fabric Changes specific to new (fabric/jsi) architecture KeyboardStickyView 🩹 Anything related to KeyboardStickyView component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant