skip to Main Content

so im building a QR code scanner in Flutter that can only scan specific type of qr codes How can I add a feature that will reject QR codes that have nothing to do with the object I’m trying to scan?

import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:provider/provider.dart';
import 'package:audioplayers/audioplayers.dart';
import 'serial_number_model.dart';
import 'serial_number_history.dart';
import 'inventory.dart';

const backgroundColor = Color.fromARGB(248, 248, 245, 245);

class QrScanner extends StatefulWidget {
  const QrScanner({super.key});

  @override
  State<QrScanner> createState() => _QrScannerState();
}

class _QrScannerState extends State<QrScanner> {
  bool hasScanned = false;
  bool isFlashActive = false;
  CameraFacing cameraDirection = CameraFacing.back;
  late MobileScannerController scannerController;
  late AudioPlayer audioPlayer;

  @override
  void initState() {
    super.initState();
    scannerController = MobileScannerController();
    audioPlayer = AudioPlayer();
  }

  Future<void> playScanSound() async {
    await audioPlayer.play(AssetSource('scan.mp3'));
  }

  void onBarcodeDetected(Barcode barcode) {
    if (!hasScanned) {
      final code = barcode.rawValue ?? 'Unknown Code';
      setState(() {
        hasScanned = true;
      });
      playScanSound();
      showSaveDialog(code);
    }
  }

  void showSaveDialog(String code) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Save Your Serial Number!'),
        content: const Text('Where would you like to keep this number?'),
        actions: [
          ...buildCategoryButtons(code),
          TextButton(
            child: const Text('No, thanks!'),
            onPressed: () {
              Navigator.pop(context);
              resetScanState();
            },
          ),
        ],
      ),
    );
  }

  List<Widget> buildCategoryButtons(String code) {
    final categories = ['Graphics Card', 'Motherboard', 'Processor'];
    return categories.map((category) {
      return TextButton(
        child: Text(category),
        onPressed: () {
          saveSerialNumber(category, code);
          Navigator.pop(context); // Close the dialog
        },
      );
    }).toList();
  }

  Future<void> saveSerialNumber(String category, String code) async {
    await Provider.of<SerialNumberModel>(context, listen: false)
        .addSerialNumber(category, code);
    if (!mounted) return; // Check if the widget is still mounted

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => SerialNumberHistoryScreen(
          category: category,
          initialSerialNumbers: Provider.of<SerialNumberModel>(context)
              .getSerialNumbers(category)
              .map((map) => map['serialNumber']!)
              .toList(),
          serialNumbers: const [],
        ),
      ),
    );

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Saved under $category!')),
    );
    resetScanState(); // Reset scan state after saving
  }

  void resetScanState() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        hasScanned = false;
      });
    });
  }

  Future<void> toggleFlash() async {
    setState(() {
      isFlashActive = !isFlashActive;
    });

    // Use the MobileScannerController to control flash
    if (isFlashActive) {
      scannerController.toggleTorch(); // Use toggleTorch instead
    } else {
      scannerController.toggleTorch(); // Use toggleTorch instead
    }
  }

  void switchCamera() {
    setState(() {
      cameraDirection = cameraDirection == CameraFacing.back
          ? CameraFacing.front // Switch to front camera
          : CameraFacing.back; // Switch to back camera
      scannerController.switchCamera();

      String cameraPosition =
          cameraDirection == CameraFacing.front ? 'front' : 'back';
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Switched to $cameraPosition camera')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: backgroundColor,
      appBar: AppBar(
        actions: [
          IconButton(
            onPressed: toggleFlash,
            icon: Icon(isFlashActive ? Icons.flash_off : Icons.flash_on,
                color: Colors.grey),
          ),
          IconButton(
            onPressed: switchCamera,
            icon: const Icon(Icons.camera, color: Colors.grey),
          ),
        ],
        iconTheme: const IconThemeData(color: Color.fromARGB(221, 131, 97, 97)),
        centerTitle: true,
        title: const Text("QR Scanner",
            style: TextStyle(
                color: Color.fromARGB(221, 0, 0, 0),
                fontWeight: FontWeight.bold)),
        leading: Builder(builder: (context) {
          return IconButton(
            icon: const Icon(Icons.menu),
            onPressed: () =>
                Scaffold.of(context).openDrawer(), // Open drawer menu
          );
        }),
      ),
      body: Container(
        width: double.infinity,
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            const Expanded(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("Hold your device steady over the QR code.",
                      style: TextStyle(
                          color: Colors.black87,
                          fontSize: 18,
                          fontWeight: FontWeight.bold)),
                  SizedBox(height: 10),
                  Text("Scanning will start automatically."),
                ],
              ),
            ),
            Expanded(
              flex: 4,
              child: Center(
                child: SizedBox(
                  width: double.infinity,
                  height: 400, // Fixed scanner height
                  child: Stack(
                    alignment: Alignment.center,
                    children: [
                      Container(
                        decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            border: Border.all(color: Colors.black, width: 2)),
                      ),
                      SizedBox(
                        width: double.infinity,
                        height: double.infinity,
                        child: MobileScanner(
                          controller: scannerController,
                          onDetect: (BarcodeCapture barcodeCapture) {
                            onBarcodeDetected(barcodeCapture.barcodes.first);
                          },
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            const Expanded(
              child: Align(
                  alignment: Alignment.center,
                  child: Text("TechShack",
                      style: TextStyle(
                          color: Color.fromARGB(221, 247, 228, 228),
                          fontSize: 14))),
            ),
          ],
        ),
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            DrawerHeader(
              decoration: const BoxDecoration(
                color: Color.fromARGB(
                    255, 73, 167, 255), // Header background color
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  const Text(
                    'TechShack',
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Image.asset(
                    'assets/logo.png', // Path to your logo
                    height: 120, // Adjusted height for better visibility
                    width: 120, // Set a width to maintain aspect ratio
                    fit: BoxFit
                        .cover, // Ensures the image covers the allocated space without distortion
                  ),
                ],
              ),
            ),
            buildDrawerItem('Graphics Card'),
            buildDrawerItem('Motherboard'),
            buildDrawerItem('Processor'),
            ListTile(
              title: const Text('Inventory'),
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => const Inventory(),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }

  Widget buildDrawerItem(String category) {
    return ListTile(
      title: Text(category),
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => Consumer<SerialNumberModel>(
              builder: (context, serialNumberModel, child) {
                return SerialNumberHistoryScreen(
                  category: category,
                  initialSerialNumbers: serialNumberModel
                      .getSerialNumbers(category)
                      .map((map) => map['serialNumber']!)
                      .toList(),
                  serialNumbers: const [],
                );
              },
            ),
          ),
        );
      },
    );
  }
}

i wanted to make the scanner scan only specific products and deny qr codes that is not relatable to the item that i wanted to scan

——–
Here’s a more polished, humanized version of your question, while keeping the original intent and clarity intact for StackOverflow:


Title: How to make a QR Code scanner in Flutter reject unrelated QR codes?


Question:

I’m building a QR Code scanner in Flutter using the mobile_scanner package, and I want it to only accept specific types of QR codes. For instance, the scanner should recognize codes that match a particular pattern or relate to a predefined list of items (e.g., product categories like Graphics Cards, Motherboards, Processors).

Currently, my implementation allows the scanner to detect all QR codes, but I need to filter out irrelevant QR codes and reject any code that does not meet my criteria.


Code Overview:

Here’s the relevant part of my implementation:

void onBarcodeDetected(Barcode barcode) {
  if (!hasScanned) {
    final code = barcode.rawValue ?? 'Unknown Code';
    setState(() {
      hasScanned = true;
    });
    playScanSound();
    showSaveDialog(code);
  }
}

In this function, the scanner detects any QR code and passes its rawValue to further processing. However, I need a way to validate the QR code content against my specific requirements (e.g., checking for a specific format, prefix, or allowed list of strings).


Desired Behavior:

  1. The scanner should:

    • Accept only QR codes that match specific criteria (like containing a certain prefix, pattern, or valid product name).

    • Ignore and reject any unrelated or invalid QR codes.

  2. When an invalid code is detected:

    • Play a rejection sound (optional).

    • Show a message indicating that the code is not valid.

  3. For valid QR codes:

    • Continue with the existing logic (e.g., show a dialog to save the serial number).

What I’ve Tried:

  • I thought about using a regular expression or a simple string comparison to validate the QR code content before processing it.

  • However, I’m unsure about the best way to integrate this validation logic into the onBarcodeDetected function while keeping the code clean and maintainable.


The Question:

How can I add validation to my QR code scanner so it only accepts codes that meet my specific criteria and rejects all others? Are there any best practices or patterns for implementing this in Flutter?

2

Answers


  1. Don’t update the UI when you get the scanned value use a method to check and return a value that updates the UI only if the value matches the product you want to scan. If the desired value is not met keep the camera instance running and don’t update the UI.

    Example:

    void onBarcodeDetected(Barcode barcode) {
    var isValidCode = checkValidCode(barcode.toString());
    if (!hasScanned && isValidCode) {
      final code = barcode.rawValue ?? 'Unknown Code';
      setState(() {
        hasScanned = true;
      });
      playScanSound();
      showSaveDialog(code);
    }
    }
    
    
    checkVaildCode(String code) {
    if(code.startsWith("BN"){//assuming your products starts with barcode BN ex. BN2023253428
    return true;
    }
    return false;
    }
    

    Thats it enjoy coding 🙂

    Login or Signup to reply.
  2. start with setting controller to accept only QR

    MobileScannerController qrController = MobileScannerController(
        formats: [BarcodeFormat.qrCode],
      );
    

    and you can check value prefix by using [string].startsWith('your-prefix') like @SaifAlmajd answer

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search