Tutorial: Enviar notificações por push para aplicativos Flutter usando os Hubs de Notificação do Azure por meio de um serviço de back-end
Baixar o de exemplo
- do Xamarin.Forms
- flutter
- React Native
Neste tutorial, você usará os Hubs de Notificação do Azure para enviar notificações por push para um aplicativo Flutter direcionado Android e iOS.
Um back-end da API Web
Essas operações são tratadas usando o SDK dos Hubs de Notificação para operações de back-end. Mais detalhes sobre a abordagem geral são fornecidos no Registro da documentação do de back-end do aplicativo.
Este tutorial leva você pelas seguintes etapas:
- configurar os Serviços de Notificação por Push e os Hubs de Notificação do Azure.
- Criar um aplicativo de back-end da API Web do ASP.NET Core.
- Criar um aplicativo Flutter multiplataforma.
- Configurar o projeto nativo do Android para notificações por push.
- Configurar o projeto nativo do iOS para notificações por push.
- Testar a solução.
Pré-requisitos
Para acompanhar, você precisa:
- Uma assinatura do Azure em que você pode criar e gerenciar recursos.
- O kit de ferramentas Flutter (juntamente com seus pré-requisitos).
- do Visual Studio Code com os plug-ins Flutter e Dart instalados.
- CocoaPods instalado para gerenciar dependências de biblioteca.
- A capacidade de executar o aplicativo em Android (dispositivos físicos ou emuladores) ou do iOS (somente dispositivos físicos).
Para Android, você deve ter:
- Um dispositivo físico desbloqueado pelo desenvolvedor ou um do emulador (executando a API 26 e superior com o Google Play Services instalado).
Para iOS, você deve ter:
- Uma conta de desenvolvedor ativa Da Apple.
- Um dispositivo iOS físico registrado em sua conta de desenvolvedor(executando o iOS 13.0 e superior).
- Um de certificado de desenvolvimento
.p12 instalado em seu conjunto de chaves permitindo que você executar um aplicativo em um dispositivo físico .
Nota
O Simulador do iOS não dá suporte a notificações remotas e, portanto, um dispositivo físico é necessário ao explorar esse exemplo no iOS. No entanto, você não precisa executar o aplicativo em android e iOS para concluir este tutorial.
Você pode seguir as etapas neste exemplo de primeiros princípios sem experiência prévia. No entanto, você se beneficiará de ter familiaridade com os seguintes aspectos.
- do Portal do Desenvolvedor da Apple.
- ASP.NET Core.
- do Console do Google Firebase.
- Microsoft Azure e Enviar notificações por push para aplicativos iOS usando os Hubs de Notificação do Azure.
- flutter e dart para desenvolvimento multiplataforma.
- kotlin e swift para desenvolvimento nativo android e iOS.
As etapas fornecidas são específicas para macOS. É possível acompanhar
Configurar os Serviços de Notificação por Push e o Hub de Notificação do Azure
Nesta seção, você configurará do Firebase Cloud Messaging (FCM) e apns (Serviços de Notificação por Push) da Apple. Em seguida, você cria e configura um hub de notificação para trabalhar com esses serviços.
Criar um projeto do Firebase e habilitar o Firebase Cloud Messaging para Android
Entre no console do Firebase . Crie um novo projeto do Firebase inserindo PushDemo como o nome do projeto .
Nota
Um nome exclusivo será gerado para você. Por padrão, isso é composto por uma variante minúscula do nome fornecido mais um número gerado separado por um traço. Você pode alterar isso se quiser, desde que ainda seja globalmente exclusivo.
Depois de criar seu projeto, selecione Adicionar o Firebase ao seu aplicativo Android.
Na página Adicionar o Firebase ao aplicativo Android página, siga as etapas a seguir.
Para o nome do pacote do Android, insira um nome para o pacote. Por exemplo:
com.<organization_identifier>.<package_name>
.Selecione Registrardo aplicativo.
Selecione Baixar google-services.json. Em seguida, salve o arquivo em uma pasta local para uso posterior e selecione Próximo.
Selecione Próximo.
Selecione Continuar no console
Nota
Se o botão Continuar no console não estiver habilitado, devido ao verificar a instalação verificação, escolha Ignorar esta etapa.
No console do Firebase, selecione a engrenagem do projeto. Em seguida, selecione Configurações do Projeto.
Nota
Se você não tiver baixado o arquivo google-services.json, poderá baixá-lo nesta página.
Alterne para a guia do
Cloud Messaging na parte superior. Copie e salve o de chave do servidorpara uso posterior. Use esse valor para configurar o hub de notificação.
Registrar seu aplicativo iOS para notificações por push
Para enviar notificações por push para um aplicativo iOS, registre seu aplicativo na Apple e também registre-se para notificações por push.
Se você ainda não registrou seu aplicativo, navegue até o portal de provisionamento do iOS no Centro de Desenvolvedores da Apple. Entre no portal com sua ID da Apple, navegue até Certificados, Identificadores & Perfise selecione Identificadores. Clique em + para registrar um novo aplicativo.
Na tela Registrar um Novo Identificador, selecione o botão de opção IDs do Aplicativo. Em seguida, selecione Continuar.
Atualize os três valores a seguir para seu novo aplicativo e selecione Continuar:
Descrição: digite um nome descritivo para seu aplicativo.
ID do pacote: insira uma ID do pacote do formulário com.<organization_identifier>.<product_name> conforme mencionado no guia de distribuição de aplicativos . Na captura de tela a seguir, o valor
é usado como um identificador da organização e o valor PushDemo é usado como o nome do produto. página de ID do aplicativo de registro do Portal de Provisionamento do iOS de Notificações por Push: verifique a opção de Notificações por Push na seção recursos do . Essa ação gera sua ID do aplicativo e solicita que você confirme as informações. Selecione Continuare selecione Registrar para confirmar a nova ID do aplicativo.
Depois de selecionar
Registrar , você verá a nova ID do aplicativo como um item de linha na página Certificados, Identificadores & Perfis.
Na página Certificados, Identificadores & Perfis, em Identificadores, localize o item de linha de ID do Aplicativo que você criou. Em seguida, selecione sua linha para exibir a tela Editar a Configuração de ID do Aplicativo.
Criando um certificado para Hubs de Notificação
Um certificado é necessário para permitir que o hub de notificação funcione com do APNS (Serviços de Notificação por Push) da Apple e pode ser fornecido de duas maneiras:
Criar um certificado push p12 que pode ser carregado diretamente no Hub de Notificação (a abordagem original)
Criar um certificado p8 que pode ser usado para de autenticação baseada em token (a abordagem mais recente e recomendada)
A abordagem mais recente tem uma série de benefícios, conforme documentado na autenticação http/2 (baseada em token) para APNS. Menos etapas são necessárias, mas também são obrigatórias para cenários específicos. No entanto, foram fornecidas etapas para ambas as abordagens, pois ambas funcionarão para fins deste tutorial.
OPÇÃO 1: Criando um certificado push p12 que pode ser carregado diretamente no Hub de Notificação
Em seu Mac, execute a ferramenta acesso de conjunto de chaves. Ele pode ser aberto na pasta utilitários
ou na pasta Outros no Launchpad.Selecione de Acesso de Conjunto de Chaves, expanda do Assistente de Certificado e selecione Solicitar um Certificado de uma Autoridade de Certificação.
Nota
Por padrão, o Keychain Access seleciona o primeiro item na lista. Isso pode ser um problema se você estiver na categoria Certificados e Apple Worldwide Developer Relations Certification Authority não for o primeiro item da lista. Verifique se você tem um item não chave ou se a chave Apple Worldwide Developer Relations Certification Authority chave está selecionada, antes de gerar a CSR (Solicitação de Assinatura de Certificado).
Selecione o
Endereço de Email do Usuário , insira o valor de Nome Comum, especifique Salvo em disco e selecioneContinuar . Deixe endereço de email da AC em branco, pois ele não é necessário.Insira um nome para o arquivo CSR (solicitação de assinatura de certificado) em Salvar como, selecione o local em Ondee selecione Salvar.
de certificado
Essa ação salva o arquivo CSR no local selecionado. O local padrão é Desktop. Lembre-se do local escolhido para o arquivo.
De volta à página
Certificados, Identificadores & Perfis no portal de provisionamento doiOS , role para baixo até a opção de Notificações por Pushmarcada e selecione Configurar para criar o certificado.da ID do aplicativo
A janela de certificados TLS/SSL do serviço de notificação por push da Apple
é exibida. Selecione o botão Criar Certificado na seção do Certificado TLS/SSL de Desenvolvimento. A tela Criar um novo Certificado é exibida.
Nota
Este tutorial usa um certificado de desenvolvimento. O mesmo processo é usado ao registrar um certificado de produção. Apenas certifique-se de usar o mesmo tipo de certificado ao enviar notificações.
Selecione Escolherde Arquivo, navegue até o local em que você salvou o arquivo CSR e clique duas vezes no nome do certificado para carregá-lo. Em seguida, selecione Continuar.
Depois que o portal criar o certificado, selecione o botão Baixar. Salve o certificado e lembre-se do local no qual ele foi salvo.
O certificado é baixado e salvo em seu computador na pasta Downloads.
Nota
Por padrão, o certificado de desenvolvimento baixado é nomeado aps_development.cer.
Clique duas vezes no certificado por push baixado aps_development.cer. Essa ação instala o novo certificado no conjunto de chaves, conforme mostrado na imagem a seguir:
Nota
Embora o nome em seu certificado possa ser diferente, o nome será prefixado com o Apple Development iOS Push Services e terá o identificador de pacote apropriado associado a ele.
No Acesso ao Conjunto de Chaves,
Controle p12Clique no novo certificado push que você criou na categoria certificados. Selecione Exportar , nomeie o arquivo, selecione o formatoe selecione Salvar .de formato p12
Você pode optar por proteger o certificado com uma senha, mas uma senha é opcional. Clique em OK se quiser ignorar a criação de senha. Anote o nome do arquivo e o local do certificado p12 exportado. Eles são usados para habilitar a autenticação com APNs.
Nota
O nome e o local do arquivo p12 podem ser diferentes do que é retratado neste tutorial.
OPÇÃO 2: Criando um certificado p8 que pode ser usado para autenticação baseada em token
Anote os seguintes detalhes:
- de prefixo da ID do aplicativo (ID da equipe)
- ID do pacote
De volta em
Certificados, Identificadores & Perfis , clique emChaves. Nota
Se você já tiver uma chave configurada para APNS, poderá reutilize o certificado p8 baixado logo após a criação dele. Nesse caso, você pode ignorar as etapas 3 até 5.
Clique no botão + (ou no botão Criar uma chave) para criar uma nova chave.
Forneça um valor de de nome de chave
adequado e, em seguida, verifique a opção apns (serviço de notificações por push) da Apple e clique em Continuar , seguido porRegistrar na próxima tela.Clique em Baixar e, em seguida, mova o arquivo p8 (prefixado com AuthKey_) para um diretório local seguro e clique em Concluído.
Nota
Mantenha o arquivo p8 em um local seguro (e salve um backup). Depois de baixar a chave, ela não pode ser baixada novamente, pois a cópia do servidor é removida.
Em Chaves, clique na chave que você criou (ou em uma chave existente, se tiver optado por usá-la).
Anote o valor da ID da Chave
. Abra o certificado p8 em um aplicativo adequado de sua escolha, como do Visual Studio Code. Anote o valor da chave (entre -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY-----).
CHAVE PRIVADA -----BEGIN-----
<key_value>
-----END PRIVATE KEY-----Nota
Esse é o de valor do token
que será usado posteriormente para configurardo Hub de Notificação .
Ao final dessas etapas, você deve ter as seguintes informações para uso posteriormente no Configurar seu hub de notificação com informações de APNS:
- ID da equipe (consulte a etapa 1)
- de ID do Pacote (consulte a etapa 1)
- de ID da chave (consulte a etapa 7)
- de valor de token (valor de chave p8 obtido na etapa 8)
Criar um perfil de provisionamento para o aplicativo
Retorne ao do Portal de Provisionamento do iOS, selecione Certificados, Identificadores & Perfis, selecione Perfis no menu à esquerda e selecione + para criar um novo perfil. A tela Registrar um Novo Perfil de Provisionamento é exibida.
Selecione
de Desenvolvimento de Aplicativos do iOS em de Desenvolvimento como o tipo de perfil de provisionamento e selecione Continuar .Em seguida, selecione a ID do aplicativo que você criou na lista suspensa ID do Aplicativo e selecione Continuar.
Na janela Selecionar certificados, selecione o certificado de desenvolvimento que você usa para assinatura de código e selecione Continuar.
Nota
Esse certificado não é o certificado push que você criou na etapa anterior. Esse é o certificado de desenvolvimento. Se não existir, você deverá criá-lo, pois esse é um de pré-requisitos para este tutorial. Os certificados de desenvolvedor podem ser criados nodo Portal do Desenvolvedor da
Apple, por meio de Xcode ou em do Visual Studio. Retorne à página Certificados
, Identificadores & Perfis, selecione Perfis no menu esquerdo e selecionepara criar um novo perfil. A tela Registrar um Novo Perfil de Provisionamento é exibida. Na janela Selecionar certificados, selecione o certificado de desenvolvimento que você criou. Em seguida, selecione Continuar.
Em seguida, selecione os dispositivos a serem usados para teste e selecione Continuar.
Por fim, escolha um nome para o perfil nonome do perfil de provisionamento
e selecione Gerar .Quando o novo perfil de provisionamento for criado, selecione Baixar. Lembre-se do local no qual ele foi salvo.
Navegue até o local do perfil de provisionamento e clique duas vezes nele para instalá-lo no computador de desenvolvimento.
Criar um Hub de Notificação
Nesta seção, você criará um hub de notificação e configurará a autenticação com APNS. Você pode usar um certificado push p12 ou autenticação baseada em token. Se você quiser usar um hub de notificação que já criou, poderá pular para a etapa 5.
Clique em Criar um recurso, pesquise e escolha do Hub de Notificação e clique em Criar.
Atualize os campos a seguir e clique em Criar:
DETALHES BÁSICOS
Assinatura
: Escolher o de Assinatura dede destino na lista suspensa
Grupo de Recursos: Criar um novo grupo de recursos (ou escolher um existente)DETALHES DO NAMESPACE
Namespace do Hub de Notificação
: Insira um nome globalmente exclusivo para o namespace do Hub de NotificaçãoNota
Verifique se a opção Criar nova está selecionada para este campo.
DETALHES DO HUB DE NOTIFICAÇÃO
Hub de Notificação : Insira um nome para o Hub de Notificação
Local: Escolher um local adequado na lista suspensa
tipo de preço: manter a opção de gratuita depadrão Nota
A menos que você tenha atingido o número máximo de hubs na camada gratuita.
Depois que o do Hub de Notificação
tiver sido provisionado, navegue até esse recurso. Navegue até o novodo Hub de Notificação do
. Selecione de Políticas de Acesso na lista (em GERENCIAR).
Anote os valores nome da política
juntamente com seus valores de de cadeia de conexão correspondentes.
Configurar o Hub de Notificação com informações de APNS
Em de Serviços de Notificação, selecione Apple siga as etapas apropriadas com base na abordagem que você escolheu anteriormente na seção Criando um Certificado para Hubs de Notificação.
Nota
Use o de Produção
OPÇÃO 1: Usando um certificado push .p12
Selecione de Certificado.
Selecione o ícone de arquivo.
Selecione o arquivo .p12 exportado anteriormente e selecione Abrir.
Se necessário, especifique a senha correta.
Selecione modo de de área restrita.
Selecione Salvar.
OPÇÃO 2: Usando a autenticação baseada em token
Selecione Token.
Insira os seguintes valores adquiridos anteriormente:
- de ID da Chave
- ID do pacote
- ID da equipe
- de Token
Escolha de área restrita.
Selecione Salvar.
Configurar seu hub de notificação com informações de FCM
- Selecione
do Google (GCM/FCM) na seção Configurações do no menu à esquerda. - Insira a chave do servidor anotada no console do Google Firebase.
- Selecione Salvar na barra de ferramentas.
Criar um aplicativo de back-end da API Web do ASP.NET Core
Nesta seção, você criará a API Web do ASP.NET Core back-end para lidar com de registro de dispositivo e o envio de notificações para o aplicativo móvel Flutter.
Criar um projeto Web
Em do Visual Studio, selecione Arquivo>Nova Solução.
Selecione aplicativo de>do .NET Core>ASP.NET Core>>Próxima.
Na caixa de diálogo Configurar sua nova API Web do ASP.NET Core, selecione do Target Framework de .NET Core 3.1.
Insira
PushDemoApi para o de Nome do Projeto e selecione Criar .Inicie a depuração (Command + Enter) para testar o aplicativo modelo.
Nota
O aplicativo modelo está configurado para usar o
WeatherForecastController como olaunchUrl. Isso é definido em propriedades >launchSettings.json. Se você for solicitado com um certificado de desenvolvimento inválido encontrado mensagem:
Clique em Sim concordar em executar a ferramenta 'dotnet dev-certs https' para corrigir isso. A ferramenta 'dotnet dev-certs https' solicita que você insira uma senha para o certificado e a senha do conjunto de chaves.
Clique em Sim quando solicitado a Instalar e confiar no novo certificadoe, em seguida, insira a senha do conjunto de chaves.
Expanda a pasta controladores de
e exclua WeatherForecastController.cs .Excluir WeatherForecast.cs.
Configurar valores de configuração local usando a ferramenta Secret Manager. A desassociação dos segredos da solução garante que eles não acabem no controle do código-fonte. Abra Terminal vá para o diretório do arquivo de projeto e execute os seguintes comandos:
dotnet user-secrets init dotnet user-secrets set "NotificationHub:Name" <value> dotnet user-secrets set "NotificationHub:ConnectionString" <value>
Substitua os valores de espaço reservado por seu próprio nome de hub de notificação e valores de cadeia de conexão. Você fez uma anotação deles na seção criar um hub de notificação. Caso contrário, você poderá pesquisá-los no do Azure.
NotificationHub:Name:
ConsulteName no resumo doEssentials na parte superior dovisão geral do . NotificationHub:ConnectionString:
Consulte DefaultFullSharedAccessSignature nas políticas de acessoNota
Para cenários de produção, você pode examinar opções como do Azure KeyVault para armazenar com segurança a cadeia de conexão. Para simplificar, os segredos serão adicionados às configurações do aplicativo Serviço de Aplicativo do Azure.
Autenticar clientes usando uma chave de API (opcional)
As chaves de API não são tão seguras quanto tokens, mas serão suficientes para os fins deste tutorial. Uma chave de API pode ser configurada facilmente por meio dodo middleware
Adicione a chave de API aos valores de configuração local.
dotnet user-secrets set "Authentication:ApiKey" <value>
Nota
Você deve substituir o valor do espaço reservado pelo seu próprio e anotar isso.
Controle Clique no projetoPushDemoApi , escolhaNovo de Pasta no menu Adicionare clique em Adicionar usando de Autenticaçãocomo onome da pasta . Controle Clique na pasta de Autenticaçãoe escolha Novo Arquivo... no menu Adicionar. Selecione
de Classe Vazia do Geral, insira ApiKeyAuthOptions.cs para ode Nome doe clique em Nova adicionando a implementação a seguir.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; } } }
Adicione outro
de Classe Vazia à pasta de Autenticação chamada ApiKeyAuthHandler.cs e adicione a implementação a seguir.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)); } } }
Adicione outro
de Classe Vazia à pasta de Autenticação chamada ApiKeyAuthenticationBuilderExtensions.cs e adicione a implementação a seguir.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
Esse método de extensão simplifica o código de configuração do middleware em Startup.cs tornando-o mais legível e geralmente mais fácil de seguir.
Em Startup.cs, atualize o método ConfigureServices para configurar a autenticação da Chave de API abaixo da chamada para os serviços de . 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); }
Ainda em
Startup.cs , atualize o métodoConfigure para chamar os métodos de extensãoUseAuthentication e UseAuthorization nodo aplicativo IApplicationBuilder. Verifique se esses métodos são chamados após userouting e antes de aplicativo. 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
Chamar UseAuthentication registra o middleware que usa os esquemas de autenticação registrados anteriormente (de ConfigureServices). Isso deve ser chamado antes de qualquer middleware que dependa da autenticação dos usuários.
Adicionar dependências e configurar serviços
ASP.NET Core dá suporte à DI (injeção de dependência) padrão de design de software, que é uma técnica para alcançar de inversão de controle (IoC) entre classes e suas dependências.
O uso do hub de notificação e do SDK dos Hubs de Notificação para operações de back-end é encapsulado em um serviço. O serviço é registrado e disponibilizado por meio de uma abstração adequada.
Controle Clique em na pasta dependências doe escolha Gerenciar Pacotes NuGet... .Pesquise Microsoft.Azure.NotificationHubs e verifique se ele está marcado.
Clique em Adicionar Pacotese, em seguida, clique em Aceitar quando solicitado a aceitar os termos de licença.
Controle Clique no projetoPushDemoApi, escolha novo de pasta no menu Adicionare clique em Adicionar usando modelos decomo onome da pasta . Controle Clique na pasta modelos dee escolha Novo Arquivo... no menu Adicionar. Selecione
de Classe Vazia Geral, insira PushTemplates.cs para ode Nome doe clique em Nova adicionando a implementação a seguir.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
Essa classe contém as cargas de notificação tokenizadas para as notificações genéricas e silenciosas exigidas por esse cenário. Os conteúdos são definidos fora do Instalação para permitir a experimentação sem precisar atualizar as instalações existentes por meio do serviço. O tratamento de alterações nas instalações dessa maneira está fora do escopo deste tutorial. Para produção, considere modelos personalizados.
Adicione outra
de Classe Vazia à pasta modelos chamada DeviceInstallation.cs e adicione a implementação a seguir.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>(); } }
Adicione outra
de Classe Vazia à pasta modelos de chamada NotificationRequest.cs e adicione a implementação a seguir.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; } } }
Adicione outro
de Classe Vazia à pasta modelos chamada NotificationHubOptions.cs e adicione a implementação a seguir.using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class NotificationHubOptions { [Required] public string Name { get; set; } [Required] public string ConnectionString { get; set; } } }
Adicione uma nova pasta ao projeto de
PushDemoApi chamado Services .Adicione um
de Interface Vazia à pasta Services chamada INotificationService.cs e adicione a implementação a seguir.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); } }
Adicione um
de Classe Vazia à pasta serviços chamado NotificationHubsService.cs e adicione o seguinte código para implementar a interface 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
A expressão de marca fornecida para SendTemplateNotificationAsync é limitada a 20 marcas. Ela é limitada a 6 para a maioria dos operadores, mas a expressão contém apenas ORs (||) nesse caso. Se houver mais de 20 marcas na solicitação, elas deverão ser divididas em várias solicitações. Consulte a documentação expressões de
roteamento e marca para obter mais detalhes. Em
Startup.cs , atualize o métodoConfigureServices para adicionar oNotificationHubsService como uma implementação 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(); }
Criar a API de notificações
Controle Clique em na pasta controladores dee escolha Novo Arquivo... no menu Adicionar. Selecione
ASP.NETde Classe do Controlador de API Web do Core , insira NotificationsController para o nome e clique em Nova .Adicione os namespaces a seguir à parte superior do arquivo.
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;
Atualize o controlador modelo para que ele deriva de ControllerBase e seja decorado com o atributo ApiController.
[ApiController] [Route("api/[controller]")] public class NotificationsController : ControllerBase { // Templated methods here }
Se você optar por concluir os clientes do
Authenticate usando uma seção de chave de API , decore oNotificationsController com o atributo de Autorização também. [Authorize]
Atualize o construtor para aceitar a instância registrada de INotificationService como um argumento e atribuí-lo a um membro somente leitura.
readonly INotificationService _notificationService; public NotificationsController(INotificationService notificationService) { _notificationService = notificationService; }
Em
launchSettings.json (dentro da pasta propriedades do), altere o launchUrl de para de api/notificações para corresponder à URL especificada no atributo RegistrationsController Route .Inicie a depuração (
Command Enter ) para validar se o aplicativo está trabalhando com o novo notificationsControllere retorna um status de 401 não autorizado .Nota
O Visual Studio pode não iniciar automaticamente o aplicativo no navegador. Você usará postman para testar a API deste ponto em diante.
Em uma nova guia do Postman
, defina a solicitação para get . Insira o endereço abaixo substituindo o espaço reservado applicationUrl pelo applicationUrl dehttps encontrado em launchSettings.json propriedades do. <applicationUrl>/api/notifications
Nota
O applicationUrl
deve ser ' ' para o perfil padrão. Se você estiver usando do IIS (padrão no do Visual Studio 2019 no Windows), deverá usar o applicationUrl especificado no item iisSettings. Você receberá uma resposta 404 se o endereço estiver incorreto. Se você optou por concluir os clientes
Authenticate usando uma seção de chave de API , configure os cabeçalhos de solicitação para incluir seu valor de apikey. Chave Valor apikey <your_api_key> Clique no botão Enviar.
Nota
Você deve receber um status de
200 OK com algum conteúdo de JSON. Se você receber um aviso de verificação de certificado SSL
, poderá alternar a verificação de certificado SSL de solicitação configuração noconfigurações dopostman . Substitua os métodos de classe modelo em NotificationsController.cs pelo código a seguir.
[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(); }
Criar o aplicativo de API
Agora você cria um aplicativo de API
Entre no portal do do Azure.
Clique em Criar um recurso, pesquise e escolha de Aplicativo de API e clique em Criar.
Atualize os campos a seguir e clique em Criar.
Nome do aplicativo:
Insira um nome global exclusivo para o aplicativo de APIAssinatura :
Escolha o mesmo destino Assinatura você criou o hub de notificação.grupo de recursos :
Escolha o mesmo Grupo de Recursos você criou o hub de notificação.Plano/Local do Serviço de Aplicativo:
Criar um novo plano do Serviço de AplicativoNota
Altere da opção padrão para um plano que inclua suporte ao SSL. Caso contrário, você precisará tomar as etapas apropriadas ao trabalhar com o aplicativo móvel para impedir que solicitações http sejam bloqueadas.
Application Insights:
Mantenha a opção sugerida (um novo recurso será criado usando esse nome) ou escolha um recurso existente.Depois que o aplicativo de API tiver sido provisionado, navegue até esse recurso.
Anote a propriedade de URL
no resumo do Essentials na parte superior dovisão geral do . Essa URL é o de ponto de extremidade de back-end que será usado posteriormente neste tutorial. Nota
A URL usa o nome do aplicativo de API especificado anteriormente, com o formato
https://<app_name>.azurewebsites.net
.Selecione
de Configuração na lista (emde Configurações de ). Para cada uma das configurações abaixo, clique em
Nova configuração de aplicativo para inserir o nomee umde valor de e, em seguida, clique em OK .Nome Valor Authentication:ApiKey
<api_key_value> NotificationHub:Name
<hub_name_value> NotificationHub:ConnectionString
<hub_connection_string_value> Nota
Essas são as mesmas configurações que você definiu anteriormente nas configurações do usuário. Você deve ser capaz de copiá-los. A configuração Authentication:ApiKey será necessária somente se você optar por concluir os clientes Authenticate usando uma seção de chave de API. Para cenários de produção, você pode examinar opções como do Azure KeyVault. Elas foram adicionadas como configurações de aplicativo para simplificar nesse caso.
Depois que todas as configurações do aplicativo tiverem sido adicionadas, clique em Salvare, em seguida, Continuar.
Publicar o serviço de back-end
Em seguida, você implanta o aplicativo no Aplicativo de API para torná-lo acessível de todos os dispositivos.
Nota
As etapas a seguir são específicas para Visual Studio para Mac. Se você estiver acompanhando do Visual Studio 2019 no Windows, o fluxo de publicação será diferente. Consulte Publicar no Serviço de Aplicativo do Azure no Windows.
Altere sua configuração de de depuração para de versão, caso ainda não tenha feito isso.
Controle Clique o projetoPushDemoApi e escolha Publicar no Azure... no menu Publicar do. Siga o fluxo de autenticação se solicitado a fazê-lo. Use a conta usada no anterior para criar a seção do Aplicativo de API.
Selecione o Aplicativo de API do Serviço de Aplicativo do Azure você criou anteriormente na lista como destino de publicação e clique em Publicar.
Depois de concluir o assistente, ele publicará o aplicativo no Azure e abrirá o aplicativo. Anote a URL se você ainda não fez isso. Essa URL é o de ponto de extremidade de back-end usado posteriormente neste tutorial.
Validando a API publicada
Em Postman abrir uma nova guia, defina a solicitação para PUT e insira o endereço abaixo. Substitua o espaço reservado pelo endereço base do qual você anotou no anterior, publique o serviço de back-end seção.
https://<app_name>.azurewebsites.net/api/notifications/installations
Nota
O endereço base deve estar no formato
https://<app_name>.azurewebsites.net/
Se você optou por concluir os clientes
Authenticate usando uma seção de chave de API , configure os cabeçalhos de solicitação para incluir seu valor de apikey. Chave Valor apikey <your_api_key> Escolha a opção
bruta para ocorpo do , escolha JSON na lista de opções de formato e, em seguida, inclua algum espaço reservado conteúdo de JSON:{}
Clique em Enviar.
Nota
Você deve receber um status 422 UnprocessableEntity do serviço.
Execute as etapas 1 a 4 novamente, mas desta vez especificando o ponto de extremidade de solicitações para validar se você recebe uma resposta 400 solicitações inválidas.
https://<app_name>.azurewebsites.net/api/notifications/requests
Nota
Ainda não é possível testar a API usando dados de solicitação válidos, pois isso exigirá informações específicas da plataforma do aplicativo móvel cliente.
Criar um aplicativo Flutter multiplataforma
Nesta seção, você criará um aplicativo móvel Flutter implementando notificações por push de maneira multiplataforma.
Ele permite que você registre e desregistre de um hub de notificação por meio do serviço de back-end que você criou.
Um alerta é exibido quando uma ação é especificada e o aplicativo está em primeiro plano. Caso contrário, as notificações aparecerão no centro de notificação.
Nota
Normalmente, você executaria as ações de registro (e desregistração) durante o ponto apropriado no ciclo de vida do aplicativo (ou como parte de sua experiência de primeira execução, talvez) sem entradas explícitas de registro/desregistro de usuário. No entanto, este exemplo exigirá uma entrada explícita do usuário para permitir que essa funcionalidade seja explorada e testada com mais facilidade.
Criar a solução Flutter
Abra uma nova instância dodo Visual Studio Code
. Abra o de Paleta de Comandos do
( comando shift P ).Selecione o comando Flutter: New Project e pressione Enter.
Insira
push_demo para o de Nome do Projeto doe selecione umde local do projeto . Quando solicitado a fazer isso, escolha Obter Pacotes.
Control Clique em na pasta do kotlin(em aplicativo principal), em seguida, escolha Revelação no Finder . Em seguida, renomeie as pastas filho (na pasta do kotlin) para , e respectivamente. Nota
Ao usar o modelo do Visual Studio Code
, essas pastas são padrão para com ,exemplo , . Supondo que mobcat seja usado para a organização , a estrutura de pastas deve aparecer como:project_name - kotlin
- com
- mobcat
- pushdemo
- mobcat
- com
- kotlin
De volta ao
do Visual Studio Code, atualize o valor applicationId em aplicativo android build.gradle para. Nota
Você deve usar seu próprio nome de organização para o espaço reservado <your_organization>. Por exemplo, usar mobcat como a organização resultará em um nome de pacote valor de com.mobcat.pushdemo.
Atualize o atributo do pacote
nos arquivos AndroidManifest.xml , emde depuração do src , principal do src e de perfil de src, respectivamente. Verifique se os valores correspondem à applicationId que você usou na etapa anterior. <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.<your_organization>.pushdemo>"> ... </manifest>
Atualize o atributo
android:label
no arquivo AndroidManifest.xml em src>principal para PushDemo. Em seguida, adicione o atributoandroid:allowBackup
, diretamente emandroid:label
, definindo seu valor como falso.<application android:name="io.flutter.app.FlutterApplication" android:label="PushDemo" android:allowBackup="false" android:icon="@mipmap/ic_launcher"> ... </application>
Abra o arquivo build.gradle de
no nível do aplicativo do aplicativo android build.gradle , em seguida, atualize ocompileSdkVersion (da seçãoandroid ) para usar a API29 . Em seguida, atualize os valoresminSdkVersion etargetSdkVersion (da seção defaultConfig ), para26 e29 respectivamente.Nota
Somente os dispositivos que executam nível de API 26 e acima têm suporte para fins deste tutorial, no entanto, você pode estendê-lo para dar suporte a dispositivos que executam versões mais antigas.
Controle + Clique na pasta ios e escolha Abrir noXcode.
Em
Xcode, clique em Runner (o xcodeprojna parte superior, não na pasta). Em seguida, selecione o destino do Runner e selecione a guia Geral do . Com a configuração de build Todos os selecionada, atualize o do Identificador de Pacotepara . Nota
Você deve usar seu próprio nome de organização para o espaço reservado <your_organization>. Por exemplo, usar mobcat como a organização resultará em um valor identificador de pacote de com.mobcat.PushDemo.
Clique Info.plist e atualize o nome do pacote valor para do PushDemo
Feche Xcode e retorne ao do Visual Studio Code.
De volta ao
do Visual Studio Code, abra pubspec.yaml, adicione os pacotes http http e flutter_secure_storage Dardos como dependências. Em seguida, salve o arquivo e clique em Obter Pacotes quando solicitado a fazê-lo.dependencies: flutter: sdk: flutter http: ^0.12.1 flutter_secure_storage: ^3.3.3
No Terminal, altere o diretório para a pasta ios (para seu projeto Flutter). Em seguida, execute o comando de instalação do pod para instalar novos pods (exigidos pelo pacote flutter_secure_storage).
Controle + Clique na pasta lib e escolha Novo Arquivo no menu usando main_page.dart como o nome do arquivo. Em seguida, adicione o código a seguir.
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>[], ) ) ); } }
Em main.dart, substitua o código modelo pelo seguinte.
import 'package:flutter/material.dart'; import 'package:push_demo/main_page.dart'; final navigatorKey = GlobalKey<NavigatorState>(); void main() => runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey));
Em Terminal, compile e execute o aplicativo em cada plataforma de destino para testar as execuções do aplicativo modelo em seus dispositivos. Verifique se os dispositivos com suporte estão conectados.
flutter run
Implementar os componentes multiplataforma
Controle + Clique na pasta lib e, em seguida, escolha Nova Pasta no menu usando modelos como o nome da pasta .
Controle Clique na pasta de modelose escolha Novo Arquivo no menu usandodevice_installation.dart como o nome do arquivo. Em seguida, adicione o código a seguir.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, }; }
Adicione um novo arquivo à pasta modelos chamada push_demo_action.dart definindo a enumeração de ações com suporte neste exemplo.
enum PushDemoAction { actionA, actionB, }
Adicione uma nova pasta ao projeto chamada serviços, em seguida, adicione um novo arquivo à pasta chamada device_installation_service.dart com a implementação a seguir.
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
Você deve usar seu próprio nome de organização para o espaço reservado <your_organization>. Por exemplo, usar
mobcat como a organização resultará em um nome MethodChannel de com.mobcat.pushdemo/deviceinstallation .Essa classe encapsula o trabalho com a plataforma nativa subjacente para adquirir os detalhes de instalação do dispositivo necessário. Uma MethodChannel
facilita a comunicação assíncrona bidirecional com as plataformas nativas subjacentes. O equivalente específico da plataforma para esse canal será criado nas etapas posteriores. Adicione outro arquivo à pasta chamada notification_action_service.dart com a implementação a seguir.
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
Isso é usado como um mecanismo simples para centralizar o tratamento de ações de notificação para que possam ser tratadas de maneira multiplataforma usando uma enumeração fortemente tipada. O serviço permite que a plataforma nativa subjacente dispare uma ação, quando uma é especificada no conteúdo da notificação. Ele também permite que o código comum verifique retrospectivamente se uma ação foi especificada durante a inicialização do aplicativo quando o Flutter estiver pronto para processá-lo. Por exemplo, quando o aplicativo é iniciado tocando em uma notificação do centro de notificação.
Adicione um novo arquivo à pasta services chamada notification_registration_service.dart com a implementação a seguir.
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
Essa classe encapsula o uso do
DeviceInstallationService e as solicitações para o serviço de back-end para executar o registro, o desregistro e as ações de registro de atualização necessários. O argumento apiKey só será necessário se você optar por concluir os clientes Authenticate usando uma seção de Chave de API. Adicione um novo arquivo à pasta lib chamada config.dart com a implementação a seguir.
class Config { static String apiKey = "API_KEY"; static String backendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT"; }
Nota
Isso é usado como uma maneira simples de definir segredos do aplicativo. Substitua os valores de espaço reservado pelos seus próprios. Você deve ter anotado isso quando criou o serviço de back-end. O de URL do aplicativo de API
deve ser . O membro apiKey só será necessário se você optar por concluir os clientes Authenticate usando uma seção de chave de API. Adicione isso ao arquivo gitignore para evitar a confirmação desses segredos no controle do código-fonte.
Implementar a interface do usuário multiplataforma
Em
main_page.dart , substitua a função de buildpela seguinte. @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, ), ], ), ), ); }
Adicione as importações necessárias à parte superior do arquivo main_page.dart.
import 'package:push_demo/services/notification_registration_service.dart'; import 'config.dart';
Adicione um campo à classe
_MainPageState para armazenar uma referência aoNotificationRegistrationService. final notificationRegistrationService = NotificationRegistrationService(Config.backendServiceEndpoint, Config.apiKey);
Na classe _MainPageState, implemente os manipuladores de eventos para os botões Registrar e Desregistroeventos de Compactados. Chame os métodos de Registro
correspondentes e, em seguida, mostre um alerta para indicar o 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(); }, ), ], ); }, ); }
Agora, em main.dart, verifique se as seguintes importações estão presentes na parte superior do arquivo.
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';
Declare uma variável para armazenar referência a uma instância de NotificationActionService e inicialize-a.
final notificationActionService = NotificationActionService();
Adicione funções para lidar com a exibição de um alerta quando uma ação é disparada.
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(); }, ), ], ); }, ); }
Atualize a função principal para observar o fluxo NotificationActionServiceactionTriggered e verificar se há ações capturadas durante a inicialização do aplicativo.
void main() async { runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey,)); notificationActionService.actionTriggered.listen((event) { notificationActionTriggered(event as PushDemoAction); }); await notificationActionService.checkLaunchAction(); }
Nota
Isso é simplesmente para demonstrar o recebimento e a propagação de ações de notificação por push. Normalmente, elas seriam tratadas silenciosamente, por exemplo, navegando para uma exibição específica ou atualizando alguns dados em vez de exibir um alerta nesse caso.
Configurar o projeto nativo do Android para notificações por push
Adicionar o arquivo JSON do Google Services
Controle + Clique em na pasta android e escolha Abrir no Android Studio. Em seguida, alterne para o modo de exibição do Projeto
(se ainda não estiver). Localize o arquivo
google-services.json que você baixou anteriormente ao configurar o projetoPushDemo nodo Console do Firebase . Em seguida, arraste-o para o diretório raiz do módulo aplicativo (do aplicativo android android android).
Definir as configurações e permissões de build
Alterne o modo de exibição do Projeto
para Android .Abra AndroidManifest.xml, adicione as permissões internet e READ_PHONE_STATE depois que o aplicativo elemento antes da marca de de fechamento.
<manifest> <application>...</application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> </manifest>
Adicionar os SDKs do Firebase
No
do Android Studio, abra o arquivo build.gradle de no nível do projeto ( build.gradle ). e verifique se você tem o classpath 'com.google.gms:google-services' nas dependênciasGradle Scripts (Project: android) buildscript
> nó.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
Certifique-se de referenciar a versão mais recente de acordo com as instruções fornecidas no do Console do Firebase
quando você criou o Android Project .No arquivo build.gradle no nível do aplicativo (scripts Gradle>build.gradle (Módulo: aplicativo)), aplique o plug-in Gradle do Google Services. Aplique o plug-in logo acima do nó android.
// ... // Add the following line: apply plugin: 'com.google.gms.google-services' // Google Services plugin android { // ... }
No mesmo arquivo, nas dependências nó, adicione a dependência para a biblioteca do Cloud Messaging Android.
dependencies { // ... implementation 'com.google.firebase:firebase-messaging:20.2.0' }
Nota
Verifique se você faz referência à versão mais recente de acordo com a documentação do cliente Android do Cloud Messaging.
Salve as alterações e clique no botão Sincronizar Agora (no prompt da barra de ferramentas) ou Sincronizar Projeto com Arquivos Gradle.
Manipular notificações por push para Android
No Android Studio, Control + Clique em no com.<your_organization>pasta do pacote .pushdemo (app>src>principal>kotlin), escolha Pacote no menu Novo. Insira serviços como o nome e pressione Retornar.
Controle Clique em na pasta de serviços, escolha arquivo kotlin/ de classe no menuNovo . Insira DeviceInstallationService como o nome e pressione Retornar.Implemente o
DeviceInstallationService usando o código a seguir. 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
Essa classe implementa o equivalente específico da plataforma para o canal
com.<your_organization>.pushdemo/deviceinstallation
. Isso foi definido na parte Flutter do aplicativo em DeviceInstallationService.dart. Nesse caso, as chamadas são feitas do código comum para o host nativo. Substitua <your_organization> por sua própria organização onde quer que isso seja usado.Essa classe fornece uma ID exclusiva (usando Secure.AndroidId) como parte do conteúdo de registro do hub de notificação.
Adicione outro
de Arquivo/Classe kotlin à pasta de serviços chamada NotificationRegistrationService e adicione o código a seguir.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
Essa classe implementa o equivalente específico da plataforma para o canal
com.<your_organization>.pushdemo/notificationregistration
. Isso foi definido na parte Flutter do aplicativo em NotificationRegistrationService.dart. Nesse caso, as chamadas são feitas do host nativo para o código comum. Novamente, tome cuidado para substituir <your_organization> por sua própria organização onde quer que isso seja usado.Adicione outro de Arquivo/Classe Kotlin à pasta services chamada NotificationActionServicee adicione o código a seguir.
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
Essa classe implementa o equivalente específico da plataforma para o canal
com.<your_organization>.pushdemo/notificationaction
. Isso foi definido na parte Flutter do aplicativo em notificationActionService.dart. As chamadas podem ser feitas em ambas as direções nesse caso. Substitua <your_organization> por sua própria organização onde quer que isso seja usado.Adicione um novo de Arquivo/Classe Kotlin ao com.<your_organization>pacote de .pushdemo chamado PushNotificationsFirebaseMessagingServicee implemente usando o código a seguir.
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
Essa classe é responsável por lidar com notificações quando o aplicativo está em execução em primeiro plano. Ele chamará condicionalmente o triggerAction no NotificationActionService se uma ação for incluída no conteúdo de notificação recebido em onMessageReceived. Isso também chamará
refreshRegistration no NotificationRegistrationService quando o token do Firebase for regenerado substituindo a função onNewToken .Mais uma vez, tome cuidado para substituir <your_organization> por sua própria organização onde quer que seja usado.
No
AndroidManifest.xml (aplicativo src principal ), adicione oPushNotificationsFirebaseMessagingService à parte inferior do elemento do aplicativo com o filtro de intenção . <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>
De volta ao DeviceInstallationService, verifique se as seguintes importações estão presentes na parte superior do arquivo.
package com.<your_organization>.pushdemo import com.<your_organization>.pushdemo.services.PushNotificationsFirebaseMessagingService
Nota
Substitua <your_organization> pelo seu próprio valor de organização.
Atualize o
Placeholder_Get_Value_From_FirebaseMessagingService_Implementation de texto do espaço reservado para obter o valor do token doPushNotificationFirebaseMessagingService. 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 }
Em MainActivity, verifique se as seguintes importações estão presentes na parte superior do arquivo.
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
Substitua <your_organization> pelo seu próprio valor de organização.
Adicione uma variável para armazenar uma referência ao DeviceInstallationService.
private lateinit var deviceInstallationService: DeviceInstallationService
Adicione uma função chamada
processNotificationActions para verificar se um de intenção detem um valor extra chamado ação . Dispare condicionalmente essa ação ou armazene-a para uso posteriormente se a ação estiver sendo processada durante a inicialização do aplicativo.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) } } } }
Substitua a função onNewIntent para chamar processNotificationActions.
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) processNotificationActions(intent) }
Nota
Como o do
LaunchMode para MainActivity está definido como singletop, um de intenção será enviado para a instância de de Atividade existente por meio do onNew Função inserção em vez da funçãoonCreate e, portanto, você deve lidar com uma de intenção dede entrada em funções onCreate eonNewIntent .Substitua a função
onCreate , defina odeviceInstallationService para uma nova instância doDeviceInstallationService . override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) } }
Defina as propriedades notificationActionService e notificationRegistrationService em PushNotificationFirebaseMessagingServices.
flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) PushNotificationsFirebaseMessagingService.notificationActionService = NotificationActionService(it) PushNotificationsFirebaseMessagingService.notificationRegistrationService = NotificationRegistrationService(it) }
Na mesma função, chame condicionalmente FirebaseInstanceId.getInstance().instanceId. Implemente o
OnCompleteListener para definir o valor de tokenresultante em PushNotificationFirebaseMessagingService antes de chamar refreshRegistration .if(deviceInstallationService?.playServicesAvailable) { FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) return@OnCompleteListener PushNotificationsFirebaseMessagingService.token = task.result?.token PushNotificationsFirebaseMessagingService.notificationRegistrationService?.refreshRegistration() }) }
Ainda no onCreate, chame processNotificationActions no final da função. Use
verdadeiro para o argumento launchActionpara indicar que essa ação está sendo processada durante a inicialização do aplicativo. processNotificationActions(this.intent, true)
Nota
Você deve registrar novamente o aplicativo sempre que o executar e interrompê-lo de uma sessão de depuração para continuar recebendo notificações por push.
Configurar o projeto iOS nativo para notificações por push
Configurar o destino do executor e o Info.plist
Em
do Visual Studio Code, Control Clique em na pasta ios doe escolha Abrir no Xcode .Em
Xcode, clique em Runner (o xcodeprojna parte superior, não na pasta) e, em seguida, selecione o destino Runner e, em seguida, recursos de & de assinatura. Com a configuração de build Todos os selecionada, escolha sua conta de Desenvolvedor para oda Equipe de. Verifique se a opção "Gerenciar assinatura automaticamente" está marcada e se o Certificado de Autenticação e o Perfil de Provisionamento são selecionados automaticamente. Nota
Se você não vir o novo valor de Perfil de Provisionamento, tente atualizar os perfis para a Identidade de Assinatura selecionando >Preferências> de Conta do Xcode e selecione o botão Baixar Perfis Manuais para baixar os perfis.
Clique em +de funcionalidade e pesquise de Notificações por Push.
clique duas vezes no de Notificações por Pushpara adicionar essa funcionalidade. Abra Info.plist e defina versão mínima do sistema para 13.0.
Nota
Somente os dispositivos que executam iOS 13.0 e acima têm suporte para fins deste tutorial, no entanto, você pode estendê-lo para dar suporte a dispositivos que executam versões mais antigas.
Abra
Runner.rights e verifique se a configuração de de Ambiente do APSestá definida para de desenvolvimento.
Manipular notificações por push para iOS
Controle Clique na pasta doRunner (no projeto Executor) e escolha Novo Grupo usando de Serviçoscomo o nome. Controle Clique em na pasta dos Serviçose escolha Novo Arquivo... . Em seguida, escolhade Arquivo Swift e clique em Próximo . Especifique DeviceInstallationService para o nome e clique em Criar.Implemente DeviceInstallationService.swift usando o código a seguir.
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
Essa classe implementa o equivalente específico da plataforma para o canal
com.<your_organization>.pushdemo/deviceinstallation
. Isso foi definido na parte Flutter do aplicativo em DeviceInstallationService.dart. Nesse caso, as chamadas são feitas do código comum para o host nativo. Substitua <your_organization> por sua própria organização onde quer que isso seja usado.Essa classe fornece uma ID exclusiva (usando o UIDevice.identifierForVendor valor) como parte do conteúdo de registro do hub de notificação.
Adicione outra
de Arquivo Swift à pasta serviços chamada NotificationRegistrationService e adicione o código a seguir.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
Essa classe implementa o equivalente específico da plataforma para o canal
com.<your_organization>.pushdemo/notificationregistration
. Isso foi definido na parte Flutter do aplicativo em NotificationRegistrationService.dart. Nesse caso, as chamadas são feitas do host nativo para o código comum. Novamente, tome cuidado para substituir <your_organization> por sua própria organização onde quer que isso seja usado.Adicione outro
de Arquivo Swift à pasta serviços chamado NotificationActionService e adicione o código a seguir. 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
Essa classe implementa o equivalente específico da plataforma para o canal
com.<your_organization>.pushdemo/notificationaction
. Isso foi definido na parte Flutter do aplicativo em notificationActionService.dart. As chamadas podem ser feitas em ambas as direções nesse caso. Substitua <your_organization> por sua própria organização onde quer que isso seja usado.Em AppDelegate.swift, adicione variáveis para armazenar uma referência aos serviços criados anteriormente.
var deviceInstallationService : DeviceInstallationService? var notificationRegistrationService : NotificationRegistrationService? var notificationActionService : NotificationActionService?
Adicione uma função chamada processNotificationActions para processar os dados de notificação. Dispare condicionalmente essa ação ou armazene-a para uso posteriormente se a ação estiver sendo processada durante a inicialização do aplicativo.
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) } } }
Substitua a função
didRegisterForRemoteNotificationsWithDeviceToken definindo o valor do tokenpara oDeviceInstallationService . Em seguida, chame refreshRegistration no NotificationRegistrationService. override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { deviceInstallationService?.token = deviceToken notificationRegistrationService?.refreshRegistration() }
Substitua a função didReceiveRemoteNotification passando o argumento userInfo para a função processNotificationActions.
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { processNotificationActions(userInfo: userInfo) }
Substitua a função didFailToRegisterForRemoteNotificationsWithError para registrar o erro em log.
override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print(error); }
Nota
Este é um espaço reservado. Você desejará implementar o registro em log e o tratamento de erros adequados para cenários de produção.
Em
didFinishLaunchingWithOptions, crie uma instância das variáveis deviceInstallationService, notificationRegistrationService enotificationActionService .let controller : FlutterViewController = window?.rootViewController as! FlutterViewController deviceInstallationService = DeviceInstallationService(withBinaryMessenger: controller.binaryMessenger) notificationRegistrationService = NotificationRegistrationService(withBinaryMessenger: controller.binaryMessenger) notificationActionService = NotificationActionService(withBinaryMessenger: controller.binaryMessenger)
Na mesma função, solicite condicionalmente a autorização e registre-se para notificações 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() } } } }
Se o
launchOptions contiver a chave remoteNotification, chame processNotificationActions no final da função didFinishLaunchingWithOptions do. Passe o objeto userInfo resultante e use verdadeiro para o argumentolaunchAction . Um valor verdadeiro indica que a ação está sendo processada durante a inicialização do aplicativo.if let userInfo = launchOptions?[.remoteNotification] as? [AnyHashable : Any] { processNotificationActions(userInfo: userInfo, launchAction: true) }
Testar a solução
Agora você pode testar o envio de notificações por meio do serviço de back-end.
Enviar uma notificação de teste
Abra uma nova guia no Postman.
Defina a solicitação para poste insira o seguinte endereço:
https://<app_name>.azurewebsites.net/api/notifications/requests
Se você optou por concluir os clientes
Authenticate usando uma seção de chave de API , configure os cabeçalhos de solicitação para incluir seu valor de apikey. Chave Valor apikey <your_api_key> Escolha a opção
bruta para ocorpo do , escolha JSON na lista de opções de formato e, em seguida, inclua algum espaço reservado conteúdo de JSON:{ "text": "Message from Postman!", "action": "action_a" }
Selecione o botão
Código , que está sob o botão salvarno canto superior direito da janela. A solicitação deve ser semelhante ao exemplo a seguir quando exibida para HTML (dependendo se você incluiu um cabeçalho 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" }
Execute o aplicativo PushDemo em uma ou ambas as plataformas de destino (Android e iOS).
Nota
Se você estiver testando em Android verifique se não está em execução no de depuraçãoou se o aplicativo foi implantado executando o aplicativo, então force fechar o aplicativo e iniciá-lo novamente do inicializador.
No aplicativo
PushDemo, toque no botão Registrar .De volta ao do Postman, feche a janela Gerar Snippets de Código (se você ainda não fez isso) e clique no botão Enviar.
Valide se você obtém uma resposta 200 OK no postman e o alerta aparece no aplicativo mostrando ação ActionA recebida.
Feche o aplicativo
PushDemo e clique no botão Enviar novamente no .Postman Valide se você obtém uma resposta 200 OK no Postman novamente. Valide se uma notificação aparece na área de notificação do aplicativo PushDemo com a mensagem correta.
Toque na notificação para confirmar se ele abre o aplicativo e exibe a ação ActionA recebida alerta.
De volta ao
, modifique o corpo da solicitação anterior para enviar uma notificação silenciosa especificandopostman action_b em vez deaction_a para o valor da ação. { "action": "action_b", "silent": true }
Com o aplicativo ainda aberto, clique no botão Enviar em Postman.
Valide se você obtém uma resposta 200 OK em postman e que o alerta aparece no aplicativo mostrando ação do ActionB recebida em vez de ação ActionA recebida.
Feche o aplicativo
PushDemo e clique no botão Enviar novamente no .Postman Valide se você obtém uma resposta 200 OK no postman e que a notificação silenciosa não aparece na área de notificação.
Solucionando problemas
Nenhuma resposta do serviço de back-end
Ao testar localmente, verifique se o serviço de back-end está em execução e se está usando a porta correta.
Se o teste no aplicativo de API do Azure, verifique se o serviço está em execução e foi implantado e foi iniciado sem erros.
Verifique se você especificou o endereço base corretamente no postman ou na configuração do aplicativo móvel ao testar por meio do cliente. O endereço base deve ser https://<api_name>.azurewebsites.net/
ou https://localhost:5001/
ao testar localmente.
Não receber notificações no Android depois de iniciar ou interromper uma sessão de depuração
Verifique se você se registrou novamente depois de iniciar ou interromper uma sessão de depuração. O depurador fará com que um novo token de do Firebase
Recebendo um código de status 401 do serviço de back-end
Valide se você está definindo o cabeçalho apikey solicitação e esse valor corresponde ao que você configurou para o serviço de back-end.
Se você receber esse erro ao testar localmente, verifique se o valor da chave definido na configuração do cliente corresponde ao valor de configuração de usuário
Se você estiver testando com um aplicativo de API , verifique se o valor da chave no arquivo de configuração do cliente corresponde à configuração de aplicativo Authentication:ApiKey que você está usando no aplicativo de API .
Nota
Se você tiver criado ou alterado essa configuração depois de implantar o serviço de back-end, deverá reiniciar o serviço para que ele entre em vigor.
Se você optou por não concluir os clientes
Recebendo um código de status 404 do serviço de back-end
Valide se o ponto de extremidade e o método de solicitação HTTP estão corretos. Por exemplo, os pontos de extremidade devem 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
Ou ao testar localmente:
-
[PUT]
https://localhost:5001/api/notifications/installations
-
[DELETE]
https://localhost:5001/api/notifications/installations/<installation_id>
-
[POST]
https://localhost:5001/api/notifications/requests
Ao especificar o endereço base no aplicativo cliente, verifique se ele termina com um /
. O endereço base deve ser https://<api_name>.azurewebsites.net/
ou https://localhost:5001/
ao testar localmente.
Não é possível registrar e uma mensagem de erro do hub de notificação é exibida
Verifique se o dispositivo de teste tem conectividade de rede. Em seguida, determine o código de status de resposta Http definindo um ponto de interrupção para inspecionar o valor da propriedade
Examine as sugestões de solução de problemas anteriores, quando aplicável com base no código de status.
Defina um ponto de interrupção nas linhas que retornam esses códigos de status específicos para a respectiva API. Em seguida, tente chamar o serviço de back-end ao depurar localmente.
Valide se o serviço de back-end está funcionando conforme o esperado por meio de postman usando o conteúdo apropriado. Use o conteúdo real criado pelo código do cliente para a plataforma em questão.
Examine as seções de configuração específicas da plataforma para garantir que nenhuma etapa tenha sido perdida. Verifique se os valores adequados estão sendo resolvidos para variáveis installation id
e token
para a plataforma apropriada.
Não é possível resolver uma ID para a mensagem de erro do dispositivo é exibida
Examine as seções de configuração específicas da plataforma para garantir que nenhuma etapa tenha sido perdida.
Links relacionados
- visão geral dos Hubs de Notificação do Azure
- instalando o Flutter no do macOS
- instalando o Flutter no Windows
- SDK dos Hubs de Notificação para operações de back-end
- SDK dos Hubs de Notificação no GitHub
- Registre-se com de back-end do aplicativo
- de gerenciamento de registro de
- Trabalhando com marcas
- Trabalhando com modelos personalizados
Próximas etapas
Agora você deve ter um aplicativo Flutter básico conectado a um hub de notificação por meio de um serviço de back-end e pode enviar e receber notificações.
Provavelmente, você precisará adaptar o exemplo usado neste tutorial para se ajustar ao seu próprio cenário. Também é recomendável implementar tratamento de erros, lógica de repetição e registro em log mais robustos.