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

Expo转裸React Native后QR Scanner无法打开WebView及显示覆盖层求助

解决React Native QR Code Scanner迁移后的扫描覆盖层与WebView问题

刚从Expo迁到裸React Native环境确实容易遇到这类依赖适配的问题,我帮你梳理下代码里的几个关键问题,咱们一步步来修复:

一、扫描覆盖层显示异常的修复

首先是权限请求和扫描框布局的冲突问题:

  1. 权限请求适配:你用了QRCodeScanner.requestPermissionsAsync(),但react-native-qrcode-scanner底层依赖的是react-native-camera(RNCamera),裸RN环境下应该用RNCamera的权限请求方法,替换原来的useEffect:
useEffect(() => {
  (async () => {
    // 改用RNCamera的相机权限请求
    const { status } = await RNCamera.requestCameraPermissionsAsync();
    setHasPermission(status === 'granted');
  })();
}, []);
  1. 扫描框布局冲突:默认情况下QRCodeScanner会自带扫描框,你自定义的边框组件和它重叠导致显示异常。咱们可以关闭默认扫描框,用customMarker属性传入你的自定义覆盖层:
<QRCodeScanner
  onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
  showMarker={false} // 关闭默认扫描框
  cameraStyle={{ flex: 1 }} // 让相机占满全屏
  customMarker={
    <View style={{ marginBottom: 100 }}>
      <View style={{ alignItems: 'center', marginBottom: 5 }}>
        <Image
          style={{
            width: 100,
            height: 100,
            resizeMode: 'contain',
            marginBottom: 20,
          }}
          source={{ uri: 'omitted' }}
        />
        <Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold', paddingBottom: 10}}>
          QR Code Reader
        </Text>
      </View>
      <View
        style={{
          borderColor: 'white',
          borderTopWidth: 5,
          borderBottomWidth: 5,
          borderLeftWidth: 1,
          borderRightWidth: 1,
          paddingVertical: 80,
          paddingHorizontal: 100,
        }}
      />
      <View style={{ alignItems: 'center', marginTop: 5 }}>
        <Text style={{ color: 'white', fontSize: 15}}>
          QR Scan...
        </Text>
      </View>
    </View>
  }
/>

二、WebView无法打开URL的修复

这里主要是URL格式处理和状态类型不匹配的问题:

  1. 初始状态修正:把uri的初始值改成null,避免初始类型不匹配导致WebView加载异常:
const [uri, setUri] = useState(null);
  1. URL格式补全:扫描到的URL如果没有http/https前缀,WebView无法识别为有效链接,同时咱们直接存储字符串URL,不用嵌套对象:
const handleBarCodeScanned = ({ type, data }) => {
  setScanned(true);
  setModalVisible(true);
  // 自动补全URL协议前缀
  let validUrl = data;
  if (!validUrl.startsWith('http://') && !validUrl.startsWith('https://')) {
    validUrl = `https://${validUrl}`;
  }
  setUri(validUrl);
};
  1. Modal与WebView配置调整:把Modal初始可见性设为false(一开始应该显示扫描界面),同时给WebView添加错误监听方便调试:
// 初始状态修改
const [modalVisible, setModalVisible] = useState(false);

// WebView部分
<WebView
  style={{ flex: 1 }}
  source={{ uri: uri }}
  // 加载错误监听,方便排查问题
  onError={(syntheticEvent) => {
    const { nativeEvent } = syntheticEvent;
    console.warn('WebView加载错误:', nativeEvent);
  }}
/>

三、原生依赖配置检查

裸RN环境下要确保原生权限配置正确:

  • iOS:在Info.plist中添加相机权限描述:
<key>NSCameraUsageDescription</key>
<string>需要访问相机扫描二维码</string>
  • Android:在AndroidManifest.xml中添加相机权限:
<uses-permission android:name="android.permission.CAMERA" />

同时确认react-native-camera和react-native-qrcode-scanner的版本与你的RN版本兼容,RN 0.60+会自动链接,但如果是老版本需要手动链接原生依赖。

修复后的完整代码

import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button, Modal, TouchableHighlight, Image } from 'react-native';
import { WebView } from 'react-native-webview';
import QRCodeScanner from 'react-native-qrcode-scanner';
import { RNCamera } from 'react-native-camera';

export default function App(){
  const [hasPermission, setHasPermission] = useState(null);
  const [scanned, setScanned] = useState(false); // 初始设为false,允许扫描
  const [modalVisible, setModalVisible] = useState(false); // 初始不显示Modal
  const [uri, setUri] = useState(null);

  useEffect(() => {
    (async () => {
      const { status } = await RNCamera.requestCameraPermissionsAsync();
      setHasPermission(status === 'granted');
    })();
  }, []);

  const handleBarCodeScanned = ({ type, data }) => {
    setScanned(true);
    setModalVisible(true);
    // 处理URL协议前缀
    let validUrl = data;
    if (!validUrl.startsWith('http://') && !validUrl.startsWith('https://')) {
      validUrl = `https://${validUrl}`;
    }
    setUri(validUrl);
  };

  if (hasPermission === false) {
    return <Text>No access to camera</Text>;
  }

  return (
    <View style={{ flex: 1, flexDirection: 'column' }}>
      <Modal
        animationType="slide"
        transparent={false}
        visible={modalVisible}
        onRequestClose={() => {
          setModalVisible(false);
          setScanned(false);
        }}>
        <View style={{ flex: 1 }}>
          {uri && (
            <WebView
              style={{ flex: 1 }}
              source={{ uri: uri }}
              onError={(syntheticEvent) => {
                const { nativeEvent } = syntheticEvent;
                console.warn('WebView加载错误:', nativeEvent);
              }}
            />
          )}
          <TouchableHighlight
            style={{
              backgroundColor:'black',
              padding: 15,
              alignItems: 'center'
            }}
            onPress={() => {
              setModalVisible(false);
              setScanned(false);
            }}
            underlayColor='slategray'
          >
            <Text style={{ color:'white', fontSize: 15 }}>Re Scan</Text>
          </TouchableHighlight>
        </View>
      </Modal>

      {hasPermission && (
        <QRCodeScanner
          onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
          showMarker={false}
          cameraStyle={{ flex: 1 }}
          customMarker={
            <View style={{ marginBottom: 100 }}>
              <View style={{ alignItems: 'center', marginBottom: 5 }}>
                <Image
                  style={{
                    width: 100,
                    height: 100,
                    resizeMode: 'contain',
                    marginBottom: 20,
                  }}
                  source={{ uri: 'omitted' }}
                />
                <Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold', paddingBottom: 10}}>
                  QR Code Reader
                </Text>
              </View>
              <View
                style={{
                  borderColor: 'white',
                  borderTopWidth: 5,
                  borderBottomWidth: 5,
                  borderLeftWidth: 1,
                  borderRightWidth: 1,
                  paddingVertical: 80,
                  paddingHorizontal: 100,
                }}
              />
              <View style={{ alignItems: 'center', marginTop: 5 }}>
                <Text style={{ color: 'white', fontSize: 15}}>
                  QR Scan...
                </Text>
              </View>
            </View>
          }
        />
      )}
    </View>
  );
}

内容的提问来源于stack exchange,提问作者Medic305

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 10:31:14