This article was contributed by a netizen.
Author: dotnet-simple
Original title: Maui Blazor Using Camera Implementation
Original link: https://www.cnblogs.com/hejiale010426/p/17045707.html
Since the UI in Maui Blazor is rendered by WebView, it is not possible to access the Android camera directly because the native camera requires binding to a UI component. I found an alternative approach: using js within WebView to call the device camera, which supports multi-platform compatibility. I have tested it on Android and PC. Since I don't have iOS or macOS devices, I couldn't test them, but it is likely compatible; dynamic permission requests may be needed.
1. Add JS Methods
Create a helper.js file in the wwwroot folder and add the following two JavaScript functions.
Add <script src="helper.js"></script> in index.html to include the JS.
/**
* Set the src of an element
* @param {any} canvasId The DOM id of the canvas
* @param {any} videoId The DOM id of the video element
* @param {any} srcId The DOM id of the img element
* @param {any} width Set the screenshot width
* @param {any} height Set the screenshot height
*/
function setImgSrc(dest, videoId, srcId, width, height) {
let video = document.getElementById(videoId);
let canvas = document.getElementById(canvasId);
console.log(video.clientHeight, video.clientWidth);
canvas.getContext("2d").drawImage(video, 0, 0, width, height);
canvas.toBlob(
function (blob) {
var src = document.getElementById(srcId);
src.setAttribute("height", height);
src.setAttribute("width", width);
src.setAttribute("src", URL.createObjectURL(blob));
},
"image/jpeg",
0.95
);
}
/**
* Initialize the camera
* @param {any} src
*/
function startVideo(src) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices
.getUserMedia({ video: true })
.then(function (stream) {
let video = document.getElementById(src);
if ("srcObject" in video) {
video.srcObject = stream;
} else {
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function (e) {
video.play();
};
//mirror image
video.style.webkitTransform = "scaleX(-1)";
video.style.transform = "scaleX(-1)";
});
}
}
Then, achieve compatibility across platforms.
Android:
Platforms/Android/AndroidManifest.xml file content
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!--Camera permission-->
<uses-permission android:name="android.permission.CAMERA" android:required="false"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--Camera feature-->
<uses-feature android:name="android.hardware.camera" />
<!--Audio recording permission-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--Location permission-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https"/>
</intent>
</queries>
</manifest>
Platforms/Android/MainActivity.cs file content
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Platform.Init(this, savedInstanceState);
// Request required permissions; can also be requested at runtime
ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.Camera, Manifest.Permission.RecordAudio, Manifest.Permission.ModifyAudioSettings }, 0);
}
}
MauiWebChromeClient.cs file content
#if ANDROID
using Android.Webkit;
using Microsoft.AspNetCore.Components.WebView.Maui;
namespace MainSample;
public class MauiWebChromeClient : WebChromeClient
{
public override void OnPermissionRequest(PermissionRequest request)
{
request.Grant(request.GetResources());
}
}
public class MauiBlazorWebViewHandler : BlazorWebViewHandler
{
protected override void ConnectHandler(Android.Webkit.WebView platformView)
{
platformView.SetWebChromeClient(new MauiWebChromeClient());
base.ConnectHandler(platformView);
}
}
#endif
Add the following code in MauiProgram.cs; without it, the camera permission may not be granted (see this issue):
#if ANDROID
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<IBlazorWebView, MauiBlazorWebViewHandler>();
});
#endif
The above is the Android adaptation code. For PC, no additional configuration is required. iOS and macOS are unclear.
Then, write the UI:
@page "/" @*Route*@
@inject IJSRuntime JSRuntime @*Inject jsRuntime*@
@if(OpenCameraStatus) @*Hide video when camera is not opened*@
{
<video id="@VideoId" width="@width" height="@height" />
<canvas class="d-none" id="@CanvasId" width="@width" height="@height" />
}
<button @onclick="" style="margin:8px">Open Camera</button>
@*Camera must be triggered manually; if the page is navigated by sliding, the method cannot be called directly, so a button is added*@
<button style="margin:8px">Screenshot</button> @*Capture an image from the video stream*@
<img id="@ImgId" />
@code{
private string CanvasId;
private string ImgId;
private string VideoId;
private bool OpenCameraStatus;
private int width = 320;
private int height = 500;
private async Task OpenCamera()
{
if (!OpenCameraStatus)
{
// Camera must be triggered by user action; sliding to the page will not trigger it
await JSRuntime.InvokeVoidAsync("startVideo", "videoFeed");
OpenCameraStatus = true;
StateHasChanged();
}
}
protected override async Task OnInitializedAsync()
{
// Initialize ids
ImgId = Guid.NewGuid().ToString("N");
CanvasId = Guid.NewGuid().ToString("N");
VideoId = Guid.NewGuid().ToString("N");
await base.OnInitializedAsync();
}
private async Task Screenshot()
{
await JSRuntime.InvokeAsync<String>("setImgSrc", CanvasId, VideoId, ImgId, width, height);
}
}
Now you can run the program and see the result.

Sample code:
Shared by token
Tech exchange group: 737776595