Documentation Index Fetch the complete documentation index at: https://mintlify.com/soymatudev/Pokedex-Fleek/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The ScannerScreen component provides the core scanning functionality of the PokéDex Fleek app. It uses the device camera to capture images and analyze dominant colors to identify Pokémon. The component supports both QR/barcode scanning and manual color-based detection.
Location : ~/workspace/source/src/features/scanner/ScannerScreen.js
Props
React Navigation object for screen navigation Navigate to other screens (e.g., navigate('Details', { pokemonId: 1 }))
Return to previous screen
State Management
The component manages three primary state variables:
Camera permission status from the device
detectedPokemon
object | null
default: "null"
Currently detected Pokémon data from POKEMON_DB
Indicates whether a scan operation is in progress
Key Functions
hexToHue(hex)
Converts a hexadecimal color value to a hue value (0-360 degrees) for color matching.
Hex color string (with or without ’#’ prefix)
Returns : number - Hue value in degrees (0-360)
const hexToHue = ( hex ) => {
if ( ! hex || hex . length < 6 ) return 0 ;
const cleanHex = hex . startsWith ( '#' ) ? hex : `# ${ hex } ` ;
let r = parseInt ( cleanHex . slice ( 1 , 3 ), 16 ) / 255 ;
let g = parseInt ( cleanHex . slice ( 3 , 5 ), 16 ) / 255 ;
let b = parseInt ( cleanHex . slice ( 5 , 7 ), 16 ) / 255 ;
let max = Math . max ( r , g , b ), min = Math . min ( r , g , b );
let h , d = max - min ;
if ( d === 0 ) h = 0 ;
else if ( max === r ) h = ( g - b ) / d + ( g < b ? 6 : 0 );
else if ( max === g ) h = ( b - r ) / d + 2 ;
else if ( max === b ) h = ( r - g ) / d + 4 ;
return Math . round ( h * 60 );
};
handleManualScan()
Manually captures a photo and analyzes its dominant color to detect a Pokémon.
Process :
Takes a photo using the camera reference
Extracts dominant color using ImageColors.getColors()
Converts color to hue value
Matches hue against POKEMON_DB color ranges
Provides haptic feedback (vibration)
Navigates to Details screen on successful match
const handleManualScan = async () => {
if ( isProcessing ) return ;
Vibration . vibrate ( 70 );
setIsProcessing ( true );
try {
const photo = await cameraRef . current . takePhoto ({
flash: 'off' ,
skipMetadata: true
});
const result = await ImageColors . getColors ( photo . path , {
fallback: '#000000'
});
const detectedHex = result . platform === 'android'
? result . dominant
: result . background ;
const hue = hexToHue ( detectedHex );
const potentialMatches = Object . values ( POKEMON_DB ). filter ( p =>
hue >= p . colorRange . hueMin && hue <= p . colorRange . hueMax
);
if ( potentialMatches . length > 0 ) {
const randomIndex = Math . floor ( Math . random () * potentialMatches . length );
const match = potentialMatches [ randomIndex ];
setDetectedPokemon ( match );
Vibration . vibrate ([ 0 , 100 , 50 , 100 ]);
setTimeout (() => {
navigation . navigate ( 'Details' , {
pokemonId: match . id ,
themeColor: match . uiTheme
});
setIsProcessing ( false );
setDetectedPokemon ( null );
}, 500 );
} else {
alert ( "No se detectó ADN Pokémon conocido en este objeto." );
setIsProcessing ( false );
}
} catch ( error ) {
console . error ( error );
Vibration . vibrate ( 400 );
setIsProcessing ( false );
}
};
codeScanner Configuration
Configures automatic QR code and barcode scanning.
codeTypes
array
default: "['qr', 'ean-13']"
Supported barcode/QR code formats
Callback fired when a code is detected
const codeScanner = useCodeScanner ({
codeTypes: [ 'qr' , 'ean-13' ],
onCodeScanned : async ( codes ) => {
if ( isProcessing || detectedPokemon || codes . length === 0 ) return ;
setIsProcessing ( true );
try {
const id = codes [ 0 ]. value ;
const photo = await cameraRef . current . takePhoto ({
flash: 'off' ,
skipMetadata: true
});
// Color analysis and matching logic
const result = await ImageColors . getColors ( photo . path , {
fallback: '#000000'
});
const detectedHex = result . platform === 'android'
? result . dominant
: result . background ;
const hue = hexToHue ( detectedHex );
const match = Object . values ( POKEMON_DB ). find ( p =>
hue >= p . colorRange . hueMin && hue <= p . colorRange . hueMax
);
if ( match ) {
setDetectedPokemon ( match );
setTimeout (() => {
navigation . navigate ( 'Details' , { pokemonId: match . id });
}, 1000 );
}
} catch ( error ) {
console . error ( "Error:" , error );
setIsProcessing ( false );
}
}
});
UI Elements
Reticle (Target Indicator)
Central circular target indicator for aiming the camera:
< View style = { styles . reticleContainer } >
< View style = { styles . reticle } >
< View style = { styles . reticleDot } />
</ View >
< Text style = { styles . reticleText } > CENTRAR OBJETIVO </ Text >
</ View >
Stylized button that resembles a Pokéball, triggers manual scanning:
< TouchableOpacity
style = { [
styles . pokeballButton ,
isProcessing && { borderColor: theme . colors . primary , elevation: 20 }
] }
onPress = { handleManualScan }
disabled = { isProcessing }
>
< View style = { [
styles . pokeballInnerRing ,
isProcessing && { borderColor: '#fff' }
] } >
{ isProcessing ? (
< ActivityIndicator color = "#333" />
) : (
< View style = { styles . pokeballCenterBtn } />
) }
</ View >
</ TouchableOpacity >
Camera Overlay
Semi-transparent overlay providing visual guidance:
< View style = { styles . overlay } >
{ /* Reticle and scan button */ }
</ View >
Camera Integration
The component uses react-native-vision-camera for camera access:
{ isFocused && (
< Camera
ref = { cameraRef }
codeScanner = { codeScanner }
device = { device }
isActive = { true }
style = { StyleSheet . absoluteFill }
photo = { true }
/>
)}
The camera only renders when the screen is focused (isFocused from useIsFocused() hook)
Navigation Flow
To Details Screen
On successful Pokémon detection:
navigation . navigate ( 'Details' , {
pokemonId: match . id ,
themeColor: match . uiTheme
});
Back Navigation
Close button in the top-left corner:
< TouchableOpacity
style = { styles . closeButton }
onPress = { () => navigation . goBack () }
>
< Text style = { styles . closeIcon } > ✕ </ Text >
</ TouchableOpacity >
Permission Handling
Requests camera permission on mount and displays settings prompt if denied:
useEffect (() => {
( async () => {
const status = await Camera . requestCameraPermission ();
setHasPermission ( status === 'granted' );
})();
}, []);
if ( ! hasPermission ) {
return (
< View style = { styles . center } >
< Text style = { styles . errorText } >
Dexter necesita permisos de cámara
</ Text >
< TouchableOpacity
onPress = { () => Linking . openSettings () }
style = { styles . backButton }
>
< Text style = { { color: 'white' } } > ABRIR AJUSTES </ Text >
</ TouchableOpacity >
</ View >
);
}
Dependencies
react-native-vision-camera - Camera access and frame processing
react-native-image-colors - Dominant color extraction
react-native-worklets-core - High-performance frame processing
@react-navigation/native - Navigation and focus detection
Usage Example
import { ScannerScreen } from './features/scanner/ScannerScreen' ;
// In your navigation stack
< Stack.Screen
name = "Scanner"
component = { ScannerScreen }
options = { { headerShown: false } }
/>