React Native QR code scanner using expo-barcode-scanner

QR code is usually used as a url locator for applications such as mobile payment.In this post I will walk you through how to implement a mobile QR code scanner by using react native along with a cool expo library. This post will help you learn how to:


  • How to implement a QR code scanner using React Native and TypeScript.
  • How to limit the QR code detecting area.
  • How to add a mask scanning area.
  • How to debug react native app by using expo client.


The code is written with Typescript and is available in Github: 

https://github.com/liqili/react-native-qrcode-scanner

Before starting, you need to install Node.js development environment and Yarn package management tool. Then install expo client to your mobile phone from ios app store or google play store.

Installing Expo CLI & Init Project

Java

1
npm install --global expo-cli
2
expo init react-native-qrcode-scanner
3
yarn install
4
expo eject //run this if you want to publish with bare workflow(ios/android)
5
expo start


Screen page for QR code scanning

import React, {useEffect, useState} from 'react';
import {Button, Dimensions, StyleSheet, TouchableOpacity} from 'react-native';
import {Text, View} from '../components/Themed';
import {BarCodeScanner, BarCodeScannerResult} from 'expo-barcode-scanner';
import BarcodeMask from 'react-native-barcode-mask';

const finderWidth: number = 280;
const finderHeight: number = 230;
const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;
const viewMinX = (width - finderWidth) / 2;
const viewMinY = (height - finderHeight) / 2;


export default function BarCodeScanScreen() {
    const [hasPermission, setHasPermission] = useState<boolean | null>(null);
    const [type, setType] = useState<any>(BarCodeScanner.Constants.Type.back);
    const [scanned, setScanned] = useState<boolean>(false);


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

    const handleBarCodeScanned = (scanningResult: BarCodeScannerResult) => {
        if (!scanned) {
            const {type, data, bounds: {origin} = {}} = scanningResult;
            // @ts-ignore
            const {x, y} = origin;
            if (x >= viewMinX && y >= viewMinY && x <= (viewMinX + finderWidth / 2) && y <= (viewMinY + finderHeight / 2)) {
                setScanned(true);
                alert(`Bar code with type ${type} and data ${data} has been scanned!`);
            }
        }
    };

    if (hasPermission === null) {
        return <Text>Requesting for camera permission</Text>;
    }
    if (hasPermission === false) {
        return <Text>No access to camera</Text>;
    }
    return (
        <View style={{flex: 1}}>
            <BarCodeScanner onBarCodeScanned={handleBarCodeScanned}
                            type={type}
                            barCodeTypes={[BarCodeScanner.Constants.BarCodeType.qr]}
                            style={[StyleSheet.absoluteFillObject, styles.container]}>
                <View
                    style={{
                        flex: 1,
                        backgroundColor: 'transparent',
                        flexDirection: 'row',
                    }}>
                    <TouchableOpacity
                        style={{
                            flex: 1,
                            alignItems: 'flex-end',
                        }}
                        onPress={() => {
                            setType(
                                type === BarCodeScanner.Constants.Type.back
                                    ? BarCodeScanner.Constants.Type.front
                                    : BarCodeScanner.Constants.Type.back
                            );
                        }}>
                        <Text style={{fontSize: 18, margin: 5, color: 'white'}}> Flip </Text>
                    </TouchableOpacity>
                </View>
                <BarcodeMask edgeColor="#62B1F6" showAnimatedLine/>
                {scanned && <Button title="Scan Again" onPress={() => setScanned(false)}/>}
            </BarCodeScanner>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    },
    title: {
        fontSize: 20,
        fontWeight: 'bold',
    },
    separator: {
        marginVertical: 30,
        height: 1,
        width: '80%',
    },
});


Add a mask area

import BarcodeMask from 'react-native-barcode-mask';

<BarcodeMask edgeColor="#62B1F6" showAnimatedLine/>


Limit detecting area

In some cases, if you don't want to scan the whole camera area, we can handle this in the callback method.

const finderWidth: number = 280;
const finderHeight: number = 230;
const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;
const viewMinX = (width - finderWidth) / 2;
const viewMinY = (height - finderHeight) / 2;
if (x >= viewMinX && y >= viewMinY && x <= (viewMinX + finderWidth / 2) && y <= (viewMinY + finderHeight / 2)) {
    setScanned(true);
    alert(`Bar code with type ${type} and data ${data} has been scanned!`);
}


Then you are all set!

Comments

Popular posts from this blog

Build J2EE micro services architecture by using Spring Boot, Spring Cloud, Spring Security OAuth2, KeyCloak

NGINX and HTTPs with Let’s Encrypt, Certbot, and Cron dockerization in production

Vault Cubbyhole authentication and its integration with Spring framework