How to Scan QR Codes in Flutter using the mobile_scanner package
QR codes have become an essential part of our daily lives, allowing quick data exchange through smartphones. As a Flutter developer, you may want to add QR code scanning to your application. In this post, we will go through the implementation of QR code scanning using the mobile_scanner package. We will go over the implementation step-by-step, making it easy to integrate QR code scanning into your Flutter application.
1. Starting code
Let us start by setting up our project by creating the following main.dart
and home_screen.dart
files.
main.dart
import 'package:codeonwards_demo/home_screen.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
In the main.dart
file, we create the MyApp
widget, which returns a MaterialApp
widget that renders our HomeScreen
widget.
home_screen.dart
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('QR-code scanner'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('No result'),
ElevatedButton(
child: Text('Scan QR code'),
onPressed: () {},
),
],
),
),
);
}
}
In the home_screen.dart
file, we create the main screen of our application using a Scaffold
widget. The screen has a centered Column
widget containing two widgets: a Text
widget showing "No result" (which will later display the scanned QR code) and a button labeled "Scan QR code" (which will later open the QR code scanner when clicked).

2. Installing
To get started with implementing the QR code scanner, we need to install the mobile_scanner package into our project. The installation process is simple. Just execute the following command: flutter pub add mobile_scanner
.
Once the command is executed, make sure to check your pubspec.yaml
file for the added dependencies. You should see the mobile_scanner package included in the dependencies section, like this:
dependencies:
flutter:
sdk: flutter
mobile_scanner: ^3.3.0
3. Change the MinSdkVersion for Android
Currently, the mobile_scanner package requires a minimum Android SDK version of 21
.

To ensure compatibility with the package, we need to make some changes to the build.gradle
file. Look for the defaultConfig
section in the build.gradle
file that is located in android/app/build.gradle
and modify it as follows:
defaultConfig {
applicationId "com.example.codeonwards_demo"
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
In this code snippet, we updated the minSdkVersion
to 21
, which ensures that our application supports Android devices running on SDK version 21
and above.
If you want to know more about the minSdkVersion
and how it affects your application, you can find additional information in my related post:

4. Add NSCameraUsageDescription to Info.plist for iOS
To enable camera access for QR code scanning on iOS, we need to add an entry for the NSCameraUsageDescription
key in the Info.plist
file. You can find this file at ios/Runner/Info.plist
in your project.
Within the <dict>
element of the Info.plist
file, you will find various keys. To add the necessary entry, simply include the following lines, making sure they are placed one below the other:
<dict>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
...
</dict>
By adding this entry, we provide a clear message to users, explaining why our application requires access to the camera.
5. Scanning QR Codes
Now that our project is set up, it is time to begin the implementation process. We will start by creating a new file called qr_code_scanner.dart
. Inside this file, we will define the QrCodeScanner
class, which will handle all the QR code related functionality. For this purpose, we will be using the mobile_scanner package.
qr_code_scanner.dart
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class QrCodeScanner extends StatelessWidget {
const QrCodeScanner({super.key});
@override
Widget build(BuildContext context) {
final MobileScannerController controller = MobileScannerController();
return MobileScanner(
controller: controller,
onDetect: (BarcodeCapture capture) {
final List<Barcode> barcodes = capture.barcodes;
for (final barcode in barcodes) {
print(barcode.rawValue);
}
},
);
}
}
The QrCodeScanner
widget imports the mobile_scanner/mobile_scanner.dart
package for the QR code scanning capabilities.
Inside the build
method of the QrCodeScanner
widget, we create a MobileScannerController
instance and assign it to the mobileScannerController
variable. This controller will handle the QR code scanning process.
The main part of the code is the MobileScanner
widget, which is responsible for displaying and handling the QR code scanning. It receives the previously created MobileScannerController
as its controller parameter.
Furthermore, we set the required onDetect
property, which activates when the scanner identifies a QR code. When the onDetect
property is triggered, it provides us with a BarcodeCapture
instance. From this instance, we can access the barcodes, which is a List
of barcodes. For the current implementation, we loop through this list and print out the rawValue
of each barcode. This enables us to see the content of each scanned QR code in the console.
Now inside our HomePage
widget, we want the ElevatedButton
widget to display our QrCodeScanner
widget when clicked, see the following code snippet:
home_screen.dart
import 'package:codeonwards_demo/qr_code_scanner.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('QR-code scanner'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('No result'),
ElevatedButton(
child: Text('Scan QR-code'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => QrCodeScanner(),
),
),
),
],
),
),
);
}
}
In the provided code snippet, we have made changes to the onPressed
property of the ElevatedButton
widget, enabling it to display the QrCodeScanner
. When the user taps the button, the code executes an anonymous function that uses Navigator.of(context).push
. This function navigates the application to a new screen.
The argument passed to push()
is a MaterialPageRoute
. This type of route handles the transition to the new screen in a way consistent with Material Design guidelines. The builder
property of MaterialPageRoute
takes a callback function that returns the widget to be displayed on the new screen.
In this case, the builder
function creates an instance of the QrCodeScanner
widget and returns it. This means that when the button is pressed, the application will transition to a new screen showing the QR code scanner interface.
Scanning QR Codes with Android emulator
Let us put our new QR code scanning functionality to the test using an Android emulator. Make sure you have started your Android emulator and run the main.dart
file of your application.
If you have not set up your Android emulator yet and you want to follow along please check my related post:

After running the main
function, the application will build and display the HomeScreen
widget. Upon pressing the "Scan QR-code" button, a pop-up will appear, requesting camera access permission. Once granted, the camera will open, and the Android emulator will simulate a room environment.

Inside the simulation room, you can look around by holding the alt
key and using your mouse to drag the screen. To move around, use the WASDQE
keys while holding the alt
key. Once familiar with the keys make your way toward the dining room as done in the GIF below:

In the dining room, you will notice a placeholder on the wall. We have the option to replace this placeholder with an image of our choice, which in this case will be a QR code. I have generated the QR code in this example by using this QR-code generator.
To replace the placeholder on the wall we need to go through the following steps:
1. Click the three dots on the right side of the emulator to open the options:

2. Navigate to "Camera", and click on "Add Image" to select an image for the wall.

3. Once you selected an image your "Virtual scene images" options should look like this:

4. Now, as we run our application again, you will notice that the QR code is displayed on the wall:

With the emulator now set up correctly, let us proceed to test our QR code scanner. If you keep your application running, you will notice that the QR code scanner continuously detects and scans the QR code, displaying the QR code in the terminal.

In this example, the QR code used returns the value https://www.codeonwards.com
.
Finalizing the QR Code Scanner
Now, let us complete the implementation of our QR code scanner. We want to ensure that the camera closes as soon as we have scanned the QR code, and assign the QR code to our Text
widget.
qr_code_scanner.dart
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class QrCodeScanner extends StatefulWidget {
const QrCodeScanner({required this.setResult, super.key});
final Function setResult;
@override
State<QrCodeScanner> createState() => _QrCodeScannerState();
}
class _QrCodeScannerState extends State<QrCodeScanner> {
final MobileScannerController controller = MobileScannerController();
@override
Widget build(BuildContext context) {
return MobileScanner(
controller: controller,
onDetect: (BarcodeCapture capture) async {
final List<Barcode> barcodes = capture.barcodes;
Barcode barcode = barcodes.first;
if (barcode.rawValue == 'https://www.codeonwards.com') {
widget.setResult(barcode.rawValue);
await controller.stop().then((value) => controller.dispose());
Navigator.of(context).pop();
}
},
);
}
}
In this code snippet, we have added the setResult
property which will be assigned via the constructor. Inside the onDetect
property of the MobileScanner
class, we made the following changes. We take the first barcode from the barcodes
list by using barcodes.first
, this value will be assigned to a barcode
variable.
we have added the setResult
property to our QrCodeScanner
class, which will be assigned through the constructor. Inside the onDetect
property of the MobileScanner
class, we made the following changes. We take the first barcode from the barcodes
list using barcodes.first
, and this value is assigned to the barcode
variable.
We created an if
statement to check if the rawValue
of our barcode
variable is equal to https://www.codeonwards.com
. In real applications you probably want to use a different check, so feel free to change it. If the check returns true, we set the result using widget.setResults(barcode.rawValue)
. Since we already have the result, we can stop our controller by calling controller.stop
. Once this is completed, we dispose of our controller by calling controller.dispose
. Lastly, we want to navigate back to our HomePage
widget, so we use Navigator.of(context).pop()
.
Now that our QrCodeScanner
widget is finished we need to update our HomeScreen
widget accordingly.
home_screen.dart
import 'package:codeonwards_demo/qr_code_scanner.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String? _result;
void setResult(String result) {
setState(() => _result = result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('QR-code scanner'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_result ?? 'No result'),
ElevatedButton(
child: Text('Scan QR-code'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => QrCodeScanner(setResult: setResult),
),
),
),
],
),
),
);
}
}
In our HomePage
widget, we added a new private variable called _result
and function called setResult
, which takes a String
parameter and updates our _result
variable using setState. Finally, we passed this setResult
function as a parameter to the QrCodeScanner
widget.
Now we can finally assign the scanned QR code to our Text
widget, as done in the GIF below:

Conclusion
In this post, we have learned how to implement QR code scanning in Flutter using the mobile_scanner package. We started by setting up our project and installing the necessary dependencies. Then, we made the required changes for Android and iOS to enable camera access for QR code scanning.
Next, we created the QrCodeScanner
widget, which handles all the QR code related functionality using the mobile_scanner package. We tested our QR code scanner using an Android emulator and saw how the scanner continuously detects and scans QR codes.
To finalize the implementation, we made sure to close the camera as soon as we scanned the QR code and assigned its value to our Text
widget. This way, the scanned QR code is displayed on the screen, allowing users to interact with it.
By following these steps, you can easily integrate QR code scanning functionality into your Flutter application, enhancing user experience and allowing for smooth data exchange.