# ZXing.Net.MAUI: barcode scanning and generation for .NET MAUI apps

> ZXing.Net.MAUI is the successor to ZXing.Net.Mobile, giving .NET MAUI applications a camera barcode reader view, a barcode generator view, and still-image decoding through a single NuGet package. Windows is excluded from scanning, and the reader's frame timing options decide whether scanning feels fast or sluggish.

**Redth/ZXing.Net.Maui** — Barcode Scanning for MAUI?

- Repository: https://github.com/Redth/ZXing.Net.Maui
- Stars: 582 · Forks: 209
- Language: C#
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/redth-zxing-net-maui

## The successor to a Xamarin plugin, in a MAUI package

The project describes itself as the successor to ZXing.Net.Mobile: barcode scanning and generation for .NET MAUI applications. That lineage explains a lot about the shape of the API, because the reader view keeps the same property names and the same delay semantics as the Xamarin plugin it replaces.

The primary language is C#, the licence is MIT, the default branch is main, and the recent releases are 0.10.4 on 2026-08-14, 0.10.3 on 2026-07-03 and 0.10.2 on 2026-06-30. The last push was on 2026-10-08.

The tree shows a normal multi-project solution rather than a single library project. There is ZXing.Net.MAUI/ for the core package, ZXing.Net.MAUI.Controls/ for the XAML controls, ZXing.Net.MAUI.Tests/ for the tests, and a ZXing.Net.MAUI.sln at the root. A BigIslandBarcode/ directory and a CAMERA_SELECTION.md file sit alongside, and the presence of a NuGet.config and an .editorconfig points at a project that takes packaging conventions seriously.

The audience is a .NET MAUI developer who needs a scanner in a shipping application rather than a demo. That framing matters because the package is opinionated about platform permissions, and it assumes you are writing a real app with a real store listing, where a camera permission with a vague justification string gets your app rejected.

## The install is one NuGet package and one builder call

The installation steps are two, and the second one is the one people miss.

First, install the ZXing.Net.Maui.Controls NuGet package on your .NET MAUI application. Second, make sure to initialize the plugin first in your MauiProgram.cs. The initialization is a single extension method on the app builder, .UseBarcodeReader(), added to the builder chain alongside .UseMauiApp<App>().

The documentation includes a complete MauiProgram.cs fragment showing where the using statement goes at the top of the file, the builder creation, and the .UseBarcodeReader() call with a comment marking it as the line that must be added. It is worth copying that structure rather than improvising, because the plugin has to be registered before any view that uses it is created.

There is no platform-specific setup beyond permissions, no native project edits, and no manual linking step. The controls come from the same package, so the XAML view and the camera work are the same dependency.

If you are working from the existing ZXing.Net.Mobile code, the migration is mostly a package swap and a namespace change, since the API is deliberately continuous with the plugin it replaces.

## Permissions differ per platform, and Windows has none of this

The permission story is where the platforms diverge, and the documentation is specific about each one.

On Android you go to your AndroidManifest.xml file under the Platforms\Android folder and add the camera permission inside of the manifest node.

```xml
<uses-permission android:name="android.permission.CAMERA" />
```

On iOS you go to your info.plist file under the Platforms\iOS folder and add the following permissions inside of the dict node.

```xml
<key>NSCameraUsageDescription</key>
<string>This app uses barcode scanning to...</string>
```

The iOS string is user facing. The documentation says to enter a clear and valid reason for your app to access the camera, and that this description will be shown to the user, so the placeholder text in the example has to be replaced with something a person reading the prompt can act on.

Windows is the odd one out and the wording is blunt: Windows is not supported at this time for barcode scanning. You can however use the barcode generation, and no extra permissions are required for that.

So if your MAUI application targets the Windows desktop alongside Android and iOS, the camera path is unavailable there and the generation path is your only option. For more information on permissions the documentation points at the Microsoft Docs page for MAUI platform integration and app model permissions.

## Checking for a camera before you scan

Before using barcode scanning, you can check if the device supports it, that is, whether it has a camera available.

```csharp
if (ZXing.Net.Maui.BarcodeScanning.IsSupported)
{
  // Device has a camera, safe to use barcode scanning
}
else
{
  // No camera available, show alternative UI or message
}
```

The documented purpose is handling devices without cameras gracefully, avoiding runtime exceptions. That is not a hypothetical concern: a MAUI application that runs on a tablet, a desktop or an emulator will hit the no-camera path, and the difference between checking a static flag and finding out when the view is created is the difference between a friendly message and a crash on a device you cannot test on.

The check is a static property on BarcodeScanning, so it can be called before any view exists. That makes it usable as a guard around the navigation that would otherwise open the reader, which is where it earns its place.

## The reader view, and the options that decide whether scanning feels fast

If you are using the controls from XAML, make sure to add the right XML namespace in the root of your file, for example xmlns:zxing with the clr-namespace pointing at ZXing.Net.Maui.Controls and the assembly ZXing.Net.MAUI.Controls. The view itself is a single element.

```xaml
<zxing:CameraBarcodeReaderView
  x:Name="cameraBarcodeReaderView"
  BarcodesDetected="BarcodesDetected" />
```

The reader options are where the real tuning happens.

```csharp
cameraBarcodeReaderView.Options = new BarcodeReaderOptions
{
  Formats = BarcodeFormats.OneDimensional,
  AutoRotate = true,
  Multiple = true,
  DelayBetweenAnalyzingFrames = 150,
  InitialDelayBeforeAnalyzingFrames = 300,
  DelayBetweenContinuousScans = 1000,
  CameraResolutionSelector = availableResolutions =>
    availableResolutions
      .OrderBy(resolution => Math.Abs((resolution.Width * resolution.Height) - (1280 * 720)))
      .ThenBy(resolution => Math.Abs(resolution.Width - 1280) + Math.Abs(resolution.Height - 720))
      .First()
};
```

The delay options are expressed in milliseconds and default to the Xamarin plugin cadence: 150 milliseconds between frame analyses, 300 milliseconds before initial analysis, and 1000 milliseconds between continuous scan detections. Set a delay to 0 to analyze as soon as the camera pipeline provides frames.

The CameraResolutionSelector receives the resolutions supported by the active camera and should return the preferred resolution. The example orders by closeness to 1280x720, and the documentation explains why that matters: the selected camera resolution is also the frame size passed to the barcode decoder. Higher resolutions provide more pixels but increase the amount of work needed for every frame, so selecting the largest available resolution can reduce scan throughput. The advice is to prefer the lowest resolution that reliably decodes, such as a size close to 1280x720 or 640x480, instead of always ordering by the highest pixel count.

## Decoding images without starting the camera

Still-image decoding is available on Android, iOS, MacCatalyst and Windows without starting the camera, and the same BarcodeReaderOptions used by the camera reader also apply to image streams.

```csharp
using ZXing.Net.Maui;
using var stream = await fileResult.OpenReadAsync();

var results = await BarcodeReader.DecodeAsync(
  stream,
  new BarcodeReaderOptions
  {
    Formats = BarcodeFormats.All,
    AutoRotate = true,
    TryHarder = true,
    Multiple = true
  });

foreach (var result in results ?? [])
  Console.WriteLine($"{result.Format}: {result.Value}");
```

You can also decode directly from a file path with BarcodeReader.DecodeFromFileAsync.

Two details in the sample matter. TryHarder is on, which tells the decoder to spend more effort looking for a barcode in difficult images, and Multiple is on, so a single image can yield several results rather than only the first.

There is one platform limit stated plainly. EXIF orientation is respected before decoding when the platform image APIs expose it. The neutral net10.0 target does not include a platform image decoder, so stream decoding there throws PlatformNotSupportedException. That is the kind of thing that turns into a confusing bug report if you did not know to expect it, since the code is identical apart from the target framework.

## Torch, zoom and picking which camera to use

The remaining view controls are small, but two of them have behaviour that differs per platform and is worth knowing before you ship.

The torch is a boolean toggle on the view.

```csharp
cameraBarcodeReaderView.IsTorchOn = !cameraBarcodeReaderView.IsTorchOn;
```

The zoom factor is normalized from 0 to 1 across platforms, and values outside that range are clamped.

```csharp
// 0 = minimum zoom, 1 = maximum zoom. Values outside this range are clamped.
cameraBarcodeReaderView.ZoomFactor = 0.5f;
```

The platform mapping is not the same behind the normalized value: Android uses CameraX linear zoom, iOS maps the value to the active device zoom range capped at 5x, and Windows maps it to the device ZoomControl range when supported.

Flipping between the rear and front cameras is a property assignment.

```csharp
cameraBarcodeReaderView.CameraLocation
  = cameraBarcodeReaderView.CameraLocation == CameraLocation.Rear ? CameraLocation.Front : CameraLocation.Rear;
```

And you can select a specific camera rather than relying on the default, by asking for the available cameras and assigning the SelectedCamera property.

```csharp
// Get available cameras
var cameras = await cameraBarcodeReaderView.GetAvailableCameras();

// Select a specific camera by setting the SelectedCamera property
if (cameras.Count > 0)
{
  cameraBarcodeReaderView.SelectedCamera = cameras[0];
}
```

The example also loops through the available cameras and picks the first rear one, which is the pattern you want on a device with a front camera that some app has already taken over.

## Where the boundary sits, and what to check before shipping

There are three limits, and the first two are documented rather than discovered.

The Windows exclusion for scanning is the big one. If your application is expected to run on Windows with a camera, the feature is not there, and the fallback is barcode generation only. A barcode scanning application that silently does nothing on one of its target platforms is a support problem, so decide the Windows behaviour deliberately.

The net10.0 stream decoding exception is the second, and it is a compile-time target decision rather than a runtime surprise, which is the good case.

The third is the one the documentation states but the code does not enforce: the resolution you select is the frame size handed to the decoder, so a high resolution costs you throughput on every single frame. On a low-end Android device that is the difference between a scanner that feels instant and one that feels broken, and the fix is a resolution closer to 640x480 or 1280x720 rather than the largest the camera offers. The delay values matter for the same reason, since analysing every frame as fast as the pipeline delivers them is the opposite of what you want on a slow device.

One more piece of information is worth carrying into the project you build on top of it. QR codes with international characters, for example the pound sign, the euro sign, the yen sign or non-Latin scripts, are supported by default with UTF-8 encoding, and you can override that if needed.

```csharp
cameraBarcodeReaderView.Options = new BarcodeReaderOptions
{
  Formats = BarcodeFormats.TwoDimensional,
  CharacterSet = "ISO-8859-1"  // Override default UTF-8 if needed
};
```

And the generation side, the BarcodeGeneratorView, is a XAML element in the same namespace, with a HeightRequest property, so the same package covers both directions of the barcode workflow.

## Conclusion

Adopt ZXing.Net.MAUI when you are building a .NET MAUI application that needs to scan or generate barcodes and want one API across Android and iOS, and skip it if you need camera scanning on Windows, which the documentation excludes. Before you wire the reader up, set the frame delay options deliberately rather than leaving the defaults, because the camera resolution you pick is also the frame size handed to the decoder, and the last push was on 2026-10-08 with release 0.10.4 on 2026-08-14.

## FAQ

### How do I add barcode scanning to a .NET MAUI app with ZXing.Net.MAUI?

Install the ZXing.Net.Maui.Controls NuGet package, then initialize the plugin in MauiProgram.cs by adding the .UseBarcodeReader() call to the app builder chain. After that you can place the CameraBarcodeReaderView in XAML.

### Does ZXing.Net.MAUI support barcode scanning on Windows?

No. The documentation states that Windows is not supported at this time for barcode scanning, but barcode generation works there and no extra permissions are required for it.

### How do I check whether the device has a camera in ZXing.Net.MAUI?

Check the static ZXing.Net.Maui.BarcodeScanning.IsSupported property before using barcode scanning. The documentation recommends this to handle devices without cameras gracefully and avoid runtime exceptions.

### Can I decode a barcode from an image file without using the camera?

Yes. Still-image decoding is available on Android, iOS, MacCatalyst and Windows, using BarcodeReader.DecodeAsync on a stream or BarcodeReader.DecodeFromFileAsync for a file path. The neutral net10.0 target has no platform image decoder, so stream decoding there throws PlatformNotSupportedException.

### What camera permissions does ZXing.Net.MAUI need on Android and iOS?

On Android, add the android.permission.CAMERA permission to the manifest node in AndroidManifest.xml. On iOS, add the NSCameraUsageDescription key to the dict node in info.plist, with a clear reason for the user since that description is shown to them.

## Sources

- [Issues](https://github.com/Redth/ZXing.Net.Maui/issues)
- [License: MIT](https://github.com/Redth/ZXing.Net.Maui/blob/main/LICENSE)
- [README](https://github.com/Redth/ZXing.Net.Maui/blob/main/README.md)
- [Redth/ZXing.Net.Maui on GitHub](https://github.com/Redth/ZXing.Net.Maui)
- [Releases](https://github.com/Redth/ZXing.Net.Maui/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/redth-zxing-net-maui
