Introdução ao Blazor Hybrid no .NET MAUI
Se você trabalha com desenvolvimento multiplataforma, provavelmente já sentiu aquela frustração de precisar manter bases de código separadas para web, mobile e desktop. Pois é — nos últimos anos, esse cenário mudou bastante. Com o .NET MAUI e a integração do Blazor Hybrid, dá pra combinar HTML, CSS e JavaScript com capacidades nativas dos dispositivos de uma forma que, sinceramente, é bem impressionante.
Mas o que torna o Blazor Hybrid diferente de verdade?
Ao contrário do Blazor WebAssembly ou Blazor Server, aqui os componentes Razor são executados nativamente no processo .NET da aplicação. Eles renderizam dentro de um controlo WebView incorporado, mas todo o processamento acontece no dispositivo. Isso significa que a aplicação é 100% nativa — instalada no dispositivo, com acesso a APIs nativas, sem servidor web e com funcionamento totalmente offline.
Neste guia, vamos percorrer desde os fundamentos da arquitetura até exemplos práticos avançados, incluindo o HybridWebView introduzido no .NET MAUI 9, interoperabilidade JavaScript-C#, boas práticas de arquitetura e as novidades do .NET 10. Então, vamos lá.
Arquitetura do Blazor Hybrid: Como Funciona por Dentro
Para aproveitar ao máximo o Blazor Hybrid, vale a pena entender como a arquitetura funciona internamente. O componente central é o BlazorWebView, um controlo .NET MAUI que hospeda componentes Blazor dentro de uma WebView nativa.
O Papel do BlazorWebView
O BlazorWebView funciona como uma ponte entre o mundo nativo e o mundo web. Quando a aplicação arranca, ele carrega uma página HTML base (normalmente wwwroot/index.html) e inicializa o runtime do Blazor. Os componentes Razor são renderizados dentro desta WebView, mas — e aqui está o ponto crucial — todo o código C# executa nativamente, não no navegador.
A arquitetura divide-se em três camadas:
- Camada Nativa (.NET MAUI): Gere o ciclo de vida da aplicação, navegação, acesso a APIs do dispositivo (câmara, GPS, sensores) e integração com o sistema operativo.
- Camada de Ponte (BlazorWebView): Hospeda o motor web nativo da plataforma (WebView2 no Windows, WKWebView no iOS/macOS, WebView no Android) e faz a comunicação bidirecional entre o código nativo e a interface web.
- Camada de Interface (Razor/HTML/CSS): Define a interface do utilizador usando componentes Razor, renderizados como HTML no WebView.
Fluxo de Execução
Quando um utilizador clica num botão, por exemplo, o evento é capturado pelo JavaScript no WebView e transmitido ao runtime do Blazor que está a executar nativamente. O código C# processa o evento, atualiza o estado do componente e as alterações de DOM são aplicadas de volta ao WebView. Tudo isto acontece localmente, sem qualquer comunicação de rede. Na prática, a experiência é fluida e responsiva.
Configuração Inicial de um Projeto Blazor Hybrid
Vamos à parte prática. Para criar um projeto Blazor Hybrid com .NET MAUI, precisa do Visual Studio 2022 (v17.14 ou superior) ou Visual Studio 2026 com o workload .NET MAUI instalado.
Criação do Projeto
Pode criar o projeto usando o template .NET MAUI Blazor Hybrid App no Visual Studio, ou pela linha de comandos (que eu pessoalmente prefiro):
dotnet new maui-blazor -n MinhaAppHibrida
cd MinhaAppHibrida
dotnet build
Este comando gera um projeto completo com a estrutura de pastas necessária, incluindo a pasta wwwroot para conteúdo web estático e componentes Razor de exemplo.
Estrutura do Projeto
A estrutura típica fica assim:
Components/— Componentes Razor da aplicaçãoComponents/Layout/— Layouts partilhados (MainLayout, NavMenu)Components/Pages/— Páginas com rotas definidasResources/Raw/wwwroot/— Conteúdo web estático (CSS, imagens)wwwroot/index.html— Página HTML base do BlazorMauiProgram.cs— Ponto de entrada e configuração de serviçosMainPage.xaml— Página MAUI que hospeda o BlazorWebView
Registo do BlazorWebView no MauiProgram.cs
O ficheiro MauiProgram.cs é onde configuramos tudo — serviços, fontes e o próprio BlazorWebView:
using Microsoft.Extensions.Logging;
namespace MinhaAppHibrida;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Registo do BlazorWebView
builder.Services.AddMauiBlazorWebView();
#if DEBUG
// Ferramentas de desenvolvimento para depuração
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
// Registo de serviços da aplicação
builder.Services.AddSingleton<IApiService, ApiService>();
builder.Services.AddScoped<IEstadoApp, EstadoApp>();
return builder.Build();
}
}
Configuração do BlazorWebView na MainPage.xaml
Na página principal XAML, o BlazorWebView é declarado como qualquer outro controlo MAUI:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MinhaAppHibrida"
x:Class="MinhaAppHibrida.MainPage">
<BlazorWebView HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app"
ComponentType="{x:Type local:Components.Routes}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
</ContentPage>
O HostPage aponta para o HTML base e o RootComponent define qual componente Razor será renderizado no elemento com o seletor #app. Simples assim.
Componentes Razor Práticos para Apps Móveis
Agora vamos ao que interessa — criar componentes Razor que resolvem problemas reais em apps móveis híbridas.
Componente de Lista com Carregamento Dinâmico
Um padrão que aparece em praticamente toda app móvel é a lista com carregamento progressivo. Veja como fica com Blazor:
@page "/produtos"
@inject IApiService ApiService
<h3>Catálogo de Produtos</h3>
@if (produtos == null)
{
<div class="loading-spinner">
<p>A carregar produtos...</p>
</div>
}
else
{
<div class="lista-produtos">
@foreach (var produto in produtos)
{
<div class="card-produto" @onclick="() => SelecionarProduto(produto)">
<img src="@produto.ImagemUrl" alt="@produto.Nome" loading="lazy" />
<h4>@produto.Nome</h4>
<p class="preco">€@produto.Preco.ToString("F2")</p>
</div>
}
</div>
@if (temMaisProdutos)
{
<button class="btn-carregar-mais" @onclick="CarregarMais">
Carregar Mais
</button>
}
}
@code {
private List<Produto>? produtos;
private bool temMaisProdutos = true;
private int pagina = 1;
protected override async Task OnInitializedAsync()
{
produtos = await ApiService.ObterProdutosAsync(pagina, 20);
}
private async Task CarregarMais()
{
pagina++;
var novosProdutos = await ApiService.ObterProdutosAsync(pagina, 20);
if (novosProdutos.Count == 0)
{
temMaisProdutos = false;
return;
}
produtos!.AddRange(novosProdutos);
}
private void SelecionarProduto(Produto produto)
{
// Navegar para detalhes do produto
}
}
Repare como o padrão é direto — carregar uma página de itens, e quando o utilizador pede mais, buscar a próxima. Nada de complicado.
Componente de Formulário com Validação
Formulários são o pão de cada dia de qualquer aplicação. O Blazor facilita bastante a validação com DataAnnotations:
@page "/perfil"
@using System.ComponentModel.DataAnnotations
<h3>Editar Perfil</h3>
<EditForm Model="@perfil" OnValidSubmit="@GuardarPerfil">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="campo-formulario">
<label>Nome Completo</label>
<InputText @bind-Value="perfil.Nome" class="input-texto" />
<ValidationMessage For="@(() => perfil.Nome)" />
</div>
<div class="campo-formulario">
<label>Email</label>
<InputText @bind-Value="perfil.Email" class="input-texto" />
<ValidationMessage For="@(() => perfil.Email)" />
</div>
<div class="campo-formulario">
<label>Telefone</label>
<InputText @bind-Value="perfil.Telefone" class="input-texto" />
<ValidationMessage For="@(() => perfil.Telefone)" />
</div>
<button type="submit" class="btn-guardar">Guardar Alterações</button>
</EditForm>
@code {
private PerfilModel perfil = new();
private async Task GuardarPerfil()
{
// Lógica para guardar o perfil
await Task.CompletedTask;
}
public class PerfilModel
{
[Required(ErrorMessage = "O nome é obrigatório")]
[StringLength(100, ErrorMessage = "O nome não pode exceder 100 caracteres")]
public string Nome { get; set; } = string.Empty;
[Required(ErrorMessage = "O email é obrigatório")]
[EmailAddress(ErrorMessage = "Formato de email inválido")]
public string Email { get; set; } = string.Empty;
[Phone(ErrorMessage = "Formato de telefone inválido")]
public string Telefone { get; set; } = string.Empty;
}
}
HybridWebView: Integrando Aplicações Web Existentes
Esta é, na minha opinião, uma das adições mais interessantes do .NET MAUI 9. Enquanto o BlazorWebView serve para construir interfaces novas com Razor, o HybridWebView permite integrar aplicações web já existentes — React, Angular, Vue.js, ou qualquer framework JavaScript — diretamente numa app .NET MAUI nativa.
Se a sua empresa já tem uma app web funcional e quer levá-la para mobile sem reescrever tudo, este controlo é a solução.
BlazorWebView vs. HybridWebView
Vale esclarecer a diferença entre os dois:
- BlazorWebView: Hospeda componentes Razor executados nativamente no processo .NET. Perfeito para interfaces novas construídas com C# e Razor.
- HybridWebView: Hospeda conteúdo web estático (HTML/JS/CSS) e facilita a comunicação bidirecional entre JavaScript e C#. Ideal para reaproveitar aplicações web existentes.
Configuração do HybridWebView
O conteúdo web deve ser colocado na pasta Resources/Raw/wwwroot do projeto, com o index.html como ficheiro principal.
No XAML, fica assim:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MinhaApp.HybridPage">
<HybridWebView x:Name="hybridView"
HybridRoot="wwwroot"
DefaultFile="index.html"
RawMessageReceived="OnMensagemRecebida" />
</ContentPage>
Interoperabilidade JavaScript ↔ C#
Aqui é onde as coisas ficam realmente interessantes. A comunicação bidirecional entre JavaScript e C# funciona de duas formas:
1. C# a Invocar JavaScript
O método InvokeJavaScriptAsync permite chamar funções JavaScript a partir do C#, com passagem de parâmetros e retorno tipado:
// No code-behind C#
private async void OnAlterarTemaClicked(object sender, EventArgs e)
{
// Chamar função JavaScript e obter resultado tipado
var corAtual = await hybridView.InvokeJavaScriptAsync<string>(
"alterarTema",
HybridJSContext.Default.String,
new object[] { "escuro" },
new[] { HybridJSContext.Default.String }
);
await DisplayAlert("Tema Alterado", $"Cor atual: {corAtual}", "OK");
}
// Alternativa simples com EvaluateJavaScriptAsync
private async void OnObterDadosClicked(object sender, EventArgs e)
{
var resultado = await hybridView.EvaluateJavaScriptAsync(
"document.getElementById('titulo').innerText"
);
}
2. JavaScript a Invocar C#
Do lado do JavaScript, enviam-se mensagens para o C# através do mecanismo nativo do HybridWebView:
// No ficheiro JavaScript (index.js)
function enviarDadosParaCSharp() {
const dados = {
utilizador: document.getElementById("nomeInput").value,
acao: "guardar_perfil",
timestamp: new Date().toISOString()
};
// Enviar mensagem crua para C#
HybridWebView.SendRawMessageToDotNet(JSON.stringify(dados));
}
// Função chamada pelo C# via InvokeJavaScriptAsync
function alterarTema(modo) {
if (modo === "escuro") {
document.body.classList.add("tema-escuro");
return "#1a1a2e";
} else {
document.body.classList.remove("tema-escuro");
return "#ffffff";
}
}
No C#, o evento RawMessageReceived captura essas mensagens:
private void OnMensagemRecebida(object? sender,
HybridWebViewRawMessageReceivedEventArgs e)
{
var mensagem = e.Message;
var dados = JsonSerializer.Deserialize<DadosUtilizador>(mensagem);
// Processar os dados recebidos do JavaScript
MainThread.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("Dados Recebidos",
$"Utilizador: {dados?.Utilizador}", "OK");
});
}
Honestamente, quando vi esta interoperabilidade a funcionar pela primeira vez num projeto real, fiquei impressionado com a fluidez. A latência é praticamente imperceptível.
Partilha de Código com Razor Class Library (RCL)
Uma das maiores vantagens do ecossistema Blazor Hybrid é poder partilhar componentes entre a aplicação web e a aplicação .NET MAUI. A peça-chave para isso é a Razor Class Library (RCL).
Criação da Biblioteca Partilhada
dotnet new razorclasslib -n MinhaApp.ComponentesPartilhados
dotnet sln add MinhaApp.ComponentesPartilhados
Dentro da RCL, crie componentes que funcionem nos dois contextos:
// MinhaApp.ComponentesPartilhados/Componentes/CartaoInfo.razor
<div class="cartao-info @ClasseCSS">
<div class="cartao-cabecalho">
<h4>@Titulo</h4>
@if (MostrarIcone)
{
<span class="icone">@Icone</span>
}
</div>
<div class="cartao-corpo">
@ChildContent
</div>
@if (OnAcao.HasDelegate)
{
<div class="cartao-rodape">
<button class="btn-acao" @onclick="OnAcao">@TextoAcao</button>
</div>
}
</div>
@code {
[Parameter] public string Titulo { get; set; } = string.Empty;
[Parameter] public string Icone { get; set; } = string.Empty;
[Parameter] public bool MostrarIcone { get; set; } = true;
[Parameter] public string ClasseCSS { get; set; } = string.Empty;
[Parameter] public string TextoAcao { get; set; } = "Ver Mais";
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public EventCallback OnAcao { get; set; }
}
Referência nos Projetos
Tanto o projeto MAUI como o projeto Web Blazor referenciam a mesma RCL no ficheiro .csproj:
<!-- No .csproj do projeto MAUI e do projeto Web -->
<ProjectReference Include="..\MinhaApp.ComponentesPartilhados\MinhaApp.ComponentesPartilhados.csproj" />
Na prática, isto traduz-se numa redução significativa do tempo de desenvolvimento. Equipas que adotaram esta abordagem reportam ganhos de produtividade na ordem dos 40-45%, o que faz todo o sentido quando se pensa em componentes escritos uma vez e usados em múltiplas plataformas.
Melhores Práticas de Arquitetura
Para tirar o máximo do Blazor Hybrid, há algumas práticas de arquitetura que vale a pena seguir desde o início do projeto.
1. Clean Architecture com Injeção de Dependências
Separar responsabilidades é fundamental. Defina interfaces no projeto partilhado e implemente-as de forma específica para cada plataforma:
// Definição de interface no projeto partilhado
public interface IServicoAutenticacao
{
Task<ResultadoLogin> LoginAsync(string email, string senha);
Task LogoutAsync();
Task<bool> EstaAutenticadoAsync();
}
// Implementação específica para MAUI
public class ServicoAutenticacaoMAUI : IServicoAutenticacao
{
private readonly ISecureStorage _armazenamentoSeguro;
public ServicoAutenticacaoMAUI(ISecureStorage armazenamentoSeguro)
{
_armazenamentoSeguro = armazenamentoSeguro;
}
public async Task<ResultadoLogin> LoginAsync(string email, string senha)
{
// Implementação com armazenamento seguro nativo
var token = await ObterTokenDoServidor(email, senha);
await _armazenamentoSeguro.SetAsync("auth_token", token);
return new ResultadoLogin { Sucesso = true };
}
public async Task<bool> EstaAutenticadoAsync()
{
var token = await _armazenamentoSeguro.GetAsync("auth_token");
return !string.IsNullOrEmpty(token);
}
public async Task LogoutAsync()
{
_armazenamentoSeguro.Remove("auth_token");
await Task.CompletedTask;
}
}
Este padrão facilita bastante quando precisa trocar a implementação para testes ou para diferentes plataformas.
2. Controlos Nativos Onde a Performance Importa
O Blazor Hybrid permite misturar controlos nativos MAUI com componentes Razor na mesma aplicação. A regra prática é simples: para elementos que exigem alto desempenho (listas longas com scroll, animações complexas), prefira os controlos nativos como CollectionView. Para formulários, dashboards e interfaces ricas em conteúdo, os componentes Razor são a escolha certa.
3. Estratégia Offline-First
Uma grande vantagem do Blazor Hybrid é que toda a aplicação, incluindo o conteúdo web, é empacotada e executada localmente. Tire partido disso para implementar uma abordagem offline-first:
public class ServicoSincronizacao
{
private readonly SQLiteAsyncConnection _dbLocal;
private readonly IApiService _api;
private readonly IConnectivity _conectividade;
public ServicoSincronizacao(
SQLiteAsyncConnection dbLocal,
IApiService api,
IConnectivity conectividade)
{
_dbLocal = dbLocal;
_api = api;
_conectividade = conectividade;
}
public async Task<List<Produto>> ObterProdutosAsync()
{
if (_conectividade.NetworkAccess == NetworkAccess.Internet)
{
var produtosRemotos = await _api.ObterProdutosAsync();
await _dbLocal.InsertAllAsync(produtosRemotos);
return produtosRemotos;
}
// Sem rede: retornar dados locais
return await _dbLocal.Table<Produto>().ToListAsync();
}
}
Isto garante que a app funciona mesmo sem ligação à internet — algo que os utilizadores realmente valorizam.
4. Gestão de Estado
Um cuidado importante: evite o uso excessivo de CascadingParameters em componentes profundamente aninhados, pois pode provocar re-renderizações desnecessárias. Em vez disso, considere o padrão WeakReferenceMessenger do CommunityToolkit.Mvvm. Isto é especialmente relevante agora que o MessagingCenter foi tornado interno no .NET MAUI 10.
Novidades do .NET MAUI 10 para Blazor Hybrid
O .NET 10, lançado em novembro de 2025 como versão LTS, trouxe melhorias que vale a pena conhecer se está a trabalhar com Blazor Hybrid.
Integração com .NET Aspire
Esta é provavelmente a novidade mais relevante. Um novo template de projeto cria um projeto de serviços .NET Aspire para .NET MAUI, trazendo capacidades cloud-native como telemetria, descoberta de serviços e gestão de configuração para as aplicações móveis. Na prática, permite monitorizar a saúde da aplicação em tempo real e integrar com dashboards de observabilidade.
Melhorias no HybridWebView
O .NET 10 adiciona uma sobrecarga simplificada do InvokeJavaScriptAsync que dispensa a especificação do tipo de retorno. Perfeito para quando só quer executar uma ação sem precisar de resultado:
// .NET 10 — Nova sobrecarga simplificada
await hybridView.InvokeJavaScriptAsync("mostrarNotificacao", "Dados guardados!");
// Versus a abordagem anterior que exigia tipagem
await hybridView.InvokeJavaScriptAsync<string>(
"mostrarNotificacao",
HybridJSContext.Default.String,
new object[] { "Dados guardados!" },
new[] { HybridJSContext.Default.String }
);
Pode parecer pouca coisa, mas quando se tem dezenas de chamadas de interop no código, esta simplificação faz diferença.
CollectionView como Handler Padrão
Os handlers para CollectionView e CarouselView baseados em UICollectionView no iOS e Mac Catalyst, que eram opcionais no .NET 9, passaram a ser os handlers padrão no .NET 10. Resultado: melhorias notáveis de performance e estabilidade para listas, tanto em interfaces nativas como em contextos híbridos.
Depreciações Importantes
Se está a atualizar para o .NET 10, tenha atenção a estas mudanças:
- ListView e TableView estão depreciados — migre para
CollectionView. - MessagingCenter foi tornado interno — use
WeakReferenceMessengerdo CommunityToolkit.Mvvm. - Métodos de animação como
FadeTo,RotateTo,ScaleToforam substituídos por versões assíncronas (FadeToAsync,RotateToAsync,ScaleToAsync).
XAML Source Generation e Namespaces Globais
Outra novidade interessante: o .NET 10 introduz namespaces XAML globais e geração de código XAML em tempo de compilação. Ativa-se assim:
<!-- No ficheiro .csproj -->
<PropertyGroup>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>
O resultado são tempos de inflação de vistas mais rápidos e melhor desempenho geral na depuração.
Otimização de Performance em Apps Blazor Hybrid
A performance é crítica em aplicações móveis. Ninguém gosta de uma app lenta. Aqui ficam estratégias que realmente funcionam.
Compilação AOT e IL Trimming
A compilação Ahead-of-Time (AOT) transforma o código intermédio em código nativo antes da execução, eliminando a necessidade de compilação JIT no dispositivo. Combinada com IL Trimming, pode reduzir bastante o tamanho da aplicação e melhorar os tempos de arranque:
<PropertyGroup>
<RunAOTCompilation>true</RunAOTCompilation>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
Atenção: O HybridWebView não está disponível por defeito quando o trimming completo ou Native AOT estão ativados. Consulte a documentação sobre feature switches antes de ativar estas opções.
Otimização de Componentes Razor
Componentes pesados com HTML profundamente aninhado podem prejudicar a renderização. Algumas práticas que ajudam:
- Virtualização: Use o componente
<Virtualize>para listas longas em vez de renderizar tudo de uma vez. - ShouldRender: Implemente
ShouldRender()em componentes que não precisam de re-renderização frequente. - StateHasChanged: Evite chamar
StateHasChanged()desnecessariamente — o Blazor já atualiza o UI automaticamente após eventos. - Lazy Loading: Carregue componentes pesados apenas quando necessário usando
@ifcondicional.
@* Exemplo de virtualização para listas longas *@
<Virtualize Items="@todosOsProdutos" Context="produto">
<div class="item-produto">
<span>@produto.Nome</span>
<span>€@produto.Preco.ToString("F2")</span>
</div>
</Virtualize>
Minimizar o Overhead do WebView
O WebView consome memória adicional. Para minimizar o impacto:
- Utilize estilos CSS leves — evite frameworks CSS pesados quando não são necessários.
- Minimize animações CSS complexas que causam reflows constantes.
- Limite chamadas síncronas pela ponte JavaScript-C#.
- Comprima e otimize imagens servidas dentro do WebView.
Diagnósticos e Métricas no .NET MAUI 10
O .NET MAUI 10 trouxe rastreamento abrangente de diagnósticos e métricas, com foco na monitorização de performance de layout. A arquitetura é extensível, o que é bom para futuras necessidades de observabilidade.
A combinação com o .NET Aspire dá uma visão completa da saúde da aplicação — desde métricas de arranque até tempos de renderização de componentes individuais. Para quem já trabalhou com aplicações em produção, ter este nível de visibilidade é inestimável.
Quando Escolher Blazor Hybrid vs. MAUI Nativo
Esta é uma pergunta que recebo com frequência, e a resposta depende do contexto:
- Escolha Blazor Hybrid quando: a equipa tem forte competência em tecnologias web; quer partilhar código entre web e mobile/desktop; o tempo de desenvolvimento é prioridade; a interface é rica em conteúdo (dashboards, formulários, relatórios).
- Escolha MAUI nativo quando: a performance é absolutamente crítica (jogos, animações complexas, processamento em tempo real); precisa de integração profunda com controlos nativos; a aplicação é primariamente de interação com hardware.
- Combine ambos quando: partes da app exigem performance nativa (listas com scroll, câmara, mapas) e outras beneficiam de interfaces web ricas (formulários, dashboards, configurações).
A abordagem híbrida é particularmente valiosa em contextos empresariais. A unificação de código entre web e mobile pode reduzir os custos de manutenção significativamente — e na minha experiência, a redução é real e mensurável.
Conclusão
O Blazor Hybrid com .NET MAUI é, sem dúvida, uma das abordagens mais pragmáticas para o desenvolvimento multiplataforma moderno. Combinar a maturidade do ecossistema Blazor com as capacidades nativas do .NET MAUI dá aos desenvolvedores o melhor dos dois mundos — interfaces ricas construídas com tecnologias web, executadas com a performance e acesso nativo que os utilizadores esperam.
Com o .NET 10 como versão LTS, a integração com .NET Aspire e as melhorias no HybridWebView, este é um bom momento para adotar ou aprofundar o uso do Blazor Hybrid. O meu conselho? Comece por um projeto de prova de conceito, experimente a partilha de componentes via RCL e explore a interoperabilidade entre JavaScript e C#. Os resultados vão falar por si.
Para dar os próximos passos, explore a documentação oficial do .NET MAUI no Microsoft Learn e experimente os templates disponíveis. O futuro do desenvolvimento multiplataforma é híbrido — e as ferramentas para lá chegar nunca estiveram tão maduras.