Partilhar via


HybridWebView

Navegar no exemplo. Navegar no exemplo

O .NET Multi-platform App UI (.NET MAUI) HybridWebView permite hospedar conteúdo HTML/JS/CSS arbitrário em uma exibição da Web e permite a comunicação entre o código na exibição da Web (JavaScript) e o código que hospeda a exibição da Web (C#/.NET). Por exemplo, se você tiver um aplicativo React JS existente, poderá hospedá-lo em um aplicativo nativo .NET MAUI de plataforma cruzada e criar o back-end do aplicativo usando C# e .NET.

HybridWebView define as seguintes propriedades:

  • DefaultFile, do tipo string?, que especifica o arquivo dentro do HybridRoot que deve ser servido como o arquivo padrão. O valor padrão é index.html.
  • HybridRoot, do tipo string?, que é o caminho dentro dos recursos de ativos não processados da aplicação que contêm o conteúdo da aplicação web. O valor padrão é wwwroot, que mapeia para Resources/Raw/wwwroot.

Além disso, HybridWebView define um evento RawMessageReceived que é gerado quando uma mensagem bruta é recebida. O objeto HybridWebViewRawMessageReceivedEventArgs que acompanha o evento define uma propriedade Message que contém a mensagem.

O código C# do seu aplicativo pode invocar métodos JavaScript síncronos e assíncronos dentro do HybridWebView com os métodos InvokeJavaScriptAsync e EvaluateJavaScriptAsync. O código JavaScript do seu aplicativo também pode invocar métodos C# síncronos e assíncronos. Para obter mais informações, consulte Invoke JavaScript from C# e Invoke C# from JavaScript.

Para criar um aplicativo .NET MAUI com HybridWebView você precisa:

  • O conteúdo da Web do aplicativo, que consiste em HTML estático, JavaScript, CSS, imagens e outros arquivos.
  • Um controle HybridWebView como parte da interface do usuário do aplicativo. Isso pode ser conseguido fazendo referência a ele no XAML do aplicativo.
  • Código no conteúdo da Web e em C#/.NET, que usa as APIs HybridWebView para enviar mensagens entre os dois componentes.

Todo o aplicativo, incluindo o conteúdo da Web, é empacotado e executado localmente em um dispositivo, e pode ser publicado em lojas de aplicativos aplicáveis. O conteúdo da Web é hospedado em um controle de exibição da Web nativo e é executado dentro do contexto do aplicativo. Qualquer parte da aplicação pode aceder a serviços Web externos, mas não é obrigada a fazê-lo.

Importante

Por padrão, o controle HybridWebView não estará disponível quando o corte completo ou o AOT nativo estiver habilitado. Para alterar esse comportamento, consulte Cortar opções de recurso.

Criar um aplicativo .NET MAUI HybridWebView

Para criar um aplicativo .NET MAUI com um HybridWebView:

  1. Abra um projeto de aplicativo .NET MAUI existente ou crie um novo projeto de aplicativo .NET MAUI.

  2. Adicione seu conteúdo da Web ao projeto do aplicativo .NET MAUI.

    O conteúdo da Web do seu aplicativo deve ser incluído como parte de um projeto .NET MAUI como ativos brutos. Um ativo bruto é qualquer ficheiro na pasta Resources\Raw da aplicação e inclui subpastas. Para um HybridWebViewpadrão, o conteúdo da web deve ser colocado na pasta Resources\Raw\wwwroot, com o arquivo principal chamado index.html.

    Um aplicativo simples pode ter os seguintes arquivos e conteúdo:

    • Recursos\Raw\wwwroot\index.html com conteúdo para a interface do usuário principal:

      <!DOCTYPE html>
      
      <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
      <head>
          <meta charset="utf-8" />
          <title></title>
          <link rel="icon" href="data:,">
          <link rel="stylesheet" href="styles/app.css">
          <script src="scripts/HybridWebView.js"></script>
          <script>
              function LogMessage(msg) {
                  var messageLog = document.getElementById("messageLog");
                  messageLog.value += '\r\n' + msg;
              }
      
              window.addEventListener(
                  "HybridWebViewMessageReceived",
                  function (e) {
                      LogMessage("Raw message: " + e.detail.message);
                  });
      
              function AddNumbers(a, b) {
                  var result = {
                      "result": a + b,
                      "operationName": "Addition"
                  };
                  return result;
              }
      
              var count = 0;
      
              async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) {
                  const response = await fetch("/asyncdata.txt");
                  if (!response.ok) {
                      throw new Error(`HTTP error: ${response.status}`);
                  }
                  var jsonData = await response.json();
      
                  jsonData[s1] = s2;
      
                  const msg = 'JSON data is available: ' + JSON.stringify(jsonData);
                  window.HybridWebView.SendRawMessage(msg)
      
                  return jsonData;
              }
      
              async function InvokeDoSyncWork() {
                  LogMessage("Invoking DoSyncWork");
                  await window.HybridWebView.InvokeDotNet('DoSyncWork');
                  LogMessage("Invoked DoSyncWork");
              }
      
              async function InvokeDoSyncWorkParams() {
                  LogMessage("Invoking DoSyncWorkParams");
                  await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']);
                  LogMessage("Invoked DoSyncWorkParams");
              }
      
              async function InvokeDoSyncWorkReturn() {
                  LogMessage("Invoking DoSyncWorkReturn");
                  const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn');
                  LogMessage("Invoked DoSyncWorkReturn, return value: " + retValue);
              }
      
              async function InvokeDoSyncWorkParamsReturn() {
                  LogMessage("Invoking DoSyncWorkParamsReturn");
                  const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']);
                  LogMessage("Invoked DoSyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
              }
      
              async function InvokeDoAsyncWork() {
                  LogMessage("Invoking DoAsyncWork");
                  await window.HybridWebView.InvokeDotNet('DoAsyncWork');
                  LogMessage("Invoked DoAsyncWork");
              }
      
              async function InvokeDoAsyncWorkParams() {
                  LogMessage("Invoking DoAsyncWorkParams");
                  await window.HybridWebView.InvokeDotNet('DoAsyncWorkParams', [123, 'hello']);
                  LogMessage("Invoked DoAsyncWorkParams");
              }
      
              async function InvokeDoAsyncWorkReturn() {
                  LogMessage("Invoking DoAsyncWorkReturn");
                  const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkReturn');
                  LogMessage("Invoked DoAsyncWorkReturn, return value: " + retValue);
              }
      
              async function InvokeDoAsyncWorkParamsReturn() {
                  LogMessage("Invoking DoAsyncWorkParamsReturn");
                  const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkParamsReturn', [123, 'hello']);
                  LogMessage("Invoked DoAsyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
              }                
      
          </script>
      </head>
      <body>
          <div>
              Hybrid sample!
          </div>
          <div>
              <button onclick="window.HybridWebView.SendRawMessage('Message from JS! ' + (count++))">Send message to C#</button>
          </div>
          <div>
              <button onclick="InvokeDoSyncWork()">Call C# sync method (no params)</button>
              <button onclick="InvokeDoSyncWorkParams()">Call C# sync method (params)</button>
              <button onclick="InvokeDoSyncWorkReturn()">Call C# method (no params) and get simple return value</button>
              <button onclick="InvokeDoSyncWorkParamsReturn()">Call C# method (params) and get complex return value</button>
          </div>
          <div>
              <button onclick="InvokeDoAsyncWork()">Call C# async method (no params)</button>
              <button onclick="InvokeDoAsyncWorkParams()">Call C# async method (params)</button>
              <button onclick="InvokeDoAsyncWorkReturn()">Call C# async method (no params) and get simple return value</button>
              <button onclick="InvokeDoAsyncWorkParamsReturn()">Call C# async method (params) and get complex return value</button>
          </div>            
          <div>
              Log: <textarea readonly id="messageLog" style="width: 80%; height: 10em;"></textarea>
          </div>
          <div>
              Consider checking out this PDF: <a href="docs/sample.pdf">sample.pdf</a>
          </div>
      </body>
      </html>
      
    • Recursos\Raw\wwwroot\scripts\HybridWebView.js com a biblioteca JavaScript padrão HybridWebView:

      window.HybridWebView = {
          "Init": function Init() {
              function DispatchHybridWebViewMessage(message) {
                  const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } });
                  window.dispatchEvent(event);
              }
      
              if (window.chrome && window.chrome.webview) {
                  // Windows WebView2
                  window.chrome.webview.addEventListener('message', arg => {
                      DispatchHybridWebViewMessage(arg.data);
                  });
              }
              else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
                  // iOS and MacCatalyst WKWebView
                  window.external = {
                      "receiveMessage": message => {
                          DispatchHybridWebViewMessage(message);
                      }
                  };
              }
              else {
                  // Android WebView
                  window.addEventListener('message', arg => {
                      DispatchHybridWebViewMessage(arg.data);
                  });
              }
          },
      
          "SendRawMessage": function SendRawMessage(message) {
              window.HybridWebView.__SendMessageInternal('__RawMessage', message);
          },
      
          "InvokeDotNet": async function InvokeDotNetAsync(methodName, paramValues) {
              const body = {
                  MethodName: methodName
              };
      
              if (typeof paramValues !== 'undefined') {
                  if (!Array.isArray(paramValues)) {
                      paramValues = [paramValues];
                  }
      
                  for (var i = 0; i < paramValues.length; i++) {
                      paramValues[i] = JSON.stringify(paramValues[i]);
                  }
      
                  if (paramValues.length > 0) {
                      body.ParamValues = paramValues;
                  }
              }
      
              const message = JSON.stringify(body);
      
              var requestUrl = `${window.location.origin}/__hwvInvokeDotNet?data=${encodeURIComponent(message)}`;
      
              const rawResponse = await fetch(requestUrl, {
                  method: 'GET',
                  headers: {
                      'Accept': 'application/json'
                  }
              });
              const response = await rawResponse.json();
      
              if (response) {
                  if (response.IsJson) {
                      return JSON.parse(response.Result);
                  }
      
                  return response.Result;
              }
      
              return null;
          },
      
          "__SendMessageInternal": function __SendMessageInternal(type, message) {
      
              const messageToSend = type + '|' + message;
      
              if (window.chrome && window.chrome.webview) {
                  // Windows WebView2
                  window.chrome.webview.postMessage(messageToSend);
              }
              else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
                  // iOS and MacCatalyst WKWebView
                  window.webkit.messageHandlers.webwindowinterop.postMessage(messageToSend);
              }
              else {
                  // Android WebView
                  hybridWebViewHost.sendMessage(messageToSend);
              }
          },
      
          "__InvokeJavaScript": async function __InvokeJavaScript(taskId, methodName, args) {
              try {
                  var result = null;
                  if (methodName[Symbol.toStringTag] === 'AsyncFunction') {
                      result = await methodName(...args);
                  } else {
                      result = methodName(...args);
                  }
                  window.HybridWebView.__TriggerAsyncCallback(taskId, result);
              } catch (ex) {
                  console.error(ex);
                  window.HybridWebView.__TriggerAsyncFailedCallback(taskId, ex);
              }
          },
      
          "__TriggerAsyncCallback": function __TriggerAsyncCallback(taskId, result) {
              const json = JSON.stringify(result);
              window.HybridWebView.__SendMessageInternal('__InvokeJavaScriptCompleted', taskId + '|' + json);
          },
      
          "__TriggerAsyncFailedCallback": function __TriggerAsyncCallback(taskId, error) {
      
              if (!error) {
                  json = {
                      Message: "Unknown error",
                      StackTrace: Error().stack
                  };
              } else if (error instanceof Error) {
                  json = {
                      Name: error.name,
                      Message: error.message,
                      StackTrace: error.stack
                  };
              } else if (typeof (error) === 'string') {
                  json = {
                      Message: error,
                      StackTrace: Error().stack
                  };
              } else {
                  json = {
                      Message: JSON.stringify(error),
                      StackTrace: Error().stack
                  };
              }
      
              json = JSON.stringify(json);
              window.HybridWebView.__SendMessageInternal('__InvokeJavaScriptFailed', taskId + '|' + json);
          }
      }
      
      window.HybridWebView.Init();
      

    Em seguida, adicione qualquer conteúdo da Web adicional ao seu projeto.

    Advertência

    Em alguns casos, o Visual Studio pode adicionar entradas ao arquivo de .csproj do projeto que estão incorretas. Ao usar o local padrão para ativos brutos, não deve haver entradas para esses arquivos ou pastas no arquivo .csproj.

  3. Adicione o controle HybridWebView ao seu aplicativo:

    <Grid RowDefinitions="Auto,*"
          ColumnDefinitions="*">
        <Button Text="Send message to JavaScript"
                Clicked="OnSendMessageButtonClicked" />
        <HybridWebView x:Name="hybridWebView"
                       RawMessageReceived="OnHybridWebViewRawMessageReceived"
                       Grid.Row="1" />
    </Grid>
    
  4. Modifique o método CreateMauiApp de sua classe MauiProgram para habilitar ferramentas de desenvolvedor nos controles WebView subjacentes quando seu aplicativo estiver sendo executado na configuração de depuração. Para fazer isso, chame o método AddHybridWebViewDeveloperTools no objeto IServiceCollection:

    using Microsoft.Extensions.Logging;
    
    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");
                });
    
    #if DEBUG
            builder.Services.AddHybridWebViewDeveloperTools();
            builder.Logging.AddDebug();            
    #endif
            // Register any app services on the IServiceCollection object
    
            return builder.Build();
        }
    }
    
  5. Use as APIs HybridWebView para enviar mensagens entre o código JavaScript e o código C#:

    private void OnSendMessageButtonClicked(object sender, EventArgs e)
    {
        hybridWebView.SendRawMessage($"Hello from C#!");
    }
    
    private async void OnHybridWebViewRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e)
    {
        await DisplayAlert("Raw Message Received", e.Message, "OK");
    }
    

    As mensagens acima são classificadas como brutas porque nenhum processamento adicional é executado. Você também pode codificar dados dentro da mensagem para executar mensagens mais avançadas.

Invoque JavaScript a partir de C#

O código C# do seu aplicativo pode invocar métodos JavaScript de forma síncrona e assíncrona dentro do HybridWebView, com parâmetros opcionais e um valor de retorno opcional. Isto pode ser conseguido com os métodos InvokeJavaScriptAsync e EvaluateJavaScriptAsync:

  • O método EvaluateJavaScriptAsync executa o código JavaScript fornecido por meio de um parâmetro e retorna o resultado como uma cadeia de caracteres.
  • O método InvokeJavaScriptAsync invoca um método JavaScript especificado, opcionalmente passando valores de parâmetro, e especifica um argumento genérico que indica o tipo do valor de retorno. Ele retorna um objeto do tipo de argumento genérico que contém o valor de retorno do método JavaScript chamado. Internamente, os parâmetros e valores de retorno são codificados em JSON.

Observação

O .NET 10 inclui uma sobrecarga de InvokeJavaScriptAsync que invoca um método JavaScript especificado sem especificar nenhuma informação sobre o tipo de retorno. Para obter mais informações, consulte Invocar métodos JavaScript que não retornam um valor.

Invocar JavaScript síncrono

Os métodos JavaScript síncronos podem ser invocados com os métodos EvaluateJavaScriptAsync e InvokeJavaScriptAsync. No exemplo a seguir, o método InvokeJavaScriptAsync é usado para demonstrar a invocação de JavaScript incorporado no conteúdo da Web de um aplicativo. Por exemplo, um método Javascript simples para adicionar dois números pode ser definido no seu conteúdo da web:

function AddNumbers(a, b) {
    return a + b;
}

O método JavaScript AddNumbers pode ser invocado a partir do C# com o método InvokeJavaScriptAsync:

double x = 123d;
double y = 321d;

double result = await hybridWebView.InvokeJavaScriptAsync<double>(
    "AddNumbers", // JavaScript method name
    HybridSampleJSContext.Default.Double, // JSON serialization info for return type
    [x, y], // Parameter values
    [HybridSampleJSContext.Default.Double, HybridSampleJSContext.Default.Double]); // JSON serialization info for each parameter

A invocação do método requer a especificação de JsonTypeInfo objetos que incluem informações de serialização para os tipos utilizados na operação. Esses objetos são criados automaticamente incluindo a seguinte classe partial em seu projeto:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(double))]
internal partial class HybridSampleJsContext : JsonSerializerContext
{
    // This type's attributes specify JSON serialization info to preserve type structure
    // for trimmed builds.
}

Importante

A classe HybridSampleJsContext deve ser partial para que a geração de código possa fornecer a implementação quando o projeto é compilado. Se o tipo estiver aninhado em outro tipo, aquele tipo também deverá ser partial.

Invoque JavaScript assíncrono

Os métodos JavaScript assíncronos podem ser invocados com os métodos EvaluateJavaScriptAsync e InvokeJavaScriptAsync. No exemplo a seguir, o método InvokeJavaScriptAsync é usado para demonstrar a invocação de JavaScript incorporado no conteúdo da Web de um aplicativo. Por exemplo, um método Javascript que recupera dados de forma assíncrona pode ser definido em seu conteúdo da Web:

async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) {
    const response = await fetch("/asyncdata.txt");
    if (!response.ok) {
            throw new Error(`HTTP error: ${response.status}`);
    }
    var jsonData = await response.json();
    jsonData[s1] = s2;

    return jsonData;
}

O método JavaScript EvaluateMeWithParamsAndAsyncReturn pode ser invocado a partir do C# com o método InvokeJavaScriptAsync:

Dictionary<string, string> asyncResult = await hybridWebView.InvokeJavaScriptAsync<Dictionary<string, string>>(
    "EvaluateMeWithParamsAndAsyncReturn", // JavaScript method name
    HybridSampleJSContext.Default.DictionaryStringString, // JSON serialization info for return type
    ["new_key", "new_value"], // Parameter values
    [HybridSampleJSContext.Default.String, HybridSampleJSContext.Default.String]); // JSON serialization info for each parameter

Neste exemplo, asyncResult é um Dictionary<string, string> que contém os dados JSON da solicitação da Web.

A invocação do método requer a especificação de JsonTypeInfo objetos que incluem informações de serialização para os tipos usados na operação. Esses objetos são criados automaticamente incluindo a seguinte classe partial em seu projeto:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(string))]
internal partial class HybridSampleJSContext : JsonSerializerContext
{
    // This type's attributes specify JSON serialization info to preserve type structure
    // for trimmed builds.  
}

Importante

A classe HybridSampleJsContext deve ser partial para que a geração de código possa fornecer a implementação quando o projeto é compilado. Se o tipo estiver aninhado dentro de outro tipo, então esse deve também ser partial.

Invoque métodos JavaScript que não retornam um valor

O método InvokeJavaScriptAsync também pode ser usado para invocar métodos JavaScript que não retornam um valor. Existem abordagens alternativas para o fazer:

  • Invoque o InvokeJavaScriptAsync, especificando o nome do método JavaScript e quaisquer parâmetros opcionais:

    await hybridWebView.InvokeJavaScriptAsync("javaScriptWithVoidReturn"); // JavaScript method name
    

    Neste exemplo, apenas o nome do método JavaScript é especificado.

  • Invoque o método InvokeJavaScriptAsync sem especificar o argumento genérico:

    await hybridWebView.InvokeJavaScriptAsync(
         "javaScriptWithParamsAndVoidReturn",  // JavaScript method name
         HybridSampleJSContext.Default.Double, // JSON serialization info for return type
         [x, y], // Parameter values
         [HybridSampleJSContext.Default.Double, HybridSampleJSContext.Default.Double]); // JSON serialization info for each parameter
    

    Neste exemplo, embora o argumento genérico não seja necessário, ainda é necessário fornecer informações de serialização JSON para o tipo de retorno, mesmo que ele não seja usado.

  • Invoque o método InvokeJavaScriptAsync ao especificar o argumento genérico:

    await hybridWebView.InvokeJavaScriptAsync<double>(
        "javaScriptWithParamsAndVoidReturn", // JavaScript method name
        null, // JSON serialization info for return type
        [x, y], // Parameter values
        [HybridSampleJSContext.Default.Double, HybridSampleJSContext.Default.Double]); // JSON serialization info for each parameter
    

    Neste exemplo, o argumento genérico é necessário e null pode ser passado como o valor das informações de serialização JSON para o tipo de retorno.

Enviar exceções JavaScript para .NET

Por padrão, a invocação de métodos JavaScript em um HybridWebView pode ocultar exceções geradas pelo seu código JavaScript. Para aceitar que as exceções JavaScript sejam enviadas para o .NET, onde são relançadas como exceções do .NET, adicione o seguinte código à sua classe MauiProgram:

static MauiProgram()
{
    AppContext.SetSwitch("HybridWebView.InvokeJavaScriptThrowsExceptions", true);
}

Isso permite cenários em que, se o código C# chamar código JavaScript e o código JavaScript falhar, a falha JavaScript será enviada para o .NET, onde será relançada como uma exceção .NET que pode ser capturada e tratada.

Invoque C# a partir de JavaScript

O código JavaScript do seu aplicativo dentro do HybridWebView pode invocar métodos C# de forma síncrona e assíncrona, com parâmetros opcionais e um valor de retorno opcional. Este objetivo pode ser atingido através das seguintes medidas:

  • Definição de métodos C# públicos que serão invocados a partir do JavaScript.
  • Chamando o método SetInvokeJavaScriptTarget para definir o objeto que será o destino das chamadas JavaScript do HybridWebView.
  • Chamando os métodos C# a partir do JavaScript.

O exemplo a seguir define métodos públicos síncronos e assíncronos para invocar a partir de JavaScript:

public partial class MainPage : ContentPage
{
    ...  

    public void DoSyncWork()
    {
        Debug.WriteLine("DoSyncWork");
    }

    public void DoSyncWorkParams(int i, string s)
    {
        Debug.WriteLine($"DoSyncWorkParams: {i}, {s}");
    }

    public string DoSyncWorkReturn()
    {
        Debug.WriteLine("DoSyncWorkReturn");
        return "Hello from C#!";
    }

    public SyncReturn DoSyncWorkParamsReturn(int i, string s)
    {
        Debug.WriteLine($"DoSyncWorkParamReturn: {i}, {s}");
        return new SyncReturn
        {
            Message = "Hello from C#!" + s,
            Value = i
        };
    }

    public async Task DoAsyncWork()
    {
        Debug.WriteLine("DoAsyncWork");
        await Task.Delay(1000);
    }

    public async Task DoAsyncWorkParams(int i, string s)
    {
        Debug.WriteLine($"DoAsyncWorkParams: {i}, {s}");
        await Task.Delay(1000);
    }

    public async Task<String> DoAsyncWorkReturn()
    {
        Debug.WriteLine("DoAsyncWorkReturn");
        await Task.Delay(1000);
        return "Hello from C#!";
    }

    public async Task<SyncReturn> DoAsyncWorkParamsReturn(int i, string s)
    {
        Debug.WriteLine($"DoAsyncWorkParamsReturn: {i}, {s}");
        await Task.Delay(1000);
        return new SyncReturn
        {
            Message = "Hello from C#!" + s,
            Value = i
        };
    }    

    public class SyncReturn
    {
        public string? Message { get; set; }
        public int Value { get; set; }
    }  
}

Em seguida, você deve chamar o método SetInvokeJavaScriptTarget para definir o objeto que será o destino das chamadas JavaScript do HybridWebView:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        hybridWebView.SetInvokeJavaScriptTarget(this);
    }
    ...
}

Os métodos públicos no objeto definido por meio do método SetInvokeJavaScriptTarget podem ser invocados do JavaScript com a função window.HybridWebView.InvokeDotNet:

// Synchronous methods
await window.HybridWebView.InvokeDotNet('DoSyncWork');
await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']);
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn');
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']);

// Asynchronous methods
await window.HybridWebView.InvokeDotNet('DoAsyncWork');
await window.HybridWebView.InvokeDotNet('DoAsyncWorkParams', [123, 'hello']);
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkReturn');
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkParamsReturn', [123, 'hello']);

A função JavaScript window.HybridWebView.InvokeDotNet invoca um método C# especificado, com parâmetros opcionais e um valor de retorno opcional.

Observação

Invocar a função JavaScript window.HybridWebView.InvokeDotNet requer que seu aplicativo inclua a biblioteca JavaScript HybridWebView.js listada anteriormente neste artigo.