Tutorial: Envío de notificaciones push a aplicaciones de Flutter mediante Azure Notification Hubs a través de un servicio back-end

Descargar ejemplo Descarga del ejemplo

En este tutorial, usará Azure Notification Hubs para insertar notificaciones en una aplicación flutter destinada a Android e iOS.

Un back-end de api web de ASP.NET Core se usa para controlar el registro de dispositivos para el cliente mediante el enfoque de instalación más reciente y óptimo. El servicio también enviará notificaciones push de forma multiplataforma.

Estas operaciones se controlan mediante el SDK de Notification Hubs para las operaciones de back-end. Se proporciona más información sobre el enfoque general en la documentación registro desde el back-end de la aplicación .

En este tutorial se describen los pasos siguientes:

Prerrequisitos

Para continuar, necesita lo siguiente:

Para Android, debe tener:

  • Un desarrollador desbloqueó un dispositivo físico o un emulador (que ejecuta la API 26 y versiones posteriores con Google Play Services instalado).

Para iOS, debe tener:

Nota:

El simulador de iOS no admite notificaciones remotas, por lo que se requiere un dispositivo físico al explorar este ejemplo en iOS. Sin embargo, no es necesario ejecutar la aplicación en Android e iOS para completar este tutorial.

Puede seguir los pasos descritos en este ejemplo de primeros principios sin experiencia previa. Sin embargo, se beneficiará de tener conocimientos sobre los siguientes aspectos.

Los pasos proporcionados son específicos de macOS. Es posible seguir los pasos de Windows omitiendo los aspectos de iOS .

Configuración de Servicios de notificaciones push y Centro de notificaciones de Azure

En esta sección, configurará Firebase Cloud Messaging (FCM) y Apple Push Notification Services (APNS). A continuación, creará y configurará un centro de notificaciones para trabajar con esos servicios.

Creación de un proyecto de Firebase y habilitación de Firebase Cloud Messaging para Android

  1. Inicie sesión en la consola de Firebase. Cree un nuevo proyecto de Firebase que escriba PushDemo como nombre del proyecto.

    Nota:

    Se generará un nombre único automáticamente. De forma predeterminada, se compone de una variante minúscula del nombre que proporcionó más un número generado separado por un guión. Puede cambiar esto si desea que sea aún único globalmente.

  2. Después de crear el proyecto, seleccione Agregar Firebase a la aplicación Android.

    Incorporación de Firebase a la aplicación Android

  3. En la página Agregar Firebase a la aplicación Android , siga estos pasos.

    1. Para el nombre del paquete Android, escriba un nombre para el paquete. Por ejemplo: com.<organization_identifier>.<package_name>.

      Especificar el nombre del paquete

    2. Seleccione Registrar aplicación.

    3. Seleccione Descargar google-services.json. A continuación, guarde el archivo en una carpeta local para usarlo más adelante y seleccione Siguiente.

      Descargar google-services.json

    4. Seleccione Siguiente.

    5. Seleccione Continue to console (Continuar a la consola).

      Nota:

      Si el botón Continuar a la consola no está habilitado, debido a la comprobación de instalación de comprobación , elija Omitir este paso.

  4. En la consola de Firebase, seleccione el engranaje del proyecto. A continuación, seleccione Configuración del proyecto.

    Seleccione Configuración del proyecto.

    Nota:

    Si no ha descargado el archivo google-services.json , puede descargarlo en esta página.

  5. Cambie a la pestaña Mensajería en la nube de la parte superior. Copie y guarde la clave de servidor para su uso posterior. Use este valor para configurar el centro de notificaciones.

    Copia de la clave del servidor

Registro de la aplicación de iOS para notificaciones push

Para enviar notificaciones push a una aplicación de iOS, registre la aplicación con Apple y también regístrese para las notificaciones push.

  1. Si aún no ha registrado la aplicación, vaya al portal de aprovisionamiento de iOS en el Centro para desarrolladores de Apple. Inicie sesión en el portal con el identificador de Apple, vaya a Certificados, Identificadores & Perfiles y seleccione Identificadores. Haga clic + para registrar una nueva aplicación.

    Página iOS Provisioning Portal App IDs (Identificadores de aplicación del portal de aprovisionamiento de iOS)

  2. En la pantalla Registrar un nuevo identificador , seleccione el botón de radio Id. de aplicación. A continuación, seleccione Continuar.

    Página registrar nuevo identificador en el portal de aprovisionamiento de iOS

  3. Actualice los tres valores siguientes para la nueva aplicación y, a continuación, seleccione Continuar:

    • Descripción: escriba un nombre descriptivo para la aplicación.

    • Id. de lote: escriba un identificador de lote del formulario com.organization_identifier<>.<>product_name como se mencionó en la Guía de distribución de aplicaciones. En la captura de pantalla siguiente, el mobcat valor se usa como identificador de organización y el valor PushDemo se usa como nombre del producto.

      Página del id. de aplicación de registro del portal de aprovisionamiento de iOS

    • Notificaciones push: active la opción Notificaciones push en la sección Funcionalidades .

      Formulario para registrar un nuevo identificador de aplicación

      Esta acción genera el identificador de la aplicación y solicita que confirme la información. Seleccione Continuar y, a continuación, seleccione Registrar para confirmar el nuevo identificador de aplicación.

      Confirmación del nuevo identificador de aplicación

      Después de seleccionar Registrar, verá el nuevo identificador de aplicación como un elemento de línea en la página Certificados, Identificadores & perfiles .

  4. En la página Certificados, Identificadores & perfiles , en Identificadores, busque el elemento de línea Id. de aplicación que creó. A continuación, seleccione su fila para mostrar la pantalla Editar configuración del identificador de aplicación .

Creación de un certificado para Notification Hubs

Se requiere un certificado para permitir que el centro de notificaciones funcione con Apple Push Notification Services (APNS) y se puede proporcionar de una de estas dos maneras:

  1. Creación de un certificado push p12 que se puede cargar directamente en el Centro de notificaciones (el enfoque original)

  2. Creación de un certificado p8 que se puede usar para la autenticación basada en tokens (el enfoque más reciente y recomendado)

El enfoque más reciente tiene una serie de ventajas, tal como se documenta en autenticación basada en tokens (HTTP/2) para APNS. Se requieren menos pasos, pero también se exige para escenarios específicos. Sin embargo, se han proporcionado pasos para ambos enfoques, ya que cualquiera funcionará con los fines de este tutorial.

OPCIÓN 1: Creación de un certificado push p12 que se puede cargar directamente en el Centro de notificaciones
  1. En el equipo Mac, ejecute la herramienta Acceso a llaves. Se puede abrir desde la carpeta Utilidades o la carpeta Other del Launchpad.

  2. Seleccione Acceso a cadena de claves, expanda Asistente para certificados y, a continuación, seleccione Solicitar un certificado desde una entidad de certificación.

    Uso del acceso a llaves para solicitar un nuevo certificado

    Nota:

    De forma predeterminada, Keychain Access selecciona el primer elemento de la lista. Esto puede ser un problema si está en la categoría Certificados y apple Worldwide Developer Relations Certification Authority no es el primer elemento de la lista. Asegúrese de que tiene un elemento que no es clave o de que está seleccionada la clave de entidad de certificación de relaciones para desarrolladores mundiales de Apple antes de generar la CSR (solicitud de firma de certificado).

  3. Seleccione el usuario Email Dirección, escriba el valor de Nombre común, asegúrese de especificar Guardado en disco y, a continuación, seleccione Continuar. Deje ca Email Dirección en blanco, ya que no es necesario.

    Información esperada del certificado

  4. Escriba un nombre para el archivo solicitud de firma de certificado (CSR) en Guardar como, seleccione la ubicación en Where (Dónde) y, a continuación, seleccione Guardar.

    Elegir un nombre de archivo para el certificado

    Esta acción guarda el archivo CSR en la ubicación seleccionada. La ubicación predeterminada es Escritorio. Recuerde la ubicación elegida para el archivo.

  5. De nuevo en la página Certificados, identificadores & perfiles del portal de aprovisionamiento de iOS, desplácese hacia abajo hasta la opción Notificaciones push activadas y, a continuación, seleccione Configurar para crear el certificado.

    Página Editar id. de aplicación

  6. Aparece la ventana Certificados TLS/SSL del servicio de notificaciones push de Apple . Seleccione el botón Create Certificate (Crear certificado ) en la sección Development TLS/SSL Certificate (Certificado TLS/SSL de desarrollo ).

    Botón Crear certificado para el identificador de aplicación

    Se muestra la pantalla Crear un nuevo certificado .

    Nota:

    En este tutorial se usa un certificado de desarrollo. El mismo proceso se usa al registrar un certificado de producción. Asegúrese de usar el mismo tipo de certificado al enviar notificaciones.

  7. Seleccione Elegir archivo, vaya a la ubicación donde guardó el archivo CSR y, a continuación, haga doble clic en el nombre del certificado para cargarlo. A continuación, seleccione Continuar.

  8. Una vez que el portal cree el certificado, seleccione el botón Descargar . Guarde el certificado y recuerde la ubicación en la que se guarda.

    Página de descarga de certificados generada

    El certificado se descarga y guarda en el equipo en la carpeta Descargas .

    Busque el archivo de certificado en la carpeta Descargas.

    Nota:

    De forma predeterminada, el certificado de desarrollo descargado se denomina aps_development.cer.

  9. Haga doble clic en el certificado push descargado aps_development.cer. Esta acción instala el nuevo certificado en la cadena de claves, como se muestra en la siguiente imagen:

    Lista de certificados de acceso de cadena de claves que muestra el nuevo certificado

    Nota:

    Aunque el nombre del certificado puede ser diferente, el nombre tendrá el prefijo Apple Development iOS Push Services y tendrá el identificador de lote adecuado asociado.

  10. En Acceso a llaves, control + Haga clic en el nuevo certificado de inserción que creó en la categoría Certificados . Seleccione Exportar, asigne un nombre al archivo, seleccione el formato p12 y, a continuación, seleccione Guardar.

    Exportación del certificado como formato p12

    Puede optar por proteger el certificado con una contraseña, pero una contraseña es opcional. Haga clic en Aceptar si desea omitir la creación de contraseñas. Anote el nombre de archivo y la ubicación del certificado p12 exportado. Se usan para habilitar la autenticación con APN.

    Nota:

    El nombre y la ubicación del archivo p12 pueden ser diferentes de los que se muestran en este tutorial.

OPCIÓN 2: Creación de un certificado p8 que se puede usar para la autenticación basada en tokens
  1. Anote los detalles siguientes:

    • Prefijo de id. de aplicación (id. de equipo)
    • Id. de lote
  2. De nuevo en Certificados, Identificadores & Perfiles, haga clic en Claves.

    Nota:

    Si ya tiene una clave configurada para APNS, puede volver a usar el certificado p8 que descargó justo después de crearlo. Si es así, puede omitir los pasos del 3 al 5.

  3. Haga clic en el + botón (o en el botón Crear una tecla ) para crear una nueva clave.

  4. Proporcione un valor de Nombre de clave adecuado, active la opción Apple Push Notifications service (APNS) y, a continuación, haga clic en Continuar, seguido de Registrar en la pantalla siguiente.

  5. Haga clic en Descargar y, a continuación, mueva el archivo p8 (con el prefijo AuthKey_) a un directorio local seguro y, a continuación, haga clic en Listo.

    Nota:

    Asegúrese de mantener el archivo p8 en un lugar seguro (y guarde una copia de seguridad). Después de descargar la clave, no se puede volver a descargar a medida que se quita la copia del servidor.

  6. En Claves, haga clic en la clave que ha creado (o una clave existente si ha elegido usarla en su lugar).

  7. Anote el valor de Id. de clave .

  8. Abra el certificado p8 en una aplicación adecuada de su elección, como Visual Studio Code. Anote el valor de clave (entre -----BEGIN PRIVATE KEY----- y -----END PRIVATE KEY-----).

    -----BEGIN PRIVATE KEY-----
    <key_value>
    -----END PRIVATE KEY-----

    Nota:

    Este es el valor del token que se usará más adelante para configurar el Centro de notificaciones.

Al final de estos pasos, debe tener la siguiente información para usarla más adelante en Configuración del centro de notificaciones con información de APNS:

  • Id. de equipo (consulte el paso 1)
  • Id. de agrupación (consulte el paso 1)
  • Id. de clave (consulte el paso 7)
  • Valor del token (valor de clave p8 obtenido en el paso 8)

Creación de un perfil de aprovisionamiento para la aplicación

  1. Vuelva al portal de aprovisionamiento de iOS, seleccione Certificados, Identificadores & Perfiles, seleccione Perfiles en el menú de la izquierda y, a continuación, seleccione + para crear un nuevo perfil. Aparece la pantalla Registrar un nuevo perfil de aprovisionamiento .

  2. Seleccione Desarrollo de aplicaciones de iOS en Desarrollo como tipo de perfil de aprovisionamiento y, a continuación, seleccione Continuar.

    Lista de perfiles de aprovisionamiento

  3. A continuación, seleccione el identificador de aplicación que creó en la lista desplegable Id . de aplicación y seleccione Continuar.

    Seleccione el identificador de la aplicación.

  4. En la ventana Seleccionar certificados , seleccione el certificado de desarrollo que usa para la firma de código y seleccione Continuar.

    Nota:

    Este certificado no es el certificado de inserción que creó en el paso anterior. Este es el certificado de desarrollo. Si no existe, debe crearlo, ya que se trata de un requisito previo para este tutorial. Los certificados de desarrollador se pueden crear en el Portal para desarrolladores de Apple, a través de Xcode o en Visual Studio.

  5. Vuelva a la página Certificados, Identificadores & Perfiles , seleccione Perfiles en el menú de la izquierda y, a continuación, seleccione + para crear un nuevo perfil. Aparece la pantalla Registrar un nuevo perfil de aprovisionamiento .

  6. En la ventana Seleccionar certificados , seleccione el certificado de desarrollo que creó. A continuación, seleccione Continuar.

  7. A continuación, seleccione los dispositivos que se van a usar para las pruebas y seleccione Continuar.

  8. Por último, elija un nombre para el perfil en Nombre del perfil de aprovisionamiento y seleccione Generar.

    Elección de un nombre de perfil de aprovisionamiento

  9. Cuando se cree el nuevo perfil de aprovisionamiento, seleccione Descargar. Recuerde la ubicación a la que se guarda.

  10. Vaya a la ubicación del perfil de aprovisionamiento y, a continuación, haga doble clic en él para instalarlo en el equipo de desarrollo.

Creación de un centro de notificaciones

En esta sección, creará un centro de notificaciones y configurará la autenticación con APNS. Puede usar un certificado push p12 o una autenticación basada en tokens. Si desea usar un centro de notificaciones que ya ha creado, puede ir directamente al paso 5.

  1. Inicie sesión en Azure.

  2. Haga clic en Crear un recurso, busque y elija Centro de notificaciones y, a continuación, haga clic en Crear.

  3. Actualice los campos siguientes y, a continuación, haga clic en Crear:

    DETALLES BÁSICOS

    Suscripción: Elija la suscripción de destino en la lista desplegable.
    Grupo de recursos: Crear un nuevo grupo de recursos (o elegir uno existente)

    DETALLES DEL ESPACIO DE NOMBRES

    Espacio de nombres del centro de notificaciones: Escriba un nombre único global para el espacio de nombres del Centro de notificaciones .

    Nota:

    Asegúrese de que la opción Crear nuevo está seleccionada para este campo.

    DETALLES DEL CENTRO DE NOTIFICACIONES

    Centro de notificaciones: Escriba un nombre para el Centro de notificaciones.
    Ubicación: Elegir una ubicación adecuada en la lista desplegable
    Plan de tarifa: Mantener la opción Gratis predeterminada

    Nota:

    A menos que haya alcanzado el número máximo de concentradores en el nivel gratis.

  4. Una vez que se ha aprovisionado el Centro de notificaciones, vaya a ese recurso.

  5. Vaya al nuevo Centro de notificaciones.

  6. Seleccione Directivas de acceso en la lista (en ADMINISTRAR).

  7. Anote los valores de Nombre de directiva junto con sus valores de cadena de conexión correspondientes.

Configuración del Centro de notificaciones con información de APNS

En Notification Services, seleccione Apple y siga los pasos adecuados según el enfoque que eligió anteriormente en la sección Creación de un certificado para Notification Hubs .

Nota:

Usa el modo de producción para aplicaciones solo si quieres enviar notificaciones push a los usuarios que compraron la aplicación desde la tienda.

OPCIÓN 1: Uso de un certificado de inserción .p12

  1. Seleccione Certificado.

  2. Seleccione el icono de archivo.

  3. Seleccione el archivo .p12 que exportó anteriormente y, a continuación, seleccione Abrir.

  4. Si es necesario, especifique la contraseña correcta.

  5. Seleccione Modo de espacio aislado .

  6. Seleccione Guardar.

OPCIÓN 2: Uso de la autenticación basada en tokens

  1. Seleccione Token.

  2. Escriba los valores siguientes que adquirió anteriormente:

    • Id. de clave
    • Identificador de lote
    • Id. de equipo
    • Token
  3. Elija Espacio aislado.

  4. Seleccione Guardar.

Configuración del centro de notificaciones con información de FCM

  1. Seleccione Google (GCM/FCM) en la sección Configuración del menú izquierdo.
  2. Escriba la clave de servidor que anotó en google Firebase Console.
  3. Seleccione Guardar en la barra de herramientas.

Creación de una aplicación back-end de api web de ASP.NET Core

En esta sección, creará el back-end de api web ASP.NET Core para controlar el registro de dispositivos y el envío de notificaciones a la aplicación móvil flutter.

Creación de un proyecto web

  1. En Visual Studio, seleccione Archivo>nueva solución.

  2. SeleccioneAplicación>.NET Core>ASP.NET Core>API>Siguiente.

  3. En el cuadro de diálogo Configurar la nueva API web de ASP.NET Core, seleccione Plataforma de destino de .NET Core 3.1.

  4. Escriba PushDemoApi como Nombre del proyecto y, a continuación, seleccione Crear.

  5. Inicie la depuración (Comando + Entrar) para probar la aplicación con plantilla.

    Nota:

    La aplicación con plantilla está configurada para usar WeatherForecastController como launchUrl. Esto se establece en Propiedades>launchSettings.json.

    Si se le pide un mensaje de confirmación de desarrollo no válido :

    1. Haga clic en para aceptar ejecutar la herramienta "dotnet dev-certs https" para corregirlo. La herramienta "dotnet dev-certs https" le pedirá que escriba una contraseña para el certificado y la contraseña de la cadena de claves.

    2. Haga clic en cuando se le pida que instale y confíe en el nuevo certificado y, a continuación, escriba la contraseña de la cadena de claves.

  6. Expanda la carpeta Controllers y, a continuación, elimine WeatherForecastController.cs.

  7. Elimine WeatherForecast.cs.

  8. Configure valores de configuración local mediante la herramienta Administrador de secretos. La desacoplación de los secretos de la solución garantiza que no terminan en el control de código fuente. Abra Terminal y, a continuación, vaya al directorio del archivo del proyecto y ejecute los siguientes comandos:

    dotnet user-secrets init
    dotnet user-secrets set "NotificationHub:Name" <value>
    dotnet user-secrets set "NotificationHub:ConnectionString" <value>
    

    Reemplace los valores de marcador de posición por su propio nombre del centro de notificaciones y cadena de conexión valores. Ha tomado nota de ellos en la sección Creación de un centro de notificaciones . De lo contrario, puede buscarlos en Azure.

    NotificationHub:Name:
    Consulte Nombre en el resumen de Essentials en la parte superior de Información general.

    NotificationHub:ConnectionString:
    Consulte DefaultFullSharedAccessSignature en Directivas de acceso.

    Nota:

    En escenarios de producción, puede ver opciones como Azure KeyVault para almacenar de forma segura el cadena de conexión. Por motivos de simplicidad, los secretos se agregarán a la configuración de la aplicación Azure App Service.

Autenticación de clientes mediante una clave de API (opcional)

Las claves de API no son tan seguras como tokens, pero bastarán con los fines de este tutorial. Una clave de API se puede configurar fácilmente a través del middleware de ASP.NET.

  1. Agregue la clave de API a los valores de configuración local.

    dotnet user-secrets set "Authentication:ApiKey" <value>
    

    Nota:

    Debe reemplazar el valor del marcador de posición por el suyo propio y anotarlo.

  2. Control + Haga clic en el proyecto PushDemoApi , elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar con autenticación como nombre de carpeta.

  3. Control + Haga clic en la carpeta Autenticación y, a continuación, elija Nuevo archivo... en el menú Agregar .

  4. SeleccioneClase vacíageneral>, escriba ApiKeyAuthOptions.cs en Nombre y, a continuación, haga clic en Nuevo agregando la siguiente implementación.

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthOptions : AuthenticationSchemeOptions
        {
            public const string DefaultScheme = "ApiKey";
            public string Scheme => DefaultScheme;
            public string ApiKey { get; set; }
        }
    }
    
  5. Agregue otra clase vacía a la carpeta Authentication denominada ApiKeyAuthHandler.cs y agregue la siguiente implementación.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions>
        {
            const string ApiKeyIdentifier = "apikey";
    
            public ApiKeyAuthHandler(
                IOptionsMonitor<ApiKeyAuthOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock)
                : base(options, logger, encoder, clock) {}
    
            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                string key = string.Empty;
    
                if (Request.Headers[ApiKeyIdentifier].Any())
                {
                    key = Request.Headers[ApiKeyIdentifier].FirstOrDefault();
                }
                else if (Request.Query.ContainsKey(ApiKeyIdentifier))
                {
                    if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey))
                        key = queryKey;
                }
    
                if (string.IsNullOrWhiteSpace(key))
                    return Task.FromResult(AuthenticateResult.Fail("No api key provided"));
    
                if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal))
                    return Task.FromResult(AuthenticateResult.Fail("Invalid api key."));
    
                var identities = new List<ClaimsIdentity> {
                    new ClaimsIdentity("ApiKeyIdentity")
                };
    
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identities), Options.Scheme);
    
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
    }
    

    Nota:

    Un controlador de autenticación es un tipo que implementa el comportamiento de un esquema, en este caso un esquema de clave de API personalizado.

  6. Agregue otra clase vacía a la carpeta Authentication denominada ApiKeyAuthenticationBuilderExtensions.cs y agregue la siguiente implementación.

    using System;
    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public static class AuthenticationBuilderExtensions
        {
            public static AuthenticationBuilder AddApiKeyAuth(
                this AuthenticationBuilder builder,
                Action<ApiKeyAuthOptions> configureOptions)
            {
                return builder
                    .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>(
                        ApiKeyAuthOptions.DefaultScheme,
                        configureOptions);
            }
        }
    }
    

    Nota:

    Este método de extensión simplifica el código de configuración de middleware en Startup.cs lo que hace que sea más legible y generalmente más fácil de seguir.

  7. En Startup.cs, actualice el método ConfigureServices para configurar la autenticación de clave de API debajo de la llamada a los servicios. Método AddControllers .

    using PushDemoApi.Authentication;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
            options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
        }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind);
    }
    
  8. Aún en Startup.cs, actualice el método Configure para llamar a los métodos de extensión UseAuthentication y UseAuthorization en IApplicationBuilder de la aplicación. Asegúrese de que se llama a esos métodos después de UseRouting y antes de la aplicación. UseEndpoints.

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthentication();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    

    Nota:

    Al llamar a UseAuthentication , se registra el middleware que usa los esquemas de autenticación registrados anteriormente (de ConfigureServices). Se debe llamar a este antes de cualquier middleware que dependa de los usuarios que se autentiquen.

Adición de dependencias y configuración de servicios

ASP.NET Core admite el patrón de diseño de software de inserción de dependencias (DI), que es una técnica para lograr la inversión de control (IoC) entre las clases y sus dependencias.

El uso del centro de notificaciones y el SDK de Notification Hubs para las operaciones de back-end se encapsulan dentro de un servicio. El servicio se registra y se pone a disposición mediante una abstracción adecuada.

  1. Control + Haga clic en la carpeta Dependencias y, a continuación, elija Administrar paquetes NuGet....

  2. Busque Microsoft.Azure.NotificationHubs y asegúrese de que está activado.

  3. Haga clic en Agregar paquetes y, a continuación, haga clic en Aceptar cuando se le pida que acepte los términos de licencia.

  4. Control + Haga clic en el proyecto PushDemoApi , elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar con modelos como nombre de carpeta.

  5. Control + Haga clic en la carpeta Modelos y, a continuación, elija Nuevo archivo... en el menú Agregar .

  6. SeleccioneClase vacíageneral>, escriba PushTemplates.cs en Nombre y, a continuación, haga clic en Nuevo agregando la siguiente implementación.

    namespace PushDemoApi.Models
    {
        public class PushTemplates
        {
            public class Generic
            {
                public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }";
                public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }";
            }
    
            public class Silent
            {
                public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }";
                public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }";
            }
        }
    }
    

    Nota:

    Esta clase contiene las cargas de notificación tokenizadas para las notificaciones genéricas y silenciosas requeridas por este escenario. Las cargas se definen fuera de la instalación para permitir la experimentación sin tener que actualizar las instalaciones existentes a través del servicio. El control de los cambios en las instalaciones de esta manera está fuera del ámbito de este tutorial. Para producción, considere la posibilidad de usar plantillas personalizadas.

  7. Agregue otra clase vacía a la carpeta Models denominada DeviceInstallation.cs y agregue la siguiente implementación.

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class DeviceInstallation
        {
            [Required]
            public string InstallationId { get; set; }
    
            [Required]
            public string Platform { get; set; }
    
            [Required]
            public string PushChannel { get; set; }
    
            public IList<string> Tags { get; set; } = Array.Empty<string>();
        }
    }
    
  8. Agregue otra clase vacía a la carpeta Models denominada NotificationRequest.cs y agregue la siguiente implementación.

    using System;
    
    namespace PushDemoApi.Models
    {
        public class NotificationRequest
        {
            public string Text { get; set; }
            public string Action { get; set; }
            public string[] Tags { get; set; } = Array.Empty<string>();
            public bool Silent { get; set; }
        }
    }
    
  9. Agregue otra clase vacía a la carpeta Models denominada NotificationHubOptions.cs y agregue la siguiente implementación.

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. Agregue una nueva carpeta al proyecto PushDemoApi denominado Services.

  11. Agregue una interfaz vacía a la carpeta Servicios denominada INotificationService.cs y agregue la siguiente implementación.

    using System.Threading;
    using System.Threading.Tasks;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public interface INotificationService
        {
            Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
            Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
            Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
        }
    }
    
  12. Agregue una clase vacía a la carpeta Services denominada NotificationHubsService.cs y agregue el código siguiente para implementar la interfaz INotificationService :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public class NotificationHubService : INotificationService
        {
            readonly NotificationHubClient _hub;
            readonly Dictionary<string, NotificationPlatform> _installationPlatform;
            readonly ILogger<NotificationHubService> _logger;
    
            public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger)
            {
                _logger = logger;
                _hub = NotificationHubClient.CreateClientFromConnectionString(
                    options.Value.ConnectionString,
                    options.Value.Name);
    
                _installationPlatform = new Dictionary<string, NotificationPlatform>
                {
                    { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns },
                    { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm }
                };
            }
    
            public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
                    return false;
    
                var installation = new Installation()
                {
                    InstallationId = deviceInstallation.InstallationId,
                    PushChannel = deviceInstallation.PushChannel,
                    Tags = deviceInstallation.Tags
                };
    
                if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
                    installation.Platform = platform;
                else
                    return false;
    
                try
                {
                    await _hub.CreateOrUpdateInstallationAsync(installation, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(installationId))
                    return false;
    
                try
                {
                    await _hub.DeleteInstallationAsync(installationId, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token)
            {
                if ((notificationRequest.Silent &&
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
                    (!notificationRequest.Silent &&
                    (string.IsNullOrWhiteSpace(notificationRequest?.Text)) ||
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)))
                    return false;
    
                var androidPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.Android :
                    PushTemplates.Generic.Android;
    
                var iOSPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.iOS :
                    PushTemplates.Generic.iOS;
    
                var androidPayload = PrepareNotificationPayload(
                    androidPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                var iOSPayload = PrepareNotificationPayload(
                    iOSPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                try
                {
                    if (notificationRequest.Tags.Length == 0)
                    {
                        // This will broadcast to all users registered in the notification hub
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token);
                    }
                    else if (notificationRequest.Tags.Length <= 20)
                    {
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token);
                    }
                    else
                    {
                        var notificationTasks = notificationRequest.Tags
                            .Select((value, index) => (value, index))
                            .GroupBy(g => g.index / 20, i => i.value)
                            .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token));
    
                        await Task.WhenAll(notificationTasks);
                    }
    
                    return true;
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Unexpected error sending notification");
                    return false;
                }
            }
    
            string PrepareNotificationPayload(string template, string text, string action) => template
                .Replace("$(alertMessage)", text, StringComparison.InvariantCulture)
                .Replace("$(alertAction)", action, StringComparison.InvariantCulture);
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
        }
    }
    

    Nota:

    La expresión de etiqueta proporcionada a SendTemplateNotificationAsync está limitada a 20 etiquetas. Se limita a 6 para la mayoría de los operadores, pero la expresión solo contiene solicitudes organizativas (||) en este caso. Si hay más de 20 etiquetas en la solicitud, deben dividirse en varias solicitudes. Consulte la documentación sobre expresiones de etiqueta y enrutamiento para obtener más detalles.

  13. En Startup.cs, actualice el método ConfigureServices para agregar NotificationHubsService como una implementación singleton de INotificationService.

    
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddSingleton<INotificationService, NotificationHubService>();
    
        services.AddOptions<NotificationHubOptions>()
            .Configure(Configuration.GetSection("NotificationHub").Bind)
            .ValidateDataAnnotations();
    }
    

Creación de la API de notificaciones

  1. Control + Haga clic en la carpeta Controladores y, a continuación, elija Nuevo archivo... en el menú Agregar .

  2. Seleccione ASP.NET Core>Clase de controlador de APIweb, escriba NotificationsController como Nombre y, a continuación, haga clic en Nuevo.

    Nota:

    Si sigue con Visual Studio 2019, elija la plantilla Controlador de API con acciones de lectura y escritura .

  3. Agregue los siguientes espacios de nombres a la parte superior del archivo.

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
  4. Actualice el controlador con plantilla para que se derive de ControllerBase y esté decorado con el atributo ApiController .

    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    {
        // Templated methods here
    }
    

    Nota:

    La clase base Controller proporciona compatibilidad con vistas, pero esto no es necesario en este caso, por lo que Se puede usar ControllerBase en su lugar. Si sigue con Visual Studio 2019, puede omitir este paso.

  5. Si decide completar la sección Autenticar clientes mediante una clave de API , también debe decorar NotificationsController con el atributo Authorize .

    [Authorize]
    
  6. Actualice el constructor para aceptar la instancia registrada de INotificationService como argumento y asignarla a un miembro de solo lectura.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. En launchSettings.json (dentro de la carpeta Properties ), cambie launchUrl de weatherforecast a api/notifications para que coincida con la dirección URL especificada en el atributo RegistrationsControllerRoute .

  8. Inicie la depuración (Comando + Entrar) para validar que la aplicación funciona con el nuevo NotificationsController y devuelve un estado 401 No autorizado .

    Nota:

    Es posible que Visual Studio no inicie automáticamente la aplicación en el explorador. Usará Postman para probar la API desde este punto.

  9. En una nueva pestaña postman , establezca la solicitud en GET. Escriba la dirección siguiente reemplazando el marcador< de posición applicationUrl por https applicationUrl> que se encuentra en Propiedades>launchSettings.json.

    <applicationUrl>/api/notifications
    

    Nota:

    ApplicationUrl debe ser "https://localhost:5001" para el perfil predeterminado. Si usa IIS (valor predeterminado en Visual Studio 2019 en Windows), debe usar applicationUrl especificado en el elemento iisSettings en su lugar. Recibirá una respuesta 404 si la dirección es incorrecta.

  10. Si eligió completar la sección Autenticar clientes mediante una clave de API , asegúrese de configurar los encabezados de solicitud para incluir el valor de apikey .

    Clave Valor
    apikey <your_api_key>
  11. Haga clic en el botón Enviar .

    Nota:

    Debe recibir un estado correcto 200 con algún contenido JSON .

    Si recibe una advertencia de comprobación del certificado SSL, puede desactivar la configuración de Postman de verificación de certificado SSL de solicitud en configuración.

  12. Reemplace los métodos de clase con plantilla en NotificationsController.cs por el código siguiente.

    [HttpPut]
    [Route("installations")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> UpdateInstallation(
        [Required]DeviceInstallation deviceInstallation)
    {
        var success = await _notificationService
            .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpDelete()]
    [Route("installations/{installationId}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<ActionResult> DeleteInstallation(
        [Required][FromRoute]string installationId)
    {
        var success = await _notificationService
            .DeleteInstallationByIdAsync(installationId, CancellationToken.None);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpPost]
    [Route("requests")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> RequestPush(
        [Required]NotificationRequest notificationRequest)
    {
        if ((notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
            (!notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Text)))
            return new BadRequestResult();
    
        var success = await _notificationService
            .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    

Creación de la aplicación de API

Ahora creará una aplicación de API en Azure App Service para hospedar el servicio back-end.

  1. Inicie sesión en Azure Portal.

  2. Haga clic en Crear un recurso, busque y elija Aplicación de API y, a continuación, haga clic en Crear.

  3. Actualice los campos siguientes y, a continuación, haga clic en Crear.

    Nombre de la aplicación:
    Escriba un nombre único global para la aplicación de API.

    Suscripción:
    Elija la misma suscripción de destino en la que creó el centro de notificaciones.

    Grupo de recursos:
    Elija el mismo grupo de recursos en el que creó el centro de notificaciones.

    App Service plan o ubicación:
    Crear un nuevo plan de App Service

    Nota:

    Cambie de la opción predeterminada a un plan que incluya compatibilidad con SSL . De lo contrario, deberá realizar los pasos adecuados al trabajar con la aplicación móvil para evitar que se bloqueen las solicitudes HTTP .

    Application Insights:
    Mantenga la opción sugerida (se creará un nuevo recurso con ese nombre) o elija un recurso existente.

  4. Una vez que se ha aprovisionado la aplicación de API , vaya a ese recurso.

  5. Anote la propiedad URL en el resumen de Essentials en la parte superior de La información general. Esta dirección URL es el punto de conexión de back-end que se usará más adelante en este tutorial.

    Nota:

    La dirección URL usa el nombre de la aplicación de API que especificó anteriormente, con el formato https://<app_name>.azurewebsites.net.

  6. Seleccione Configuración en la lista (en Configuración).

  7. Para cada una de las opciones de configuración siguientes, haga clic en Nueva configuración de la aplicación para escribir el nombre y un valor y, a continuación, haga clic en Aceptar.

    Nombre Valor
    Authentication:ApiKey <api_key_value>
    NotificationHub:Name <hub_name_value>
    NotificationHub:ConnectionString <hub_connection_string_value>

    Nota:

    Se trata de la misma configuración que definió anteriormente en la configuración de usuario. Debería poder copiarlos. La configuración Authentication:ApiKey solo es necesaria si decide completar la sección Autenticar clientes mediante una clave de API . En escenarios de producción, puede ver opciones como Azure KeyVault. Estos se han agregado como configuración de la aplicación para simplificar en este caso.

  8. Una vez agregada toda la configuración de la aplicación, haga clic en Guardar y, a continuación, en Continuar.

Publicación del servicio back-end

A continuación, implementará la aplicación en la aplicación de API para que sea accesible desde todos los dispositivos.

Nota:

Los pasos siguientes son específicos de Visual Studio para Mac. Si sigue con Visual Studio 2019 en Windows, el flujo de publicación será diferente. Consulte Publicar en Azure App Service en Windows.

  1. Cambie la configuración de Depuración a Versión si aún no lo ha hecho.

  2. Control + Haga clic en el proyecto PushDemoApi y, a continuación, elija Publicar en Azure... en el menú Publicar .

  3. Siga el flujo de autenticación si se le pide que lo haga. Use la cuenta que usó en la sección anterior creación de la aplicación de API .

  4. Seleccione la aplicación de API de Azure App Service que creó anteriormente en la lista como destino de publicación y, a continuación, haga clic en Publicar.

Una vez completado el asistente, publica la aplicación en Azure y, a continuación, abre la aplicación. Tome nota de la dirección URL si aún no lo ha hecho. Esta dirección URL es el punto de conexión de back-end que se usa más adelante en este tutorial.

Validación de la API publicada

  1. En Postman , abra una nueva pestaña, establezca la solicitud en PUT y escriba la dirección siguiente. Reemplace el marcador de posición por la dirección base que anotó en la sección anterior publicar el servicio back-end .

    https://<app_name>.azurewebsites.net/api/notifications/installations
    

    Nota:

    La dirección base debe tener el formato . https://<app_name>.azurewebsites.net/

  2. Si eligió completar la sección Autenticar clientes mediante una clave de API , asegúrese de configurar los encabezados de solicitud para incluir el valor de apikey .

    Clave Valor
    apikey <your_api_key>
  3. Elija la opción sin formato del cuerpo, elija JSON en la lista de opciones de formato y, a continuación, incluya algún contenido JSON de marcador de posición:

    {}
    
  4. Haga clic en Enviar.

    Nota:

    Debe recibir un estado 422 UnprocessableEntity del servicio.

  5. Vuelva a realizar los pasos del 1 al 4, pero esta vez especifique el punto de conexión de solicitudes para validar que recibe una respuesta de solicitud incorrecta 400 .

    https://<app_name>.azurewebsites.net/api/notifications/requests
    

Nota:

Todavía no es posible probar la API con datos de solicitud válidos, ya que esto requerirá información específica de la plataforma de la aplicación móvil cliente.

Creación de una aplicación flutter multiplataforma

En esta sección, creará una aplicación móvil de Flutter que implementa notificaciones push de forma multiplataforma.

Permite registrar y anular el registro desde un centro de notificaciones a través del servicio back-end que ha creado.

Se muestra una alerta cuando se especifica una acción y la aplicación está en primer plano. De lo contrario, las notificaciones aparecen en el Centro de notificaciones.

Nota:

Normalmente, realizaría las acciones de registro (y desregistración) durante el punto adecuado del ciclo de vida de la aplicación (o como parte de la experiencia de primera ejecución quizás) sin entradas explícitas de registro o registro del usuario. Sin embargo, este ejemplo requerirá una entrada explícita del usuario para permitir que esta funcionalidad se explore y pruebe más fácilmente.

Creación de la solución Flutter

  1. Abra una nueva instancia de Visual Studio Code.

  2. Abra la paleta de comandos (Mayús + Comando + P).

  3. Seleccione el comando Flutter: New Project y presione Entrar.

  4. Escriba push_demo en Nombre del proyecto y, a continuación, seleccione una ubicación del proyecto.

  5. Cuando se le pida que lo haga, elija Obtener paquetes.

  6. Control + Haga clic en la carpeta kotlin (en app>src>main) y, a continuación, elija Reveal in Finder (Mostrar en Finder). A continuación, cambie el nombre de las carpetas secundarias (en la carpeta kotlin ) a com, <your_organization>y pushdemo respectivamente.

    Nota:

    Al usar la plantilla de Visual Studio Code, estas carpetas son com,por ejemplo, <project_name>. Suponiendo que mobcat se usa para la organización, la estructura de carpetas debe aparecer indicativamente como:

    • kotlin
      • Com
        • mobcat
          • pushdemo
  7. De nuevo en Visual Studio Code, actualice el valor applicationId en android>app>build.gradle a com.<your_organization>.pushdemo.

    Nota:

    Debe usar su propio nombre de organización para el <marcador de posición your_organization> . Por ejemplo, el uso de mobcat como organización dará como resultado un valor de nombre de paquete de com.mobcat.pushdemo.

  8. Actualice el atributo package en los archivos AndroidManifest.xml , en src>debug, src>main y src>profile respectivamente. Asegúrese de que los valores coinciden con el valor applicationId que usó en el paso anterior.

    <manifest
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.<your_organization>.pushdemo>">
        ...
    </manifest>
    
  9. Actualice el android:label atributo en el archivo AndroidManifest.xml bajo src>main a PushDemo. A continuación, agregue el android:allowBackup atributo, directamente en android:label, estableciendo su valor en false.

    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="PushDemo"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher">
        ...
    </application>
    
  10. Abra el archivo build.gradle de nivel de aplicación (android>app>build.gradle) y, a continuación, actualice compileSdkVersion (desde la sección android ) para usar la API 29. A continuación, actualice los valores minSdkVersion y targetSdkVersion (de la sección defaultConfig ) a 26 y 29 respectivamente.

    Nota:

    Solo se admiten los dispositivos que ejecutan el nivel de API 26 y versiones posteriores para los fines de este tutorial, pero puede ampliarlo para admitir dispositivos que ejecutan versiones anteriores.

  11. Control + Haga clic en la carpeta ios y elija Abrir en Xcode.

  12. En Xcode, haga clic en Ejecutor ( xcodeproj en la parte superior, no en la carpeta). A continuación, seleccione el destino Ejecutor y seleccione la pestaña General . Con la configuración De compilación seleccionada, actualice el identificador de agrupación a com.<your_organization>.PushDemo.

    Nota:

    Debe usar su propio nombre de organización para el <marcador de posición your_organization> . Por ejemplo, el uso de mobcat como organización dará como resultado un valor de Identificador de agrupación de com.mobcat.PushDemo.

  13. Haga clic en Info.plist y, a continuación, actualice el valor del nombre del paquete a PushDemo.

  14. Cierre Xcode y vuelva a Visual Studio Code.

  15. De nuevo en Visual Studio Code, abra pubspec.yaml, agregue los paquetes http y flutter_secure_storageDart como dependencias. A continuación, guarde el archivo y haga clic en Obtener paquetes cuando se le pida que lo haga.

    dependencies:
      flutter:
        sdk: flutter
    
      http: ^0.12.1
      flutter_secure_storage: ^3.3.3
    
  16. En Terminal, cambie el directorio a la carpeta ios (para el proyecto de Flutter). A continuación, ejecute el comando de instalación del pod para instalar nuevos pods (requeridos por el paquete de flutter_secure_storage ).

  17. Control + Haga clic en la carpeta lib y, a continuación, elija Nuevo archivo en el menú con main_page.dart como nombre de archivo. A continuación, agregue el código siguiente.

    import 'package:flutter/material.dart';
    
    class MainPage extends StatefulWidget {
      @override
      _MainPageState createState() => _MainPageState();
    }
    
    class _MainPageState extends State<MainPage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[],
            )
          )
        );
      }
    }
    
  18. En main.dart, reemplace el código con plantilla por lo siguiente.

    import 'package:flutter/material.dart';
    import 'package:push_demo/main_page.dart';
    
    final navigatorKey = GlobalKey<NavigatorState>();
    
    void main() => runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey));
    
  19. En Terminal, compile y ejecute la aplicación en cada plataforma de destino para probar las ejecuciones de la aplicación con plantilla en los dispositivos. Asegúrese de que los dispositivos compatibles están conectados.

    flutter run
    

Implementación de los componentes multiplataforma

  1. Control + Haga clic en la carpeta lib y, a continuación, elija Nueva carpeta en el menú con modelos como nombre de carpeta.

  2. Control + Haga clic en la carpeta models y, a continuación, elija Nuevo archivo en el menú con device_installation.dart como nombre de archivo. A continuación, agregue el código siguiente.

    class DeviceInstallation {
        final String deviceId;
        final String platform;
        final String token;
        final List<String> tags;
    
        DeviceInstallation(this.deviceId, this.platform, this.token, this.tags);
    
        DeviceInstallation.fromJson(Map<String, dynamic> json)
          : deviceId = json['installationId'],
            platform = json['platform'],
            token = json['pushChannel'],
            tags = json['tags'];
    
        Map<String, dynamic> toJson() =>
        {
          'installationId': deviceId,
          'platform': platform,
          'pushChannel': token,
          'tags': tags,
        };
    }
    
  3. Agregue un nuevo archivo a la carpeta models denominada push_demo_action.dart que define la enumeración de acciones que se admiten en este ejemplo.

    enum PushDemoAction {
      actionA,
      actionB,
    }
    
  4. Agregue una nueva carpeta al proyecto denominado services y agregue un nuevo archivo a esa carpeta denominada device_installation_service.dart con la siguiente implementación.

    import 'package:flutter/services.dart';
    
    class DeviceInstallationService {
      static const deviceInstallation = const MethodChannel('com.<your_organization>.pushdemo/deviceinstallation');
      static const String getDeviceIdChannelMethod = "getDeviceId";
      static const String getDeviceTokenChannelMethod = "getDeviceToken";
      static const String getDevicePlatformChannelMethod = "getDevicePlatform";
    
      Future<String> getDeviceId() {
        return deviceInstallation.invokeMethod(getDeviceIdChannelMethod);
      }
    
      Future<String> getDeviceToken() {
        return deviceInstallation.invokeMethod(getDeviceTokenChannelMethod);
      }
    
      Future<String> getDevicePlatform() {
        return deviceInstallation.invokeMethod(getDevicePlatformChannelMethod);
      }
    }
    

    Nota:

    Debe usar su propio nombre de organización para el <marcador de posición your_organization> . Por ejemplo, el uso de mobcat como organización dará como resultado un nombre MethodChannel de com.mobcat.pushdemo/deviceinstallation.

    Esta clase encapsula el trabajo con la plataforma nativa subyacente para adquirir los detalles de instalación de dispositivos necesarios. Un MethodChannel facilita la comunicación asincrónica bidireccional con las plataformas nativas subyacentes. El homólogo específico de la plataforma para este canal se creará en los pasos posteriores.

  5. Agregue otro archivo a esa carpeta denominada notification_action_service.dart con la siguiente implementación.

    import 'package:flutter/services.dart';
    import 'dart:async';
    import 'package:push_demo/models/push_demo_action.dart';
    
    class NotificationActionService {
      static const notificationAction =
          const MethodChannel('com.<your_organization>.pushdemo/notificationaction');
      static const String triggerActionChannelMethod = "triggerAction";
      static const String getLaunchActionChannelMethod = "getLaunchAction";
    
      final actionMappings = {
        'action_a' : PushDemoAction.actionA,
        'action_b' : PushDemoAction.actionB
      };
    
      final actionTriggeredController = StreamController.broadcast();
    
      NotificationActionService() {
        notificationAction
            .setMethodCallHandler(handleNotificationActionCall);
      }
    
      Stream get actionTriggered => actionTriggeredController.stream;
    
      Future<void> triggerAction({action: String}) async {
    
        if (!actionMappings.containsKey(action)) {
          return;
        }
    
        actionTriggeredController.add(actionMappings[action]);
      }
    
      Future<void> checkLaunchAction() async {
        final launchAction = await notificationAction.invokeMethod(getLaunchActionChannelMethod) as String;
    
        if (launchAction != null) {
          triggerAction(action: launchAction);
        }
      }
    
      Future<void> handleNotificationActionCall(MethodCall call) async {
        switch (call.method) {
          case triggerActionChannelMethod:
            return triggerAction(action: call.arguments as String);
          default:
            throw MissingPluginException();
            break;
        }
      }
    }
    

    Nota:

    Se usa como un mecanismo sencillo para centralizar el control de las acciones de notificación para que se puedan controlar de forma multiplataforma mediante una enumeración fuertemente tipada. El servicio permite que la plataforma nativa subyacente desencadene una acción, cuando se especifica una en la carga de notificación. También permite al código común comprobar retrospectivamente si se especificó una acción durante el inicio de la aplicación una vez que Flutter está listo para procesarla. Por ejemplo, cuando la aplicación se inicia pulsando en una notificación desde el centro de notificaciones.

  6. Agregue un nuevo archivo a la carpeta services denominada notification_registration_service.dart con la siguiente implementación.

    import 'dart:convert';
    import 'package:flutter/services.dart';
    import 'package:http/http.dart' as http;
    import 'package:push_demo/services/device_installation_service.dart';
    import 'package:push_demo/models/device_installation.dart';
    import 'package:flutter_secure_storage/flutter_secure_storage.dart';
    
    class NotificationRegistrationService {
      static const notificationRegistration =
          const MethodChannel('com.<your_organization>.pushdemo/notificationregistration');
    
      static const String refreshRegistrationChannelMethod = "refreshRegistration";
      static const String installationsEndpoint = "api/notifications/installations";
      static const String cachedDeviceTokenKey = "cached_device_token";
      static const String cachedTagsKey = "cached_tags";
    
      final deviceInstallationService = DeviceInstallationService();
      final secureStorage = FlutterSecureStorage();
    
      String baseApiUrl;
      String apikey;
    
      NotificationRegistrationService(this.baseApiUrl, this.apikey) {
        notificationRegistration
            .setMethodCallHandler(handleNotificationRegistrationCall);
      }
    
      String get installationsUrl => "$baseApiUrl$installationsEndpoint";
    
      Future<void> deregisterDevice() async {
        final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey);
        final serializedTags = await secureStorage.read(key: cachedTagsKey);
    
        if (cachedToken == null || serializedTags == null) {
          return;
        }
    
        var deviceId = await deviceInstallationService.getDeviceId();
    
        if (deviceId.isEmpty) {
          throw "Unable to resolve an ID for the device.";
        }
    
        var response = await http
            .delete("$installationsUrl/$deviceId", headers: {"apikey": apikey});
    
        if (response.statusCode != 200) {
          throw "Deregister request failed: ${response.reasonPhrase}";
        }
    
        await secureStorage.delete(key: cachedDeviceTokenKey);
        await secureStorage.delete(key: cachedTagsKey);
      }
    
      Future<void> registerDevice(List<String> tags) async {
        try {
          final deviceId = await deviceInstallationService.getDeviceId();
          final platform = await deviceInstallationService.getDevicePlatform();
          final token = await deviceInstallationService.getDeviceToken();
    
          final deviceInstallation =
              DeviceInstallation(deviceId, platform, token, tags);
    
          final response = await http.put(installationsUrl,
              body: jsonEncode(deviceInstallation),
              headers: {"apikey": apikey, "Content-Type": "application/json"});
    
          if (response.statusCode != 200) {
            throw "Register request failed: ${response.reasonPhrase}";
          }
    
          final serializedTags = jsonEncode(tags);
    
          await secureStorage.write(key: cachedDeviceTokenKey, value: token);
          await secureStorage.write(key: cachedTagsKey, value: serializedTags);
        } on PlatformException catch (e) {
          throw e.message;
        } catch (e) {
          throw "Unable to register device: $e";
        }
      }
    
      Future<void> refreshRegistration() async {
        final currentToken = await deviceInstallationService.getDeviceToken();
        final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey);
        final serializedTags = await secureStorage.read(key: cachedTagsKey);
    
        if (currentToken == null ||
            cachedToken == null ||
            serializedTags == null ||
            currentToken == cachedToken) {
          return;
        }
    
        final tags = jsonDecode(serializedTags);
    
        return registerDevice(tags);
      }
    
      Future<void> handleNotificationRegistrationCall(MethodCall call) async {
        switch (call.method) {
          case refreshRegistrationChannelMethod:
            return refreshRegistration();
          default:
            throw MissingPluginException();
            break;
        }
      }
    }
    

    Nota:

    Esta clase encapsula el uso de DeviceInstallationService y las solicitudes al servicio back-end para realizar las acciones de registro, anulación del registro y actualización necesarias. El argumento apiKey solo es necesario si decide completar la sección Autenticar clientes mediante una clave de API .

  7. Agregue un nuevo archivo a la carpeta lib denominada config.dart con la siguiente implementación.

    class Config {
      static String apiKey = "API_KEY";
      static String backendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT";
    }
    

    Nota:

    Esto se usa como una manera sencilla de definir secretos de aplicación. Reemplace los valores de marcador de posición por los suyos propios. Debe haber tomado nota de estos al compilar el servicio back-end. La dirección URL de la aplicación de API debe ser https://<api_app_name>.azurewebsites.net/. El miembro apiKey solo es necesario si decide completar la sección Autenticar clientes mediante una clave de API .

    Asegúrese de agregarlo al archivo gitignore para evitar confirmar estos secretos en el control de código fuente.

Implementación de la interfaz de usuario multiplataforma

  1. En main_page.dart, reemplace la función de compilación por lo siguiente.

    @override
    Widget build(BuildContext context) {
    return Scaffold(
        body: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 40.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              FlatButton(
                child: Text("Register"),
                onPressed: registerButtonClicked,
              ),
              FlatButton(
                child: Text("Deregister"),
                onPressed: deregisterButtonClicked,
              ),
            ],
          ),
        ),
      );
    }
    
  2. Agregue las importaciones necesarias a la parte superior del archivo main_page.dart .

    import 'package:push_demo/services/notification_registration_service.dart';
    import 'config.dart';
    
  3. Agregue un campo a la clase _MainPageState para almacenar una referencia a NotificationRegistrationService.

    final notificationRegistrationService = NotificationRegistrationService(Config.backendServiceEndpoint, Config.apiKey);
    
  4. En la clase _MainPageState , implemente los controladores de eventos para los botones Registrar y Anular registro en eventospressed . Llame a los métodos Register/Deregister correspondientes y muestre una alerta para indicar el resultado.

    void registerButtonClicked() async {
        try {
          await notificationRegistrationService.registerDevice(List<String>());
          await showAlert(message: "Device registered");
        }
        catch (e) {
          await showAlert(message: e);
        }
      }
    
      void deregisterButtonClicked() async {
        try {
          await notificationRegistrationService.deregisterDevice();
          await showAlert(message: "Device deregistered");
        }
        catch (e) {
          await showAlert(message: e);
        }
      }
    
      Future<void> showAlert({ message: String }) async {
        return showDialog<void>(
          context: context,
          barrierDismissible: false,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('PushDemo'),
              content: SingleChildScrollView(
                child: ListBody(
                  children: <Widget>[
                    Text(message),
                  ],
                ),
              ),
              actions: <Widget>[
                FlatButton(
                  child: Text('OK'),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
              ],
            );
          },
        );
      }
    
  5. Ahora en main.dart, asegúrese de que las siguientes importaciones están presentes en la parte superior del archivo.

    import 'package:flutter/material.dart';
    import 'package:push_demo/models/push_demo_action.dart';
    import 'package:push_demo/services/notification_action_service.dart';
    import 'package:push_demo/main_page.dart';
    
  6. Declare una variable para almacenar la referencia a una instancia de NotificationActionService e inicialícela.

    final notificationActionService = NotificationActionService();
    
  7. Agregue funciones para controlar la visualización de una alerta cuando se desencadene una acción.

    void notificationActionTriggered(PushDemoAction action) {
      showActionAlert(message: "${action.toString().split(".")[1]} action received");
    }
    
    Future<void> showActionAlert({ message: String }) async {
      return showDialog<void>(
        context: navigatorKey.currentState.overlay.context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return AlertDialog(
            title: Text('PushDemo'),
            content: SingleChildScrollView(
              child: ListBody(
                children: <Widget>[
                  Text(message),
                ],
              ),
            ),
            actions: <Widget>[
              FlatButton(
                child: Text('OK'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          );
        },
      );
    }
    
  8. Actualice la función principal para observar la secuencia NotificationActionServiceTriggered y comprobar las acciones capturadas durante el inicio de la aplicación.

    void main() async {
      runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey,));
      notificationActionService.actionTriggered.listen((event) { notificationActionTriggered(event as PushDemoAction); });
      await notificationActionService.checkLaunchAction();
    }
    

    Nota:

    Esto es simplemente para demostrar la recepción y propagación de acciones de notificación de inserción. Normalmente, estos se controlarían silenciosamente, por ejemplo, navegando a una vista específica o actualizando algunos datos en lugar de mostrar una alerta en este caso.

Configuración del proyecto nativo de Android para notificaciones push

Agregar el archivo JSON de Google Services

  1. Control + Haga clic en la carpeta android y, a continuación, elija Abrir en Android Studio. A continuación, cambie a la vista Proyecto (si aún no lo está).

  2. Busque el archivo google-services.json que descargó anteriormente al configurar el proyecto PushDemo en la consola de Firebase. A continuación, arrástrelo al directorio raíz del módulo de aplicación (aplicaciónandroid>android>).

Configuración de los permisos y opciones de compilación

  1. Cambie la vista Proyecto a Android.

  2. Abra AndroidManifest.xmly agregue los permisos internet y READ_PHONE_STATE después del elemento de aplicación antes de la etiqueta de cierre .

    <manifest>
        <application>...</application>
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    </manifest>
    

Adición de los SDK de Firebase

  1. En Android Studio, abra el archivo build.gradle de nivel de proyecto (Gradle Scripts>build.gradle (Project: android)). y asegúrese de que tiene la ruta de clase "com.google.gms:google-services" en el buildscript> nodo de dependencias.

    buildscript {
    
      repositories {
        // Check that you have the following line (if not, add it):
        google()  // Google's Maven repository
      }
    
      dependencies {
        // ...
    
        // Add the following line:
        classpath 'com.google.gms:google-services:4.3.3'  // Google Services plugin
      }
    }
    
    allprojects {
      // ...
    
      repositories {
        // Check that you have the following line (if not, add it):
        google()  // Google's Maven repository
        // ...
      }
    }
    

    Nota:

    Asegúrese de hacer referencia a la versión más reciente según las instrucciones proporcionadas en la consola firebase al crear el proyecto de Android.

  2. En el archivo build.gradle de nivel de aplicación (Gradle Scripts>build.gradle (Módulo: aplicación)), aplique el complemento Gradle de Servicios de Google. Aplique el complemento justo encima del nodo android .

    // ...
    
    // Add the following line:
    apply plugin: 'com.google.gms.google-services'  // Google Services plugin
    
    android {
      // ...
    }
    
  3. En el mismo archivo, en el nodo dependencias , agregue la dependencia para la biblioteca android de Mensajería en la nube .

    dependencies {
        // ...
        implementation 'com.google.firebase:firebase-messaging:20.2.0'
    }
    

    Nota:

    Asegúrese de hacer referencia a la versión más reciente según la documentación del cliente android de Cloud Messaging.

  4. Guarde los cambios y, a continuación, haga clic en el botón Sincronizar ahora (desde el símbolo del sistema de la barra de herramientas) o Sincronizar proyecto con archivos gradle.

Controlar las notificaciones push para Android

  1. En Android Studio, control + Haga clic en la carpeta del paquete com.your_organization.pushdemo<> (app>src>main>kotlin), elija Paquete en el menú Nuevo. Escriba los servicios como nombre y presione Devolver.

  2. Control + Haga clic en la carpeta services y elija Kotlin File/Class en el menú Nuevo . Escriba DeviceInstallationService como nombre y presione Devolver.

  3. Implemente DeviceInstallationService con el código siguiente.

    package com.<your_organization>.pushdemo.services
    
    import android.annotation.SuppressLint
    import android.content.Context
    import android.provider.Settings.Secure
    import com.google.android.gms.common.ConnectionResult
    import com.google.android.gms.common.GoogleApiAvailability
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.MethodCall
    import io.flutter.plugin.common.MethodChannel
    
    @SuppressLint("HardwareIds")
    class DeviceInstallationService {
    
        companion object {
            const val DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation"
            const val GET_DEVICE_ID = "getDeviceId"
            const val GET_DEVICE_TOKEN = "getDeviceToken"
            const val GET_DEVICE_PLATFORM = "getDevicePlatform"
        }
    
        private var context: Context
        private var deviceInstallationChannel : MethodChannel
    
        val playServicesAvailable
            get() = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
    
        constructor(context: Context, flutterEngine: FlutterEngine) {
            this.context = context
            deviceInstallationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, DEVICE_INSTALLATION_CHANNEL)
            deviceInstallationChannel.setMethodCallHandler { call, result -> handleDeviceInstallationCall(call, result) }
        }
    
        fun getDeviceId() : String
            = Secure.getString(context.applicationContext.contentResolver, Secure.ANDROID_ID)
    
        fun getDeviceToken() : String {
            if(!playServicesAvailable) {
                throw Exception(getPlayServicesError())
            }
    
            // TODO: Revisit once we have created the PushNotificationsFirebaseMessagingService
            val token = "Placeholder_Get_Value_From_FirebaseMessagingService_Implementation"
    
            if (token.isNullOrBlank()) {
                throw Exception("Unable to resolve token for FCM.")
            }
    
            return token
        }
    
        fun getDevicePlatform() : String = "fcm"
    
        private fun handleDeviceInstallationCall(call: MethodCall, result: MethodChannel.Result) {
            when (call.method) {
                GET_DEVICE_ID -> {
                    result.success(getDeviceId())
                }
                GET_DEVICE_TOKEN -> {
                    getDeviceToken(result)
                }
                GET_DEVICE_PLATFORM -> {
                    result.success(getDevicePlatform())
                }
                else -> {
                    result.notImplemented()
                }
            }
        }
    
        private fun getDeviceToken(result: MethodChannel.Result) {
            try {
                val token = getDeviceToken()
                result.success(token)
            }
            catch (e: Exception) {
                result.error("ERROR", e.message, e)
            }
        }
    
        private fun getPlayServicesError(): String {
            val resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
    
            if (resultCode != ConnectionResult.SUCCESS) {
                return if (GoogleApiAvailability.getInstance().isUserResolvableError(resultCode)){
                    GoogleApiAvailability.getInstance().getErrorString(resultCode)
                } else {
                    "This device is not supported"
                }
            }
    
            return "An error occurred preventing the use of push notifications"
        }
    }
    

    Nota:

    Esta clase implementa el homólogo específico de la plataforma para el com.<your_organization>.pushdemo/deviceinstallation canal. Esto se definió en la parte Flutter de la aplicación en DeviceInstallationService.dart. En este caso, las llamadas se realizan desde el código común al host nativo. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.

    Esta clase proporciona un identificador único (mediante Secure.AndroidId) como parte de la carga de registro del centro de notificaciones.

  4. Agregue otro archivo o clase kotlin a la carpeta services llamada NotificationRegistrationService y agregue el código siguiente.

    package com.<your_organization>.pushdemo.services
    
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.MethodChannel
    
    class NotificationRegistrationService {
    
        companion object {
            const val NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration"
            const val REFRESH_REGISTRATION = "refreshRegistration"
        }
    
        private var notificationRegistrationChannel : MethodChannel
    
        constructor(flutterEngine: FlutterEngine) {
            notificationRegistrationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationRegistrationService.NOTIFICATION_REGISTRATION_CHANNEL)
        }
    
        fun refreshRegistration() {
            notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, null)
        }
    }
    

    Nota:

    Esta clase implementa el homólogo específico de la plataforma para el com.<your_organization>.pushdemo/notificationregistration canal. Esto se definió en la parte Flutter de la aplicación en NotificationRegistrationService.dart. En este caso, las llamadas se realizan desde el host nativo al código común. De nuevo, tenga cuidado de reemplazar <your_organization> por su propia organización donde se use.

  5. Agregue otro archivo o clase kotlin a la carpeta services denominada NotificationActionService y agregue el código siguiente.

    package com.<your_organization>.pushdemo.services
    
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugin.common.MethodCall
    import io.flutter.plugin.common.MethodChannel
    
    class NotificationActionService {
        companion object {
            const val NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction"
            const val TRIGGER_ACTION = "triggerAction"
            const val GET_LAUNCH_ACTION = "getLaunchAction"
        }
    
        private var notificationActionChannel : MethodChannel
        var launchAction : String? = null
    
        constructor(flutterEngine: FlutterEngine) {
            notificationActionChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationActionService.NOTIFICATION_ACTION_CHANNEL)
            notificationActionChannel.setMethodCallHandler { call, result -> handleNotificationActionCall(call, result) }
        }
    
        fun triggerAction(action: String) {
            notificationActionChannel.invokeMethod(NotificationActionService.TRIGGER_ACTION, action)
        }
    
        private fun handleNotificationActionCall(call: MethodCall, result: MethodChannel.Result) {
            when (call.method) {
                NotificationActionService.GET_LAUNCH_ACTION -> {
                    result.success(launchAction)
                }
                else -> {
                    result.notImplemented()
                }
            }
        }
    }
    

    Nota:

    Esta clase implementa el homólogo específico de la plataforma para el com.<your_organization>.pushdemo/notificationaction canal. Esto se definió en la parte Flutter de la aplicación dentro de NotificationActionService.dart. Las llamadas se pueden realizar en ambas direcciones en este caso. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.

  6. Agregue un nuevo archivo o clase kotlin al paquete com.your_organization.pushdemo<> denominado PushNotificationsFirebaseMessagingService y, a continuación, implemente con el código siguiente.

    package com.<your_organization>.pushdemo
    
    import android.os.Handler
    import android.os.Looper
    import com.google.firebase.messaging.FirebaseMessagingService
    import com.google.firebase.messaging.RemoteMessage
    import com.<your_organization>.pushdemo.services.NotificationActionService
    import com.<your_organization>.pushdemo.services.NotificationRegistrationService
    
    class PushNotificationsFirebaseMessagingService : FirebaseMessagingService() {
    
        companion object {
            var token : String? = null
            var notificationRegistrationService : NotificationRegistrationService? = null
            var notificationActionService : NotificationActionService? = null
        }
    
        override fun onNewToken(token: String) {
            PushNotificationsFirebaseMessagingService.token = token
            notificationRegistrationService?.refreshRegistration()
        }
    
        override fun onMessageReceived(message: RemoteMessage) {
            message.data.let {
                Handler(Looper.getMainLooper()).post {
                    notificationActionService?.triggerAction(it.getOrDefault("action", null))
                }
            }
        }
    }
    

    Nota:

    Esta clase es responsable de controlar las notificaciones cuando la aplicación se ejecuta en primer plano. Llamará condicionalmente a triggerAction en NotificationActionService si se incluye una acción en la carga de notificación que se recibe en onMessageReceived. Esto también llamará a refreshRegistration en NotificationRegistrationService cuando el token de Firebase se vuelva a generar invalidando la función onNewToken .

    Una vez más, tenga cuidado de reemplazar <your_organization> por su propia organización donde se use.

  7. En AndroidManifest.xml (app>src>main), agregue PushNotificationsFirebaseMessagingService a la parte inferior del elemento de aplicación con el filtro de com.google.firebase.MESSAGING_EVENT intención.

    <manifest>
        <application>
            <!-- EXISTING MANIFEST CONTENT -->
             <service
                android:name="com.<your_organization>.pushdemo.PushNotificationsFirebaseMessagingService"
                android:exported="false">
                <intent-filter>
                    <action android:name="com.google.firebase.MESSAGING_EVENT" />
                </intent-filter>
            </service>
        </application>
    </manifest>
    
  8. De nuevo en DeviceInstallationService, asegúrese de que las importaciones siguientes están presentes en la parte superior del archivo.

    package com.<your_organization>.pushdemo
    import com.<your_organization>.pushdemo.services.PushNotificationsFirebaseMessagingService
    

    Nota:

    Reemplace <your_organization> por su propio valor de organización.

  9. Actualice el texto del marcador de posición Placeholder_Get_Value_From_FirebaseMessagingService_Implementation para obtener el valor del token de PushNotificationFirebaseMessagingService.

    fun getDeviceToken() : String {
        if(!playServicesAvailable) {
            throw Exception(getPlayServicesError())
        }
    
        // Get token from the PushNotificationsFirebaseMessagingService.token field.
        val token = PushNotificationsFirebaseMessagingService.token
    
        if (token.isNullOrBlank()) {
            throw Exception("Unable to resolve token for FCM.")
        }
    
        return token
    }
    
  10. En MainActivity, asegúrese de que las importaciones siguientes están presentes en la parte superior del archivo.

    package com.<your_organization>.pushdemo
    
    import android.content.Intent
    import android.os.Bundle
    import com.google.android.gms.tasks.OnCompleteListener
    import com.google.firebase.iid.FirebaseInstanceId
    import com.<your_organization>.pushdemo.services.DeviceInstallationService
    import com.<your_organization>.pushdemo.services.NotificationActionService
    import com.<your_organization>.pushdemo.services.NotificationRegistrationService
    import io.flutter.embedding.android.FlutterActivity
    

    Nota:

    Reemplace <your_organization> por su propio valor de organización.

  11. Agregue una variable para almacenar una referencia a DeviceInstallationService.

    private lateinit var deviceInstallationService: DeviceInstallationService
    
  12. Agregue una función denominada processNotificationActions para comprobar si una intención tiene un valor adicional denominado action. Desencadene condicionalmente esa acción o almacénela para usarla más adelante si la acción se procesa durante el inicio de la aplicación.

     private fun processNotificationActions(intent: Intent, launchAction: Boolean = false) {
        if (intent.hasExtra("action")) {
            var action = intent.getStringExtra("action");
    
            if (action.isNotEmpty()) {
                if (launchAction) {
                    PushNotificationsFirebaseMessagingService.notificationActionService?.launchAction = action
                }
                else {
                    PushNotificationsFirebaseMessagingService.notificationActionService?.triggerAction(action)
                }
            }
        }
    }
    
  13. Invalide la función onNewIntent para llamar a processNotificationActions.

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        processNotificationActions(intent)
    }
    

    Nota:

    Dado que LaunchMode para MainActivity se establece en SingleTop, se enviará una intención a la instancia de actividad existente a través de la función onNewIntent en lugar de la función onCreate y, por tanto, debe controlar una intención entrante en las funciones onCreate y onNewIntent .

  14. Invalide la función onCreate , establezca deviceInstallationService en una nueva instancia de DeviceInstallationService.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        flutterEngine?.let {
            deviceInstallationService = DeviceInstallationService(context, it)
        }
    }
    
  15. Establezca las propiedades notificationActionService y notificationRegistrationService en PushNotificationFirebaseMessagingServices.

    flutterEngine?.let {
      deviceInstallationService = DeviceInstallationService(context, it)
      PushNotificationsFirebaseMessagingService.notificationActionService = NotificationActionService(it)
      PushNotificationsFirebaseMessagingService.notificationRegistrationService = NotificationRegistrationService(it)
    }
    
  16. En la misma función, llame condicionalmente a FirebaseInstanceId.getInstance().instanceId. Implemente OnCompleteListener para establecer el valor del token resultante en PushNotificationFirebaseMessagingService antes de llamar a refreshRegistration.

    if(deviceInstallationService?.playServicesAvailable) {
        FirebaseInstanceId.getInstance().instanceId
            .addOnCompleteListener(OnCompleteListener { task ->
                if (!task.isSuccessful)
                    return@OnCompleteListener
    
                PushNotificationsFirebaseMessagingService.token = task.result?.token
                PushNotificationsFirebaseMessagingService.notificationRegistrationService?.refreshRegistration()
            })
    }
    
  17. Todavía en onCreate, llame a processNotificationActions al final de la función. Use true para el argumento launchAction para indicar que esta acción se está procesando durante el inicio de la aplicación.

    processNotificationActions(this.intent, true)
    

Nota:

Debe volver a registrar la aplicación cada vez que la ejecute y detenerla desde una sesión de depuración para continuar recibiendo notificaciones push.

Configuración del proyecto nativo de iOS para notificaciones push

Configuración del destino del ejecutor e Info.plist

  1. En Visual Studio Code, control + Haga clic en la carpeta ios y, a continuación, elija Abrir en Xcode.

  2. En Xcode, haga clic en Ejecutor ( xcodeproj en la parte superior, no en la carpeta) y, a continuación, seleccione el destino Ejecutor y, después, Firma & Capacidades. Con la configuración De compilación seleccionada, elija su cuenta de desarrollador para el equipo. Asegúrese de que la opción "Administrar automáticamente la firma" está activada y el certificado de firma y el perfil de aprovisionamiento se seleccionan automáticamente.

    Nota:

    Si no ve el nuevo valor de Perfil de aprovisionamiento, pruebe a actualizar los perfiles de la identidad de firma seleccionandoCuentade preferencias> de Xcode> y, a continuación, seleccione el botón Descargar perfiles manuales para descargar los perfiles.

  3. Haga clic en + Funcionalidad y busque Notificaciones push. Haga doble clic en Notificaciones push para agregar esta funcionalidad.

  4. Abra Info.plist y establezca Versión mínima del sistema en 13.0.

    Nota:

    Solo se admiten los dispositivos que ejecutan iOS 13.0 y versiones posteriores para los fines de este tutorial, pero puede ampliarlo para admitir dispositivos que ejecutan versiones anteriores.

  5. Abra Runner.entitlements y asegúrese de que la configuración del entorno de APS esté establecida en desarrollo.

Control de notificaciones push para iOS

  1. Control + Haga clic en la carpeta Runner (dentro del proyecto Runner) y, a continuación, elija Nuevo grupo con Servicios como nombre.

  2. Control + Haga clic en la carpeta Servicios y, a continuación, elija Nuevo archivo.... A continuación, elija Archivo Swift y haga clic en Siguiente. Especifique DeviceInstallationService como nombre y, a continuación, haga clic en Crear.

  3. Implemente DeviceInstallationService.swift con el código siguiente.

    import Foundation
    
    class DeviceInstallationService {
    
        enum DeviceRegistrationError: Error {
            case notificationSupport(message: String)
        }
    
        var token : Data? = nil
    
        let DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation"
        let GET_DEVICE_ID = "getDeviceId"
        let GET_DEVICE_TOKEN = "getDeviceToken"
        let GET_DEVICE_PLATFORM = "getDevicePlatform"
    
        private let deviceInstallationChannel : FlutterMethodChannel
    
        var notificationsSupported : Bool {
            get {
                if #available(iOS 13.0, *) {
                    return true
                }
                else {
                    return false
                }
            }
        }
    
        init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) {
            deviceInstallationChannel = FlutterMethodChannel(name: DEVICE_INSTALLATION_CHANNEL, binaryMessenger: binaryMessenger)
            deviceInstallationChannel.setMethodCallHandler(handleDeviceInstallationCall)
        }
    
        func getDeviceId() -> String {
            return UIDevice.current.identifierForVendor!.description
        }
    
        func getDeviceToken() throws -> String {
            if(!notificationsSupported) {
                let notificationSupportError = getNotificationsSupportError()
                throw DeviceRegistrationError.notificationSupport(message: notificationSupportError)
            }
    
            if (token == nil) {
                throw DeviceRegistrationError.notificationSupport(message: "Unable to resolve token for APNS.")
            }
    
            return token!.reduce("", {$0 + String(format: "%02X", $1)})
        }
    
        func getDevicePlatform() -> String {
            return "apns"
        }
    
        private func handleDeviceInstallationCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
            switch call.method {
            case GET_DEVICE_ID:
                result(getDeviceId())
            case GET_DEVICE_TOKEN:
                getDeviceToken(result: result)
            case GET_DEVICE_PLATFORM:
                result(getDevicePlatform())
            default:
                result(FlutterMethodNotImplemented)
            }
        }
    
        private func getDeviceToken(result: @escaping FlutterResult) {
            do {
                let token = try getDeviceToken()
                result(token)
            }
            catch let error {
                result(FlutterError(code: "UNAVAILABLE", message: error.localizedDescription, details: nil))
            }
        }
    
        private func getNotificationsSupportError() -> String {
    
            if (!notificationsSupported) {
                return "This app only supports notifications on iOS 13.0 and above. You are running \(UIDevice.current.systemVersion)"
            }
    
            return "An error occurred preventing the use of push notifications."
        }
    }
    

    Nota:

    Esta clase implementa el homólogo específico de la plataforma para el com.<your_organization>.pushdemo/deviceinstallation canal. Esto se definió en la parte Flutter de la aplicación en DeviceInstallationService.dart. En este caso, las llamadas se realizan desde el código común al host nativo. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.

    Esta clase proporciona un identificador único (mediante el valor UIDevice.identifierForVendor ) como parte de la carga de registro del centro de notificaciones.

  4. Agregue otro archivo Swift a la carpeta Services denominada NotificationRegistrationService y agregue el código siguiente.

    import Foundation
    
    class NotificationRegistrationService {
    
        let NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration"
        let REFRESH_REGISTRATION = "refreshRegistration"
    
        private let notificationRegistrationChannel : FlutterMethodChannel
    
        init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) {
           notificationRegistrationChannel = FlutterMethodChannel(name: NOTIFICATION_REGISTRATION_CHANNEL, binaryMessenger: binaryMessenger)
        }
    
        func refreshRegistration() {
            notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, arguments: nil)
        }
    }
    

    Nota:

    Esta clase implementa el homólogo específico de la plataforma para el com.<your_organization>.pushdemo/notificationregistration canal. Esto se definió en la parte Flutter de la aplicación en NotificationRegistrationService.dart. En este caso, las llamadas se realizan desde el host nativo al código común. De nuevo, tenga cuidado de reemplazar <your_organization> por su propia organización donde se use.

  5. Agregue otro archivo Swift a la carpeta Services denominada NotificationActionService y agregue el código siguiente.

    import Foundation
    
    class NotificationActionService {
    
        let NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction"
        let TRIGGER_ACTION = "triggerAction"
        let GET_LAUNCH_ACTION = "getLaunchAction"
    
        private let notificationActionChannel: FlutterMethodChannel
    
        var launchAction: String? = nil
    
        init(withBinaryMessenger binaryMessenger: FlutterBinaryMessenger) {
            notificationActionChannel = FlutterMethodChannel(name: NOTIFICATION_ACTION_CHANNEL, binaryMessenger: binaryMessenger)
            notificationActionChannel.setMethodCallHandler(handleNotificationActionCall)
        }
    
        func triggerAction(action: String) {
           notificationActionChannel.invokeMethod(TRIGGER_ACTION, arguments: action)
        }
    
        private func handleNotificationActionCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
            switch call.method {
            case GET_LAUNCH_ACTION:
                result(launchAction)
            default:
                result(FlutterMethodNotImplemented)
            }
        }
    }
    

    Nota:

    Esta clase implementa el homólogo específico de la plataforma para el com.<your_organization>.pushdemo/notificationaction canal. Esto se definió en la parte Flutter de la aplicación dentro de NotificationActionService.dart. Las llamadas se pueden realizar en ambas direcciones en este caso. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.

  6. En AppDelegate.swift, agregue variables para almacenar una referencia a los servicios que creó anteriormente.

    var deviceInstallationService : DeviceInstallationService?
    var notificationRegistrationService : NotificationRegistrationService?
    var notificationActionService : NotificationActionService?
    
  7. Agregue una función denominada processNotificationActions para procesar los datos de notificación. Desencadene condicionalmente esa acción o almacénela para usarla más adelante si la acción se procesa durante el inicio de la aplicación.

    func processNotificationActions(userInfo: [AnyHashable : Any], launchAction: Bool = false) {
        if let action = userInfo["action"] as? String {
            if (launchAction) {
                notificationActionService?.launchAction = action
            }
            else {
                notificationActionService?.triggerAction(action: action)
            }
        }
    }
    
  8. Invalide la función didRegisterForRemoteNotificationsWithDeviceToken estableciendo el valor del token para DeviceInstallationService. A continuación, llame a refreshRegistration en NotificationRegistrationService.

    override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
      deviceInstallationService?.token = deviceToken
      notificationRegistrationService?.refreshRegistration()
    }
    
  9. Invalide la función didReceiveRemoteNotification pasando el argumento userInfo a la función processNotificationActions .

    override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
        processNotificationActions(userInfo: userInfo)
    }
    
  10. Invalide la función didFailToRegisterForRemoteNotificationsWithError para registrar el error.

    override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print(error);
    }
    

    Nota:

    Esto es muy un marcador de posición. Querrá implementar el registro y el control de errores adecuados para escenarios de producción.

  11. En didFinishLaunchingWithOptions, cree una instancia de las variables deviceInstallationService, notificationRegistrationService y notificationActionService .

    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    
    deviceInstallationService = DeviceInstallationService(withBinaryMessenger: controller.binaryMessenger)
    notificationRegistrationService = NotificationRegistrationService(withBinaryMessenger: controller.binaryMessenger)
    notificationActionService = NotificationActionService(withBinaryMessenger: controller.binaryMessenger)
    
  12. En la misma función, solicite condicionalmente autorización y regístrese para las notificaciones remotas.

    if #available(iOS 13.0, *) {
      UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
          (granted, error) in
    
          if (granted)
          {
              DispatchQueue.main.async {
                  let pushSettings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil)
                  application.registerUserNotificationSettings(pushSettings)
                  application.registerForRemoteNotifications()
              }
          }
      }
    }
    
  13. Si launchOptions contiene la clave remoteNotification , llame a processNotificationActions al final de la función didFinishLaunchingWithOptions . Pase el objeto userInfo resultante y use true para el argumento launchAction . Un valor true indica que la acción se está procesando durante el inicio de la aplicación.

    if let userInfo = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
        processNotificationActions(userInfo: userInfo, launchAction: true)
    }
    

Prueba de la solución

Ahora puede probar el envío de notificaciones a través del servicio back-end.

Envío de una notificación de prueba

  1. Abra una nueva pestaña en Postman.

  2. Establezca la solicitud en POST y escriba la siguiente dirección:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Si eligió completar la sección Autenticar clientes mediante una clave de API , asegúrese de configurar los encabezados de solicitud para incluir el valor de apikey .

    Clave Valor
    apikey <your_api_key>
  4. Elija la opción sin formato del cuerpo, elija JSON en la lista de opciones de formato y, a continuación, incluya algún contenido JSON de marcador de posición:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Seleccione el botón Código , que se encuentra en el botón Guardar de la esquina superior derecha de la ventana. La solicitud debe ser similar al ejemplo siguiente cuando se muestra para HTML (dependiendo de si ha incluido un encabezado apikey ):

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from backend service",
        "action": "action_a"
    }
    
  6. Ejecute la aplicación PushDemo en una o ambas plataformas de destino (Android e iOS).

    Nota:

    Si está probando en Android , asegúrese de que no se está ejecutando en Depuración, o si la aplicación se ha implementado mediante la ejecución de la aplicación, forzará el cierre de la aplicación e iníciela de nuevo desde el iniciador.

  7. En la aplicación PushDemo , pulse en el botón Registrar .

  8. De nuevo en Postman, cierre la ventana Generar fragmentos de código (si aún no lo ha hecho) y haga clic en el botón Enviar .

  9. Compruebe que obtiene una respuesta 200 OK en Postman y la alerta aparece en la aplicación que muestra la acción ActionA recibida.

  10. Cierre la aplicación PushDemo y, a continuación, haga clic en el botón Enviar de nuevo en Postman.

  11. Compruebe que recibe una respuesta 200 OK en Postman de nuevo. Compruebe que aparece una notificación en el área de notificación de la aplicación PushDemo con el mensaje correcto.

  12. Pulse en la notificación para confirmar que abre la aplicación y muestra la alerta recibida de la acción ActionA .

  13. De nuevo en Postman, modifique el cuerpo de la solicitud anterior para enviar una notificación silenciosa que especifique action_b en lugar de action_a para el valor de la acción .

    {
        "action": "action_b",
        "silent": true
    }
    
  14. Con la aplicación abierta, haga clic en el botón Enviar en Postman.

  15. Valide que obtiene una respuesta 200 OK en Postman y que la alerta aparece en la aplicación que muestra la acción ActionB recibida en lugar de acción ActionA recibida.

  16. Cierre la aplicación PushDemo y, a continuación, haga clic en el botón Enviar de nuevo en Postman.

  17. Compruebe que obtiene una respuesta 200 OK en Postman y que la notificación silenciosa no aparece en el área de notificación.

Solución de problemas

Sin respuesta del servicio back-end

Al probar localmente, asegúrese de que el servicio back-end se está ejecutando y que usa el puerto correcto.

Si realiza pruebas en la aplicación de API de Azure, compruebe que el servicio se está ejecutando y se ha implementado y se ha iniciado sin errores.

Asegúrese de comprobar que ha especificado correctamente la dirección base en Postman o en la configuración de la aplicación móvil al probar a través del cliente. La dirección base debe ser https://<api_name>.azurewebsites.net/ o https://localhost:5001/ al realizar pruebas localmente.

No recibir notificaciones en Android después de iniciar o detener una sesión de depuración

Asegúrese de volver a registrarse después de iniciar o detener una sesión de depuración. El depurador hará que se genere un nuevo token de Firebase . La instalación del centro de notificaciones también debe actualizarse.

Recepción de un código de estado 401 del servicio back-end

Compruebe que va a establecer el encabezado de solicitud apikey y este valor coincide con el que ha configurado para el servicio back-end.

Si recibe este error al probar localmente, asegúrese de que el valor de clave definido en la configuración del cliente coincide con el valor de configuración de usuario Authentication:ApiKey usado por la API.

Si va a probar con una aplicación de API, asegúrese de que el valor de clave del archivo de configuración del cliente coincide con la configuración de la aplicación Authentication:ApiKey que está usando en la aplicación de API.

Nota:

Si ha creado o cambiado esta configuración después de haber implementado el servicio back-end, debe reiniciar el servicio para que surta efecto.

Si decide no completar la sección Autenticar clientes mediante una clave de API , asegúrese de que no ha aplicado el atributo Authorize a la clase NotificationsController .

Recepción de un código de estado 404 del servicio back-end

Compruebe que el punto de conexión y el método de solicitud HTTP son correctos. Por ejemplo, los puntos de conexión deben ser:

  • [PUT]https://<api_name>.azurewebsites.net/api/notifications/installations
  • [DELETE]https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
  • [POST]https://<api_name>.azurewebsites.net/api/notifications/requests

O bien, al probar localmente:

  • [PUT]https://localhost:5001/api/notifications/installations
  • [DELETE]https://localhost:5001/api/notifications/installations/<installation_id>
  • [POST]https://localhost:5001/api/notifications/requests

Al especificar la dirección base en la aplicación cliente, asegúrese de que termina con ./ La dirección base debe ser https://<api_name>.azurewebsites.net/ o https://localhost:5001/ al realizar pruebas localmente.

No se puede registrar y se muestra un mensaje de error del centro de notificaciones

Compruebe que el dispositivo de prueba tiene conectividad de red. A continuación, determine el código de estado de respuesta Http estableciendo un punto de interrupción para inspeccionar el valor de la propiedad StatusCode en HttpResponse.

Revise las sugerencias de solución de problemas anteriores cuando corresponda en función del código de estado.

Establezca un punto de interrupción en las líneas que devuelven estos códigos de estado específicos para la API correspondiente. A continuación, intente llamar al servicio back-end al depurar localmente.

Valide que el servicio back-end funciona según lo previsto a través de Postman mediante la carga adecuada. Use la carga real creada por el código de cliente para la plataforma en cuestión.

Revise las secciones de configuración específicas de la plataforma para asegurarse de que no se han perdido pasos. Compruebe que se resuelven los valores adecuados para installation id y token las variables de la plataforma adecuada.

No se puede resolver un identificador para el mensaje de error del dispositivo.

Revise las secciones de configuración específicas de la plataforma para asegurarse de que no se han perdido pasos.

Pasos siguientes

Ahora debería tener una aplicación básica de Flutter conectada a un centro de notificaciones a través de un servicio back-end y puede enviar y recibir notificaciones.

Es probable que tenga que adaptar el ejemplo que se usa en este tutorial para adaptarse a su propio escenario. También se recomienda implementar un control de errores más sólido, la lógica de reintento y el registro.

Visual Studio App Center se puede incorporar rápidamente en aplicaciones móviles que proporcionan análisis y diagnósticos para ayudar en la solución de problemas.