.NET MAUI Blazor Hybrid: Construiește Aplicații Cross-Platform cu UI Partajat

Învață cum să construiești aplicații cross-platform cu .NET MAUI Blazor Hybrid în 2026. Partajare UI între web și mobil cu Razor Class Library, acces la API-uri native, optimizarea performanței și publicare în App Store și Play Store.

Introducere: De Ce Blazor Hybrid Schimbă Regulile Jocului în 2026

Imaginează-ți că poți scrie o singură interfață utilizator în C# și Razor, iar aceasta funcționează nativ pe Android, iOS, Windows, macOS — și pe web. Fără JavaScript obligatoriu, fără să menții trei codebase-uri separate, fără să înveți Dart sau Kotlin doar ca să acoperi toate platformele.

Sună prea frumos ca să fie adevărat? Ei bine, exact asta promite .NET MAUI Blazor Hybrid — și sincer, în 2026 promisiunea chiar se ține.

Blazor Hybrid nu e un concept nou. A apărut odată cu .NET 6 în 2022, dar în primii ani era considerat experimental, instabil și destul de limitat. Lucrurile s-au schimbat radical între timp. Odată cu .NET 10 (lansat în noiembrie 2025 ca versiune LTS, cu suport până în noiembrie 2028), Microsoft a adăugat generare XAML la compilare, interceptarea cererilor web în BlazorWebView, persistența stării circuitelor Blazor Server și îmbunătățiri serioase de performanță. Pe scurt: Blazor Hybrid e acum gata de producție.

În acest ghid, vom acoperi tot ce trebuie să știi — de la arhitectura internă și configurarea proiectului, la accesul la API-uri native, partajarea UI-ului între web și mobil, optimizarea performanței și publicarea în magazinele de aplicații. Fiecare secțiune include cod funcțional pe care îl poți aplica direct.

Ce Este .NET MAUI Blazor Hybrid și Cum Funcționează

Blazor Hybrid este o arhitectură în care componentele Razor (scrise cu HTML, CSS și C#) sunt randate într-un control BlazorWebView încorporat într-o aplicație .NET MAUI nativă. Și aici vine partea importantă: componentele nu rulează în browser și nu folosesc WebAssembly. Ele rulează direct în procesul .NET al aplicației, pe același runtime ca restul codului MAUI.

Comunicarea între componentele Blazor și controlul WebView se face printr-un canal de interop local. Practic, ai un „mini browser" integrat în aplicația nativă, dar profund conectat la platforma de bază. E un mecanism elegant, sincer vorbind.

Ce WebView Folosește Fiecare Platformă

  • Windows: WebView2 (bazat pe Microsoft Edge Chromium)
  • Android: WebView nativ Android (bazat pe Chromium)
  • iOS și macOS: WKWebView (bazat pe motorul WebKit al Safari)

Această diferență de motoare de randare înseamnă că, deși codul Razor e identic, randarea CSS poate avea variații subtile între platforme — mai ales cu proprietăți CSS mai noi. Recomandarea mea: testează mereu pe toate platformele țintă, nu doar pe Windows. Am pierdut ore bune depanând un bug CSS care apărea doar pe iOS.

Arhitectura la Nivel Înalt

O soluție tipică Blazor Hybrid conține trei proiecte:

  1. Aplicația .NET MAUI — proiectul principal care conține configurarea platformei, MauiProgram.cs și paginile MAUI cu controlul BlazorWebView
  2. Razor Class Library (RCL) — biblioteca partajată cu componentele Razor reutilizabile, referențiabilă atât din MAUI cât și din web
  3. Blazor Web App — aplicația web care referențiază aceleași componente din RCL

Această separare e cheia întregii arhitecturi. UI-ul tău trăiește în RCL și este consumat de ambele „shell-uri" — MAUI pentru nativ, Blazor Web pentru browser.

Crearea Primului Proiect Blazor Hybrid

Cerințe Preliminare

  • Visual Studio 2026 cu workload-ul „.NET Multi-platform App UI development"
  • .NET 10 SDK instalat
  • WebView2 Runtime (pe Windows — pentru rulare nativă)
  • Pentru iOS: un Mac cu Xcode disponibil (local sau remote)

Pasul 1: Crearea Soluției

Deschide Visual Studio și selectează Create a new project. Caută template-ul .NET MAUI Blazor Hybrid and Web App. Acesta creează automat cele trei proiecte descrise mai sus:

# Alternativ, din linia de comandă:
dotnet new maui-blazor-web -n MyHybridApp
cd MyHybridApp
dotnet build

Structura generată arată cam așa:

MyHybridApp/
├── MyHybridApp.Maui/          # Aplicația MAUI nativă
│   ├── MauiProgram.cs
│   ├── MainPage.xaml
│   └── Platforms/
├── MyHybridApp.Web/           # Blazor Web App
│   └── Program.cs
├── MyHybridApp.Shared/        # Razor Class Library (RCL)
│   ├── Components/
│   │   ├── Pages/
│   │   └── Layout/
│   └── wwwroot/
└── MyHybridApp.sln

Pasul 2: Configurarea BlazorWebView în MAUI

Fișierul MainPage.xaml e locul unde controlul BlazorWebView este configurat. Nimic complicat:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyHybridApp.Maui"
             x:Class="MyHybridApp.Maui.MainPage">

    <BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html">
        <BlazorWebView.RootComponents>
            <RootComponent Selector="#app"
                           ComponentType="{x:Type local:Routes}" />
        </BlazorWebView.RootComponents>
    </BlazorWebView>

</ContentPage>

Pasul 3: Înregistrarea Serviciilor în MauiProgram.cs

Configurarea Dependency Injection se face în MauiProgram.cs. Dacă ai lucrat cu ASP.NET Core, totul va părea foarte familiar:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // Adăugăm serviciul MAUI Blazor WebView
        builder.Services.AddMauiBlazorWebView();

#if DEBUG
        // Developer tools doar în mod Debug
        builder.Services.AddBlazorWebViewDeveloperTools();
#endif

        // Înregistrăm serviciile noastre
        builder.Services.AddSingleton<IDeviceInfoService, MauiDeviceInfoService>();
        builder.Services.AddTransient<IApiService, ApiService>();

        return builder.Build();
    }
}

Partajarea UI-ului între Web și Mobil cu Razor Class Library

Asta e, fără exagerare, funcționalitatea care face toată arhitectura să merite. Ideea e simplă: scrii componentele Razor o singură dată în RCL și le folosești atât pe web, cât și pe mobil.

Dar ce faci când platforma nativă are capabilități diferite față de web? Ei bine, aici intervine un pattern clasic.

Abstractizarea Diferențelor de Platformă cu Interfețe

Soluția e una pe care probabil o știi deja: definești o interfață în RCL, apoi oferi implementări diferite pentru MAUI și web. Să luăm un exemplu concret — un serviciu care returnează informații despre dispozitiv:

// În RCL (MyHybridApp.Shared)
public interface IDeviceInfoService
{
    string GetPlatformName();
    string GetDeviceModel();
    bool HasCamera();
}

// În proiectul MAUI (MyHybridApp.Maui)
public class MauiDeviceInfoService : IDeviceInfoService
{
    public string GetPlatformName()
        => DeviceInfo.Current.Platform.ToString();

    public string GetDeviceModel()
        => DeviceInfo.Current.Model;

    public bool HasCamera()
        => MediaPicker.Default.IsCaptureSupported;
}

// În proiectul Web (MyHybridApp.Web)
public class WebDeviceInfoService : IDeviceInfoService
{
    public string GetPlatformName() => "Web Browser";
    public string GetDeviceModel() => "N/A";
    public bool HasCamera() => false;
}

Componenta Razor Partajată

Componenta din RCL injectează interfața fără să știe dacă rulează pe MAUI sau pe web. Asta e frumusețea abstractizării:

@* În RCL: Components/Pages/DeviceDetails.razor *@
@page "/device-details"
@inject IDeviceInfoService DeviceInfo

<h3>Informații Dispozitiv</h3>

<div class="device-card">
    <p><strong>Platformă:</strong> @DeviceInfo.GetPlatformName()</p>
    <p><strong>Model:</strong> @DeviceInfo.GetDeviceModel()</p>
    <p><strong>Cameră:</strong>
        @(DeviceInfo.HasCamera() ? "Disponibilă" : "Indisponibilă")
    </p>
</div>

@if (DeviceInfo.HasCamera())
{
    <button class="btn btn-primary"
            @onclick="CapturePhoto">
        Fotografiază
    </button>
}

@code {
    private async Task CapturePhoto()
    {
        // Logica de captură — implementată doar pe MAUI
    }
}

Considerații Importante pentru Render Mode

Un aspect pe care mulți îl trec cu vederea: când partajezi componente între MAUI și web, modul de randare contează enorm. Aplicațiile MAUI rulează mereu interactiv — dacă o componentă Razor specifică explicit un render mode (cum ar fi @rendermode InteractiveServer), aplicația MAUI va arunca o excepție. Am pățit-o personal și m-a costat câteva ore de debugging.

Soluția: setează Interactivity location la Global în proiectul web, astfel încât componentele să nu fie nevoite să declare explicit modul de randare.

Accesul la API-uri Native din Componentele Blazor

Aici e probabil cel mai mare avantaj al Blazor Hybrid față de o aplicație web pură. Ai acces complet la API-urile native ale dispozitivului — cameră, GPS, senzori, sistem de fișiere, notificări — totul direct din C#, fără JavaScript interop.

Sincer, asta singură justifică alegerea Blazor Hybrid pentru multe proiecte.

Exemplu: Geolocalizare

// Serviciu de geolocalizare pentru MAUI
public class MauiLocationService : ILocationService
{
    public async Task<LocationResult?> GetCurrentLocationAsync()
    {
        try
        {
            var status = await Permissions.CheckStatusAsync
                <Permissions.LocationWhenInUse>();

            if (status != PermissionStatus.Granted)
            {
                status = await Permissions.RequestAsync
                    <Permissions.LocationWhenInUse>();

                if (status != PermissionStatus.Granted)
                    return null;
            }

            var location = await Geolocation.Default.GetLocationAsync(
                new GeolocationRequest
                {
                    DesiredAccuracy = GeolocationAccuracy.High,
                    Timeout = TimeSpan.FromSeconds(15)
                });

            if (location is not null)
            {
                return new LocationResult
                {
                    Latitude = location.Latitude,
                    Longitude = location.Longitude,
                    Accuracy = location.Accuracy ?? 0
                };
            }
        }
        catch (FeatureNotSupportedException)
        {
            // GPS-ul nu este disponibil pe acest dispozitiv
        }
        catch (PermissionException)
        {
            // Permisiunile au fost refuzate
        }

        return null;
    }
}

// Model partajat (în RCL)
public class LocationResult
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public double Accuracy { get; set; }
}

Exemplu: Captură Foto cu MediaPicker

Începând cu .NET 10, MediaPicker suportă selecția multiplă de fișiere și compresia imaginilor direct în API. O îmbunătățire binevenită, pentru că anterior trebuia să implementezi asta manual:

public class MauiPhotoService : IPhotoService
{
    public async Task<Stream?> CapturePhotoAsync()
    {
        if (!MediaPicker.Default.IsCaptureSupported)
            return null;

        var photo = await MediaPicker.Default.CapturePhotoAsync(
            new MediaPickerOptions
            {
                Title = "Fă o fotografie"
            });

        if (photo is null)
            return null;

        return await photo.OpenReadAsync();
    }

    public async Task<List<Stream>> PickMultiplePhotosAsync()
    {
        var results = new List<Stream>();
        var photos = await FilePicker.Default.PickMultipleAsync(
            new PickOptions
            {
                PickerTitle = "Selectează fotografii",
                FileTypes = FilePickerFileType.Images
            });

        foreach (var photo in photos)
        {
            results.Add(await photo.OpenReadAsync());
        }

        return results;
    }
}

Apelarea Serviciilor Native cu TryDispatchAsync

BlazorWebView oferă metoda TryDispatchAsync, care permite codului nativ MAUI să acceseze serviciile scoped disponibile în componentele Razor. E util când trebuie să invoci navigarea Blazor sau alt serviciu scoped din codul MAUI nativ:

// Din code-behind-ul paginii MAUI
await blazorWebView.TryDispatchAsync(async serviceProvider =>
{
    var navigationManager = serviceProvider
        .GetRequiredService<NavigationManager>();
    navigationManager.NavigateTo("/notifications");
});

Optimizarea Performanței în Aplicații Blazor Hybrid

Performanța e domeniul unde Blazor Hybrid poate să exceleze sau să dezamăgească — și depinde aproape în totalitate de alegerile arhitecturale pe care le faci din start. Iată cele mai eficiente tehnici, testate în aplicații reale de producție.

1. Activarea Compilării AOT

NativeAOT elimină nevoia de JIT la runtime, ceea ce reduce dramatic timpul de pornire. Pe iOS și macOS, configurarea e directă:

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier
    ('$(TargetFramework)')) == 'ios'">
    <PublishAot>true</PublishAot>
    <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

Pe Android, recomandarea e să folosești Profiled AOT — un compromis bun între dimensiunea aplicației și viteza de execuție.

2. Lazy Loading al Componentelor

Dacă toate componentele sunt încărcate în avans, utilizatorii vor aștepta mai mult la pornire. E o problemă reală, mai ales pe dispozitive mai vechi. Soluția: încărcarea leneșă a componentelor care nu sunt necesare imediat:

@* Încarcă componenta doar când este vizibilă *@
@if (_showDashboard)
{
    <DynamicComponent Type="@typeof(DashboardComponent)" />
}

@code {
    private bool _showDashboard = false;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // Afișăm dashboard-ul după ce pagina principală s-a randat
            await Task.Delay(100);
            _showDashboard = true;
            StateHasChanged();
        }
    }
}

3. Virtualizarea Listelor Lungi

Blazor oferă componenta Virtualize care randează doar elementele vizibile pe ecran. Pentru liste cu sute sau mii de elemente, diferența de performanță e pur și simplu enormă:

<Virtualize Items="@_products" Context="product"
            ItemSize="72" OverscanCount="5">
    <div class="product-item">
        <img src="@product.ThumbnailUrl"
             loading="lazy" alt="@product.Name" />
        <h4>@product.Name</h4>
        <span class="price">@product.Price.ToString("C")</span>
    </div>
</Virtualize>

4. Optimizarea Asset-urilor Web

Fișierele din wwwroot sunt încărcate de WebView la pornire. Merită să le minimizezi cât poți:

  • Comprimă CSS-ul și elimină stilurile nefolosite
  • Folosește formate de imagine moderne (WebP, AVIF) cu fallback
  • Adaugă atributul loading="lazy" pe imagini
  • Evită biblioteci JavaScript mari — ai deja C# pentru logică

5. Gestionarea Corectă a Threading-ului

Componentele Blazor se randează pe thread-ul UI. Operațiunile lungi (apeluri API, procesare date) trebuie executate asincron. Altfel, blocaj garantat:

// GREȘIT — blochează thread-ul UI
protected override void OnInitialized()
{
    _data = _apiService.GetData().Result; // NICIODATĂ!
}

// CORECT — operație asincronă
protected override async Task OnInitializedAsync()
{
    _isLoading = true;
    _data = await _apiService.GetDataAsync();
    _isLoading = false;
}

Pare evident, dar am văzut această greșeală în destule proiecte reale. Nu bloca niciodată thread-ul UI cu .Result sau .Wait().

Blazor Hybrid vs. Alte Framework-uri Cross-Platform în 2026

Întrebarea pe care toată lumea o pune: cum se compară Blazor Hybrid cu celelalte soluții populare? Iată o comparație pe care am încercat s-o fac cât mai obiectivă:

Criteriu Blazor Hybrid (.NET MAUI) Flutter React Native
Limbaj C# / Razor / HTML / CSS Dart JavaScript / TypeScript
Performanță startup ~2.3s (cu AOT) ~1.5s ~2.0s
Consum memorie idle ~85 MB ~60 MB ~80 MB
Reutilizare cod 70-80% (web + mobil) 90%+ (doar mobil/desktop) 85%+ (doar mobil)
Acces API-uri native Complet (prin .NET MAUI) Plugin-uri / canale platformă Native modules / bridge
Suport offline Complet Complet Complet
Partajare cu web Da (prin RCL) Flutter Web (cu limitări) React Web (librării diferite)
Ecosistem enterprise Excelent (.NET, Azure, Microsoft) În creștere Matur (Meta, comunitate JS)

Când să alegi Blazor Hybrid:

  • Echipa ta are expertiză .NET / C#
  • Ai nevoie să partajezi UI între o aplicație web și una nativă
  • Proiectul necesită integrare cu ecosistemul Microsoft (Azure, SQL Server, Active Directory)
  • Construiești aplicații enterprise sau instrumente interne

Când să alegi altceva:

  • Flutter — dacă ai nevoie de animații complexe și UI pixel-perfect identic pe toate platformele
  • React Native — dacă echipa ta e puternică pe JavaScript și ai un ecosistem React existent

Niciun framework nu e perfect pentru toate scenariile. Alegerea corectă depinde mereu de context — echipă, proiect, termen limită.

Interceptarea Cererilor Web în BlazorWebView (.NET 10)

Una dintre funcționalitățile noi și chiar utile din .NET 10 e posibilitatea de a intercepta cererile HTTP făcute din BlazorWebView și HybridWebView. Asta deschide ușa pentru scenarii destul de avansate:

  • Adăugarea de headere personalizate (token de autorizare, CSRF)
  • Injectarea de răspunsuri custom pentru anumite resurse
  • Caching la nivel nativ
  • Redirecționarea cererilor în funcție de conectivitate
// Interceptarea cererilor web în .NET 10
blazorWebView.UrlLoading += (sender, args) =>
{
    // Verificăm dacă cererea este către un API extern
    if (args.Url.Host == "api.myservice.com")
    {
        // Putem modifica cererea sau o putem trata nativ
        args.UrlLoadingStrategy =
            UrlLoadingStrategy.OpenInWebView;
    }
    else if (args.Url.Scheme == "https"
             && !args.Url.Host.Contains("localhost"))
    {
        // Link-uri externe — deschidem în browser-ul implicit
        args.UrlLoadingStrategy =
            UrlLoadingStrategy.OpenExternally;
    }
};

Publicarea Aplicației în Magazinele de Aplicații

Aplicațiile Blazor Hybrid sunt aplicații .NET MAUI native complete, deci procesul de publicare e identic cu orice altă aplicație MAUI. Nu te aștepta la surprize aici.

Android (Google Play Store)

  1. Generează un Android App Bundle (.aab) în mod Release
  2. Semnează cu un keystore propriu
  3. Încarcă în Google Play Console și completează declarațiile de confidențialitate
  4. Folosește track-uri de testare (intern, închis, deschis) pentru validare
# Publicare Android din CLI
dotnet publish -f net10.0-android -c Release \
    -p:AndroidKeyStore=true \
    -p:AndroidSigningKeyStore=myapp.keystore \
    -p:AndroidSigningKeyAlias=mykey \
    -p:AndroidSigningKeyPass=parola_mea \
    -p:AndroidSigningStorePass=parola_store

iOS (App Store)

  1. Creează un provisioning profile de distribuție în Apple Developer Portal
  2. Configurează Privacy Manifest — obligatoriu în 2026 pentru aprobarea Apple
  3. Construiește pe Mac (local sau remote)
  4. Publică prin Visual Studio sau Xcode, apoi încarcă în App Store Connect

Un sfat din experiență: certificatele de provisioning iOS expiră anual. Configurează alerte în pipeline-ul CI/CD ca să nu fii luat prin surprindere. E genul de problemă care apare mereu vineri seara.

Configurare CI/CD

GitHub Actions și Azure DevOps sunt cele mai populare opțiuni pentru automatizarea build-urilor MAUI Blazor Hybrid. Pipeline-urile .NET 10 sunt compatibile cu agenții de build moderni și suportă semnarea automată pentru ambele platforme.

Depanarea și Rezolvarea Problemelor Comune

Conflicte de Workload pe Windows

Asta e o capcană clasică. Dacă rulezi dotnet workload install după ce ai instalat .NET MAUI prin Visual Studio, poate apărea un conflict. Visual Studio nu mai găsește workload-urile și totul se strică. Soluția: dezinstalează workload-urile prin CLI, elimină SDK-urile .NET din Control Panel, apoi reinstalează totul prin Visual Studio.

Problema SDK x86 vs x64

Eroarea „Platform version is not present for one or more target frameworks" apare de obicei când SDK-ul .NET x86 e rezolvat în loc de cel x64. Verifică variabila PATH și asigură-te că SDK-ul x64 are prioritate. E o problemă frustrantă dar ușor de rezolvat odată ce știi cauza.

Logging și Diagnosticare în BlazorWebView

BlazorWebView are logging integrat care te ajută la depanare. Activează-l în MauiProgram.cs:

#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
builder.Logging.SetMinimumLevel(LogLevel.Debug);
#endif

Întrebări Frecvente (FAQ)

Pot partaja codul UI între o aplicație MAUI Blazor Hybrid și o aplicație web Blazor?

Da, absolut. Folosind o Razor Class Library (RCL) partajată, poți scrie componentele Razor o singură dată și le poți referenția atât din proiectul MAUI, cât și din proiectul Blazor Web App. Template-ul oficial „.NET MAUI Blazor Hybrid and Web App" configurează automat această structură.

Aplicațiile Blazor Hybrid au acces la funcționalitățile native ale dispozitivului?

Da, complet. Componentele Blazor rulează în procesul .NET al aplicației MAUI (nu în browser), deci ai acces la toate API-urile .NET MAUI: cameră, GPS, senzori, sistem de fișiere, notificări push, autentificare biometrică și multe altele. Nu e nevoie de JavaScript interop.

Ce diferență este între Blazor Hybrid și Blazor WebAssembly?

Blazor WebAssembly rulează în browser folosind WebAssembly — e practic o aplicație web. Blazor Hybrid rulează într-un BlazorWebView încorporat într-o aplicație nativă (.NET MAUI, WPF sau WinForms). Diferența principală: Hybrid are acces la API-uri native și poate fi publicat în App Store / Play Store, în timp ce WebAssembly e limitată la ce poate face browserul.

Blazor Hybrid e pregătit pentru producție în 2026?

Da. Cu .NET 10 (versiune LTS cu suport până în noiembrie 2028), Microsoft a consolidat stabilitatea, performanța și tooling-ul. Blazor Hybrid e folosit de aplicații first-party Microsoft și de companii enterprise în producție. Suportul de la Syncfusion, Telerik și DevExpress confirmă maturitatea framework-ului.

Pot publica o aplicație Blazor Hybrid în App Store și Google Play?

Da. Aplicațiile Blazor Hybrid sunt aplicații .NET MAUI native standard. Generează pachete native (.apk/.aab pentru Android, .ipa pentru iOS) și pot fi publicate în toate magazinele de aplicații. Procesul e identic cu cel al oricărei aplicații .NET MAUI.

Despre Autor Editorial Team

Our team of expert writers and editors.