You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native:KeyboardAvoidingView与SlidingUpPanel收起键盘后面板不归位

React Native滑动面板收起键盘后无法复位的解决方法

问题场景

我实现了一个快速登录界面,使用rn-sliding-up-panel组件作为密码登录面板。调用panelRef.current.show()显示面板时尺寸正常,但打开键盘后面板会上移,收起键盘后无法回到初始位置。

原组件代码

import { useEffect, useRef, useState } from "react";
import {
  KeyboardAvoidingView,
  Pressable,
  ScrollView,
  StyleSheet,
  TextInput,
  View,
} from "react-native";
import colors from "../config/colors";
import SlidingUpPanel from "rn-sliding-up-panel";
import Header from "../components/common/Header";
import Text from "../components/Text";
import PasswordIcon from "../assets/svg/Login/PasswordIcon";
import Separator from "../assets/svg/Login/Separator";
import Checkbox from "expo-checkbox";
import Button from "../components/common/Button";
import { useNavigation } from "@react-navigation/native";
import FaceID from "../assets/svg/QuickLogin/FaceID";
import InsrgramButton from "../assets/svg/QuickLogin/InsrgramButton";
import WhatsappButton from "../assets/svg/QuickLogin/WhatsappButton";
import XButton from "../assets/svg/QuickLogin/XButton";
import DefaultAvatar from "../assets/svg/QuickLogin/DefaultAvatar";
import * as LocalAuthentication from "expo-local-authentication";

const QuickLoginScreen = () => {
  const panelRef = useRef(null);
  const [isChecked, setChecked] = useState();
  const navigation = useNavigation();

  const draggableRange = {
    top: 320, // 完全展开的位置
    bottom: 0, // 收起位置
  };

  const handleFaceID = () => {
    LocalAuthentication.authenticateAsync({
      promptMessage: "تسجيل دخول لمرشحكم",
    })
      .then((r) => console.log(r))
      .catch((e) => console.log(e));
  };
  const handleLogin = () => {
    navigation.navigate("Home");
  };

  return (
    <View style={styles.container}>
      <Header title="تسجيل الدخول" />
      <View style={styles.welcomeTextContainer}>
        <View style={styles.avatarContainer}>
          <DefaultAvatar />
        </View>

        <Text size="14" style={{ marginTop: 16, marginBottom: 10 }} bold>
          أهلا بك
        </Text>
        <Text size="14" bold>
          هاشم عبدالله
        </Text>
      </View>
      <Pressable onPress={handleFaceID}>
        <FaceID />
      </Pressable>
      <View>
        <Pressable
          onPress={() => panelRef.current.show()}
          style={styles.passwordLoginContainer}
        >
          <Text size="12" style={styles.passwordLoginText} bold>
            أو قم بتسجيل الدخول باستخدام
          </Text>

          <Text size="12" style={{ color: colors.green, marginLeft: 4 }} bold>
            كلمة المرور
          </Text>
        </Pressable>
      </View>

      <View style={styles.socialLoginButtonsContainer}>
        <XButton />
        <InsrgramButton />
        <WhatsappButton />
      </View>
      <KeyboardAvoidingView
        behavior="height"
        style={{ flex: 1 }}
        keyboardVerticalOffset={Platform.OS === "ios" ? 0 : -500}
      >
        <SlidingUpPanel
          ref={panelRef}
          draggableRange={draggableRange}
          showBackdrop={false}
          initialAnimatedValue={0}
          containerStyle={styles.panelContainer}
        >
          <ScrollView contentContainerStyle={styles.panel}>
            <View style={styles.panelButton} />
            <View style={styles.panelContents}>
              <Text size="13" style={styles.enterPasswordText}>
                ادخل كلمة السر
              </Text>
              <View style={styles.textInputContainer}>
                <PasswordIcon />
                <Text size="10" style={styles.inputLabel}>
                  Pass
                </Text>
                <Separator />
                <TextInput
                  style={styles.textInput}
                  secureTextEntry={!isChecked}
                />
              </View>
              <View style={styles.showPasswordContainer}>
                <Text size="12">اظهار كلمة المرور</Text>
                <Checkbox
                  style={styles.checkbox}
                  value={isChecked}
                  onValueChange={setChecked}
                  color={isChecked ? colors.green : undefined}
                />
              </View>
              <View style={styles.linksContainer}>
                <Text
                  size="12"
                  style={{ ...styles.textLinks, marginBottom: 12 }}
                >
                  إستعادة كلمة المرور ؟
                </Text>
                <Text size="12" style={styles.textLinks}>
                  إستعادة اسم المستخدم ؟
                </Text>
              </View>

              <Button
                title="تسجيل الدخول"
                style={{ marginTop: 24 }}
                onPress={handleLogin}
              />
            </View>
          </ScrollView>
        </SlidingUpPanel>
      </KeyboardAvoidingView>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "white",
  },
  welcomeTextContainer: {
    justifyContent: "center",
    alignItems: "center",
    marginTop: 86,
  },
  avatarContainer: {
    justifyContent: "center",
    alignItems: "center",
    height: 104,
    width: 104,
    borderColor: colors.green,
    borderWidth: 2,
    borderRadius: 70,
  },
  passwordLoginContainer: {
    flexDirection: "row",
    justifyContent: "center",
    marginTop: 25,
    height: 30,
  },
  passwordLoginText: {
    textAlign: "center",
  },
  socialLoginButtonsContainer: {
    flexDirection: "row-reverse",
    justifyContent: "center",
    alignItems: "center",
    marginTop: 63,
  },

  panelContainer: {
    position: "absolute",
    bottom: 0,
    left: 0,
    right: 0,
    zIndex: 10,
    shadowColor: "#000",
    shadowOffset: {
      width: 0,
      height: 6,
    },
    shadowOpacity: 0.37,
    shadowRadius: 7.49,
    elevation: 12,
  },
  panel: {
    backgroundColor: "white",
    alignItems: "center",
    height: 320,
    borderTopRightRadius: 10,
    borderTopLeftRadius: 10,
  },
  panelContents: {
    marginTop: 19,
  },
  panelButton: {
    backgroundColor: colors.lgray4,
    width: 47,
    height: 5,
    marginTop: 8,
    borderRadius: 10,
  },
  enterPasswordText: {
    textAlign: "center",
    marginBottom: 16.2,
    color: colors.lgray5,
  },
  textInputContainer: {
    flexDirection: "row-reverse",
    alignItems: "center",
    borderColor: colors.lightGreen,
    borderWidth: 1,
    height: 48,
    width: 327,
    borderRadius: 8,
    marginBottom: 12,
  },
  textInput: {
    textAlign: "left",
    height: "100%",
    width: 150,
    marginRight: 10.79,
  },
  inputLabel: {
    color: colors.lgray1,
    marginRight: 12,
    marginLeft: 12.21,
  },
  showPasswordContainer: {
    flexDirection: "row",
    marginBottom: 12,
  },
  checkbox: {
    marginLeft: "auto",
    borderRadius: 5,
  },
  linksContainer: {},
  textLinks: {
    color: colors.lightGreen,
    textAlign: "left",
  },
});

export default QuickLoginScreen;

解决方法

1. 监听键盘收起事件,强制面板复位

添加Keyboard模块的监听,在键盘收起时将面板拉回初始位置:

  • 首先导入Keyboard:
import {
  KeyboardAvoidingView,
  Pressable,
  ScrollView,
  StyleSheet,
  TextInput,
  View,
  Keyboard, // 新增
} from "react-native";
  • 然后在组件内添加useEffect监听:
useEffect(() => {
  const keyboardHideListener = Keyboard.addListener('keyboardDidHide', () => {
    if (panelRef.current) {
      // 调用show()让面板回到完全展开的初始位置
      panelRef.current.show();
      // 或者用setAnimatedValue精准设置到draggableRange的bottom值
      // panelRef.current.setAnimatedValue(0);
    }
  });

  // 组件卸载时移除监听
  return () => keyboardHideListener.remove();
}, []);

2. 优化KeyboardAvoidingView配置

将behavior改为平台自适应,移除不合理的keyboardVerticalOffset负值:

<KeyboardAvoidingView
  behavior={Platform.OS === "ios" ? "padding" : "height"}
  style={{ flex: 1 }}
>

修改后的完整组件代码

import { useEffect, useRef, useState } from "react";
import {
  KeyboardAvoidingView,
  Pressable,
  ScrollView,
  StyleSheet,
  TextInput,
  View,
  Keyboard,
} from "react-native";
import colors from "../config/colors";
import SlidingUpPanel from "rn-sliding-up-panel";
import Header from "../components/common/Header";
import Text from "../components/Text";
import PasswordIcon from "../assets/svg/Login/PasswordIcon";
import Separator from "../assets/svg/Login/Separator";
import Checkbox from "expo-checkbox";
import Button from "../components/common/Button";
import { useNavigation } from "@react-navigation/native";
import FaceID from "../assets/svg/QuickLogin/FaceID";
import InsrgramButton from "../assets/svg/QuickLogin/InsrgramButton";
import WhatsappButton from "../assets/svg/QuickLogin/WhatsappButton";
import XButton from "../assets/svg/QuickLogin/XButton";
import DefaultAvatar from "../assets/svg/QuickLogin/DefaultAvatar";
import * as LocalAuthentication from "expo-local-authentication";

const QuickLoginScreen = () => {
  const panelRef = useRef(null);
  const [isChecked, setChecked] = useState();
  const navigation = useNavigation();

  const draggableRange = {
    top: 320, // 完全展开的位置
    bottom: 0, // 收起位置
  };

  useEffect(() => {
    const keyboardHideListener = Keyboard.addListener('keyboardDidHide', () => {
      if (panelRef.current) {
        panelRef.current.show();
      }
    });

    return () => keyboardHideListener.remove();
  }, []);

  const handleFaceID = () => {
    LocalAuthentication.authenticateAsync({
      promptMessage: "تسجيل دخول لمرشحكم",
    })
      .then((r) => console.log(r))
      .catch((e) => console.log(e));
  };
  const handleLogin = () => {
    navigation.navigate("Home");
  };

  return (
    <View style={styles.container}>
      <Header title="تسجيل الدخول" />
      <View style={styles.welcomeTextContainer}>
        <View style={styles.avatarContainer}>
          <DefaultAvatar />
        </View>

        <Text size="14" style={{ marginTop: 16, marginBottom: 10 }} bold>
          أهلا بك
        </Text>
        <Text size="14" bold>
          هاشم عبدالله
        </Text>
      </View>
      <Pressable onPress={handleFaceID}>
        <FaceID />
      </Pressable>
      <View>
        <Pressable
          onPress={() => panelRef.current.show()}
          style={styles.passwordLoginContainer}
        >
          <Text size="12" style={styles.passwordLoginText} bold>
            أو قم بتسجيل الدخول باستخدام
          </Text>

          <Text size="12" style={{ color: colors.green, marginLeft: 4 }} bold>
            كلمة المرور
          </Text>
        </Pressable>
      </View>

      <View style={styles.socialLoginButtonsContainer}>
        <XButton />
        <InsrgramButton />
        <WhatsappButton />
      </View>
      <KeyboardAvoidingView
        behavior={Platform.OS === "ios" ? "padding" : "height"}
        style={{ flex: 1 }}
      >
        <SlidingUpPanel
          ref={panelRef}
          draggableRange={draggableRange}
          showBackdrop={false}
          initialAnimatedValue={0}
          containerStyle={styles.panelContainer}
        >
          <ScrollView contentContainerStyle={styles.panel}>
            <View style={styles.panelButton} />
            <View style={styles.panelContents}>
              <Text size="13" style={styles.enterPasswordText}>
                ادخل كلمة السر
              </Text>
              <View style={styles.textInputContainer}>
                <PasswordIcon />
                <Text size="10" style={styles.inputLabel}>
                  Pass
                </Text>
                <Separator />
                <TextInput
                  style={styles.textInput}
                  secureTextEntry={!isChecked}
                />
              </View>
              <View style={styles.showPasswordContainer}>
                <Text size="12">اظهار كلمة المرور</Text>
                <Checkbox
                  style={styles.checkbox}
                  value={isChecked}
                  onValueChange={setChecked}
                  color={isChecked ? colors.green : undefined}
                />
              </View>
              <View style={styles.linksContainer}>
                <Text
                  size="12"
                  style={{ ...styles.textLinks, marginBottom: 12 }}
                >
                  إستعادة كلمة المرور ؟
                </Text>
                <Text size="12" style={styles.textLinks}>
                  إستعادة اسم المستخدم ؟
                </Text>
              </View>

              <Button
                title="تسجيل الدخول"
                style={{ marginTop: 24 }}
                onPress={handleLogin}
              />
            </View>
          </ScrollView>
        </SlidingUpPanel>
      </KeyboardAvoidingView>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "white",
  },
  welcomeTextContainer: {
    justifyContent: "center",
    alignItems: "center",
    marginTop: 86,
  },
  avatarContainer: {
    justifyContent: "center",
    alignItems: "center",
    height: 104,
    width: 104,
    borderColor: colors.green,
    borderWidth: 2,
    borderRadius: 70,
  },
  passwordLoginContainer: {
    flexDirection: "row",
    justifyContent: "center",
    marginTop: 25,
    height: 30,
  },
  passwordLoginText: {
    textAlign: "center",
  },
  socialLoginButtonsContainer: {
    flexDirection: "row-reverse",
    justifyContent: "center",
    alignItems: "center",
    marginTop: 63,
  },

  panelContainer: {
    position: "absolute",
    bottom: 0,
    left: 0,
    right: 0,
    zIndex: 10,
    shadowColor: "#000",
    shadowOffset: {
      width: 0,
      height: 6,
    },
    shadowOpacity: 0.37,
    shadowRadius: 7.49,
    elevation: 12,
  },
  panel: {
    backgroundColor: "white",
    alignItems: "center",
    height: 320,
    borderTopRightRadius: 10,
    borderTopLeftRadius: 10,
  },
  panelContents: {
    marginTop: 19,
  },
  panelButton: {
    backgroundColor: colors.lgray4,
    width: 47,
    height: 5,
    marginTop: 8,
    borderRadius: 10,
  },
  enterPasswordText: {
    textAlign: "center",
    marginBottom: 16.2,
    color: colors.l
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 10:00:08