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

如何在Expo SDK 51.0.0中使用expo-camera开启手电筒?

在Expo SDK 51中使用expo-camera启用手电筒的解决方案

问题背景

我正在尝试在基于Expo SDK 51.0.0的应用中,使用expo-camera包启用手电筒(torch)。

环境信息

  • Expo SDK版本:51.0.0
  • 平台:Android、iOS
  • 依赖项:
    • expo-camera
    • react-native
    • @react-native-async-storage/async-storage
    • @react-navigation/native

当前实现代码

import { CameraView, useCameraPermissions } from 'expo-camera';
import React, { useState, useContext, useCallback } from 'react';
import { Button, Text, TouchableOpacity, View } from 'react-native';
import { AppContext } from "../../context/AppContext";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useFocusEffect } from '@react-navigation/native';
import styles from './ScanEnterGTINScreenStyles';

const ScanEnterGTINScreen = ({ navigation }) => {
  const [permission, requestPermission] = useCameraPermissions();
  const [scannedData, setScannedData] = useState(null);
  const [isScanning, setIsScanning] = useState(true);
  const [isLoading, setIsLoading] = useState(false);
  const { t } = useContext(AppContext);
  const [isFlashOn, setIsFlashOn] = useState(false);

  useFocusEffect(
    useCallback(() => {
      setIsScanning(true);
      setScannedData(null);
      setIsLoading(false);
    }, [])
  );

  if (!permission) {
    return <View />;
  }

  if (!permission.granted) {
    return (
      <View style={styles.container}>
        <Text style={{ textAlign: 'center' }}>We need your permission to show the camera</Text>
        <Button onPress={requestPermission} title="grant permission" />
      </View>
    );
  }

  const handleBarcodeScanned = ({ data }) => {
    // Processing scanned data
  };

  const toggleFlash = () => {
    setIsFlashOn(!isFlashOn);
  };

  return (
    <View style={styles.container}>
      <CameraView
        style={styles.camera}
        onBarcodeScanned={handleBarcodeScanned}
        flashMode={isFlashOn ? 'torch' : 'off'}
      />
      <View style={styles.overlay}>
        <TouchableOpacity
          style={styles.flashlightButton}
          onPress={toggleFlash}
        >
          <Text style={styles.flashlightText}>{isFlashOn ? 'Flash Off' : 'Flash On'}</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

export default ScanEnterGTINScreen;

问题分析与修正方案

你的代码逻辑框架没问题,但有几个细节需要调整,才能确保手电筒在双平台稳定工作:

1. 先检查设备是否支持手电筒

部分设备(如老旧平板、无闪光灯的机型)不支持手电筒功能,直接切换会导致无响应。需要提前检测:

// 导入新增的API
import { CameraView, useCameraPermissions, isTorchAvailableAsync } from 'expo-camera';

在组件中添加状态和检测逻辑:

const [isTorchAvailable, setIsTorchAvailable] = useState(false);

useFocusEffect(
  useCallback(async () => {
    setIsScanning(true);
    setScannedData(null);
    setIsLoading(false);
    // 检查手电筒可用性
    const available = await isTorchAvailableAsync();
    setIsTorchAvailable(available);
  }, [])
);

2. 使用官方枚举值避免字符串错误

Expo Camera提供了FlashMode枚举,比直接写字符串更可靠,避免拼写失误:

import { CameraView, useCameraPermissions, FlashMode, isTorchAvailableAsync } from 'expo-camera';

修改flashMode的赋值:

flashMode={isFlashOn ? FlashMode.TORCH : FlashMode.OFF}

3. 优化按钮交互逻辑

如果设备不支持手电筒,应该禁用按钮并提示用户:

<TouchableOpacity
  style={[styles.flashlightButton, !isTorchAvailable && styles.disabledButton]}
  onPress={toggleFlash}
  disabled={!isTorchAvailable}
>
  <Text style={styles.flashlightText}>
    {isTorchAvailable ? (isFlashOn ? '关闭手电筒' : '打开手电筒') : '设备不支持手电筒'}
  </Text>
</TouchableOpacity>

在样式文件中添加禁用状态:

// ScanEnterGTINScreenStyles.js
disabledButton: {
  opacity: 0.5,
}

4. 完善iOS权限配置

iOS需要在app.json中添加相机权限描述,否则应用会崩溃:

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSCameraUsageDescription": "需要访问相机以扫描条码和使用手电筒"
      }
    }
  }
}

5. 确保相机处于激活状态

手电筒只有在相机活跃时才能工作,需保证CameraView未被暂停,比如扫描完成后如果停止了扫描逻辑,要注意手电筒的状态同步。

完整修正后的代码

import { CameraView, useCameraPermissions, FlashMode, isTorchAvailableAsync } from 'expo-camera';
import React, { useState, useContext, useCallback } from 'react';
import { Button, Text, TouchableOpacity, View } from 'react-native';
import { AppContext } from "../../context/AppContext";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useFocusEffect } from '@react-navigation/native';
import styles from './ScanEnterGTINScreenStyles';

const ScanEnterGTINScreen = ({ navigation }) => {
  const [permission, requestPermission] = useCameraPermissions();
  const [scannedData, setScannedData] = useState(null);
  const [isScanning, setIsScanning] = useState(true);
  const [isLoading, setIsLoading] = useState(false);
  const { t } = useContext(AppContext);
  const [isFlashOn, setIsFlashOn] = useState(false);
  const [isTorchAvailable, setIsTorchAvailable] = useState(false);

  useFocusEffect(
    useCallback(async () => {
      setIsScanning(true);
      setScannedData(null);
      setIsLoading(false);
      // 检查设备是否支持手电筒
      const torchAvailable = await isTorchAvailableAsync();
      setIsTorchAvailable(torchAvailable);
    }, [])
  );

  if (!permission) {
    return <View />;
  }

  if (!permission.granted) {
    return (
      <View style={styles.container}>
        <Text style={{ textAlign: 'center' }}>我们需要相机权限才能使用扫描功能</Text>
        <Button onPress={requestPermission} title="授予权限" />
      </View>
    );
  }

  const handleBarcodeScanned = ({ data }) => {
    // 处理扫描到的数据
    if (isScanning) {
      setScannedData(data);
      setIsScanning(false);
      // 这里可以添加导航或数据处理逻辑
    }
  };

  const toggleFlash = () => {
    if (isTorchAvailable) {
      setIsFlashOn(!isFlashOn);
    }
  };

  return (
    <View style={styles.container}>
      <CameraView
        style={styles.camera}
        onBarcodeScanned={isScanning ? handleBarcodeScanned : undefined}
        flashMode={isFlashOn ? FlashMode.TORCH : FlashMode.OFF}
      />
      <View style={styles.overlay}>
        <TouchableOpacity
          style={[styles.flashlightButton, !isTorchAvailable && styles.disabledButton]}
          onPress={toggleFlash}
          disabled={!isTorchAvailable}
        >
          <Text style={styles.flashlightText}>
            {isTorchAvailable ? (isFlashOn ? '关闭手电筒' : '打开手电筒') : '设备不支持手电筒'}
          </Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

export default ScanEnterGTINScreen;

额外注意事项

  • 测试时确保设备配备闪光灯硬件
  • Android平台的权限Expo已自动处理,无需额外配置
  • Expo Go中可直接测试,构建独立应用时需确保依赖正确打包

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:13:13