Criar widgets controlados por PWA

Vários sistemas operacionais têm painéis de widgets que permitem aos usuários ler conteúdo e executar tarefas. Exemplos disso incluem widgets da Tela Inicial do Android, widgets do Painel macOS e Painel Hoje, a Barra de Toque da Apple, Cartões Diários samsung, widgets de Mini Aplicativo e companheiros de aplicativo watch inteligentes.

No Windows 11, os widgets aparecem no Widgets Board, que você abre do lado esquerdo da barra de tarefas:

O Quadro de Widgets no Windows 11

Em Windows 11, pwas (Aplicativos Web progressivos) podem definir widgets, atualizá-los e manipular interações do usuário dentro deles.

Requer a criação de um widget personalizado para o PWA

Um PWA existente não pode simplesmente ser colocado no widget dashboard como está, como você pode com a barra lateral do Microsoft Edge. Em vez disso, você precisa criar uma experiência de widget personalizada apropriada para o host widget, que atualmente é o Windows 11 Widgets Board. (Pode haver outros hosts de widget no futuro.) O Windows 11 Widgets Board requer que os widgets sejam criados usando modelos de Cartão Adaptável em vez de HTML e JavaScript, portanto, o widget precisa ser projetado separadamente do restante da interface do usuário do aplicativo.

Veja também:

Para criar um widget controlado por PWA e entregá-lo por meio da loja da Microsoft, nenhum código C++/C# é necessário. Depois de produzir o widget e instalar e executar com êxito o widget de um ponto de extremidade público, você pode empacotar o aplicativo usando PWABuilder.com e enviar o aplicativo para a loja da Microsoft sem exigir nenhum código adicional. O suporte do PWA ao widget deve ser instalável a partir de um ponto de extremidade público, pois PWABuilder.com não dá suporte a aplicativos de empacotamento de localhost.

Veja também:

Instalar o WinAppSDK e habilitar o Modo de Desenvolvedor

Para habilitar o desenvolvimento e o teste de widgets em seu computador local:

  • Instale o WinAppSDK 1.2.

  • Habilitar o modo de desenvolvedor no Windows 11:

    1. Abra Configurações.

    2. Na caixa de texto Localizar uma configuração, insira e clique em developerUsar recursos do desenvolvedor.

    3. Habilitar o modo de desenvolvedor:

      As configurações de desenvolvedor do Windows 11

Definir widgets

Os widgets são definidos no arquivo de manifesto PWA usando o membro do widgets manifesto. Esse membro de manifesto é uma matriz que pode conter várias definições de widget.

{
  "name": "PWAmp",
  "description": "A music player app",
  "icons": [
    { "src": "img/icon-96.png", "sizes": "96x96" },
    { "src": "img/icon-128.png", "sizes": "128x128" },
    { "src": "img/icon-256.png", "sizes": "256x256" },
    { "src": "img/icon-512.png", "sizes": "512x512" }
  ],
  "widgets": [
    /* widget definitions go here */
  ]
}

Cada entrada na widgets matriz contém vários campos, conforme mostrado abaixo:

{
  ...
  "widgets": [
    {
      "name": "PWAmp mini player",
      "description": "widget to control the PWAmp music player",
      "tag": "pwamp",
      "template": "pwamp-template",
      "ms_ac_template": "widgets/mini-player-template.json",
      "data": "widgets/mini-player-data.json",
      "type": "application/json",
      "screenshots": [
        {
          "src": "./screenshot-widget.png",
          "sizes": "600x400",
          "label": "The PWAmp mini-player widget"
        }
      ],
      "icons": [
        {
          "src": "./favicon-16.png",
          "sizes": "16x16"
        }
      ],
      "auth": false,
      "update": 86400
    }
  ]
}

No exemplo acima, um aplicativo de music player define um widget de mini player. Uma definição de widget no manifesto do aplicativo Web tem os seguintes campos necessários e opcionais:

Campo Descrição Obrigatório
name O título do widget, apresentado aos usuários. Sim
short_name Uma versão curta alternativa do nome. Não
description Uma descrição do que o widget faz. Sim
icons Uma matriz de ícones a ser usada para o widget. Se estiver ausente, o membro do icons manifesto será usado em vez disso. Ícones maiores que 1024x1024 são ignorados. Não
screenshots Uma matriz de capturas de tela que mostram como é o widget. Análogo ao membro do screenshot manifesto. O platform campo de um item de captura de tela dá suporte aos Windows valores e any . Imagens maiores que 1024x1024 pixels são ignoradas. Para obter requisitos de captura de tela específicos do Windows 11 Widgets Board, consulte Requisitos de imagem de captura de tela em Integrar com o seletor de widget. Sim
tag Uma cadeia de caracteres usada para referenciar o widget no trabalho de serviço PWA. Sim
template O modelo a ser usado para exibir o widget nos widgets do sistema operacional dashboard. Observação: essa propriedade é atualmente apenas informativa e não usada. Confira ms_ac_template abaixo. Não
ms_ac_template A URL do modelo personalizado de Cartões Adaptáveis a ser usado para exibir o widget nos widgets do sistema operacional dashboard. Confira Definir um modelo de widget abaixo. Sim
data A URL em que os dados para preencher o modelo podem ser encontrados. Se estiver presente, essa URL será necessária para retornar JSON válido. Não
type O tipo MIME para os dados de widget. Não
auth Um booliano indicando se o widget requer autenticação. Não
update A frequência, em segundos, em que o widget será atualizado. O código em seu trabalho de serviço deve executar a atualização; o widget não é atualizado automaticamente. Consulte Acessar instâncias de widget no runtime. Não
multiple Um booliano que indica se deve permitir várias instâncias do widget. O padrão é true Não

Definir um modelo de widget

Para tornar os widgets fáceis de criar e se adaptar a vários painéis de widgets do sistema operacional, eles são exibidos usando modelos. Existem dois tipos de modelos:

  • Modelos genéricos, definidos por seus nomes usando o template campo.
  • Modelos personalizados, definidos por suas URLs usando um campo de modelo personalizado.

Por enquanto, há suporte apenas para modelos de Cartões Adaptáveis personalizados. Cartões Adaptáveis é um formato de troca de cartão aberto que pode ser usado para trocar conteúdo da interface do usuário de maneira comum e consistente. Confira Visão geral de cartões adaptáveis.

Para definir um modelo personalizado de Cartões Adaptáveis no Windows 11, use o ms_ac_template campo na definição de widget que está no manifesto do aplicativo Web. Embora não seja template usado atualmente, é um campo obrigatório.

{
  ...
  "template": "pwamp-template",
  "ms_ac_template": "widgets/mini-player.json",
  ...
}

O ms_ac_template valor de campo deve ser uma URL válida de um arquivo de modelo.

Aqui está um exemplo de um modelo de Cartões Adaptáveis:

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "text": "Now playing...",
      "horizontalAlignment": "Center"
    },
    {
      "type": "TextBlock",
      "spacing": "Large",
      "weight": "Bolder",
      "horizontalAlignment": "Center",
      "text": "${song}, by ${artist}",
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5"
}

Para saber mais, confira Templating cartões adaptáveis.

Em seguida, você precisa associar dados ao modelo.

Associar dados ao modelo

O modelo declara a interface do usuário de um widget. Em seguida, os dados preenchem essa interface do usuário.

Para associar dados ao modelo, use o data campo em sua definição de widget. Esse campo deve ser definido como uma URL que retorna dados JSON válidos.

O modelo definido na seção anterior contém duas variáveis: song e artist, que estão incluídas na sintaxe de expressão de associação: ${}. Os dados retornados pela data URL em sua definição de widget devem conter valores para essas variáveis.

Aqui está um exemplo do que a data URL pode retornar:

{
  "song": "I Will Always Love You",
  "artist": "Whitney Houston"
}

Definir ações de widget

Se você quiser que seu widget permita que os usuários executem tarefas, defina um modelo que dê suporte a ações.

Aqui está um exemplo de uma ação definida em um modelo personalizado de Cartões Adaptáveis:

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "text": "Now playing...",
      "horizontalAlignment": "Center"
    },
    {
      "type": "TextBlock",
      "spacing": "Large",
      "weight": "Bolder",
      "horizontalAlignment": "Center",
      "text": "${song}, by ${artist}",
    }
  ],
  "actions": [
    {
      "type": "Action.Execute",
      "title": "Previous",
      "verb": "previous-song"
    },
    {
      "type": "Action.Execute",
      "title": "Next",
      "verb": "next-song"
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5"
}

Observe o verb campo no modelo JSON acima. Ele será usado ao lidar com ações de widget em seu código de trabalho de serviço. Consulte Manipular ações de widget.

Acessar instâncias de widget no runtime

Você pode acessar widgets e atualizá-los do código de trabalho do serviço PWA. Acessar widgets no runtime é útil em casos como:

Os funcionários do serviço têm acesso ao objeto e a self.widgets vários eventos de widget que, juntos, constituem uma API que você usa para reagir a alterações e acessar widgets no runtime.

As seções a seguir fornecem exemplos de código. Para obter uma referência da API, consulte a referência da API do trabalho de serviço.

Renderizar widgets na instalação

Quando um PWA é instalado, os widgets que o aplicativo define em seu manifesto são adicionados aos widgets dashboard mas ainda não instalados. Um widget só é instalado quando o usuário opta por adicionar o widget do dashboard.

Quando um widget é instalado, ele não é renderizado automaticamente usando os ms_ac_template campos e data da definição de widget.

Para renderizar o widget, ouça o widgetinstall evento em seu trabalho de serviço e atualize o widget usando a widgets.updateByTag função:

// Listen to the widgetinstall event.
self.addEventListener("widgetinstall", event => {
  // The widget just got installed, render it using renderWidget.
  // Pass the event.widget object to the function.
  event.waitUntil(renderWidget(event.widget));
});

async function renderWidget(widget) {
  // Get the template and data URLs from the widget definition.
  const templateUrl = widget.definition.msAcTemplate;
  const dataUrl = widget.definition.data;

  // Fetch the template text and data.
  const template = await (await fetch(templateUrl)).text();
  const data = await (await fetch(dataUrl)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

Atualizar widgets em atualizações de trabalho de serviço

Quando o código do trabalho de serviço é alterado em um PWA, o navegador detecta essa alteração, instala o novo trabalho de serviço e, em seguida, o ativa.

Quando isso acontece, é importante atualizar todas as instâncias de widget que já estejam em execução. Os widgets podem ter sido instalados antes do evento de trabalho activate do serviço ser emitido. Para evitar exibir widgets vazios, atualize seus widgets quando o activate evento ocorrer

// Update the widgets to their initial states
// when the service worker is activated.
self.addEventListener("activate", event => {
  event.waitUntil(updateWidgets());
});

async function updateWidgets() {
  // Get the widget that match the tag defined in the web app manifest.
  const widget = await self.widgets.getByTag("pwamp");
  if (!widget) {
    return;
  }

  // Using the widget definition, get the template and data.
  const template = await (await fetch(widget.definition.msAcTemplate)).text();
  const data = await (await fetch(widget.definition.data)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

Manipular ações de widget

Se o modelo de widget contiver ações, os usuários poderão executar essas ações clicando em botões no widget renderizado. Para obter informações sobre como definir ações em um modelo, consulte Definir ações de widget.

Quando um usuário executa uma ação de widget, um widgetclick evento é disparado no trabalho de serviço PWA. Para lidar com a ação do usuário, ouça o evento:

self.addEventListener('widgetclick', (event) => {
  switch (event.action) {
    case 'previous-song':
      // Application logic to play the previous song...
      break;
    case 'next-song':
      // Application logic to play the next song...
      break;
  }
});

Para brevidade, o código do aplicativo real não é mostrado no snippet de código acima. Quando as previous-song ações ou next-song são recebidas, uma mensagem provavelmente precisaria ser enviada para o aplicativo usando Client.postMessage para informar ao aplicativo que ele deve começar a reproduzir as músicas anteriores ou próximas.

Observe que a action propriedade do widgetEvent objeto passada para o ouvinte de eventos acima corresponde à cadeia de caracteres definida no action.verb campo do modelo de widget.

Para obter mais informações sobre o widgetclick evento e quais informações você pode acessar dele, confira a referência da API do Trabalho de Serviço abaixo.

Atualizar widgets em alterações de aplicativo

Nas seções anteriores, você aprendeu a atualizar widgets quando ocorreram eventos de widget específicos, ações de widget e atualizações de trabalho de serviço. Também pode ser útil atualizar widgets quando algo acontece no aplicativo ou quando ocorre uma notificação por push ou periodicamente.

Nesta seção, você aprenderá a usar a API de Sincronização periódica em segundo plano para atualizar widgets periodicamente. Para obter mais informações sobre a API de Sincronização periódica em segundo plano, consulte Usar a API de Sincronização de Plano de Fundo Periódico para obter conteúdo recente regularmente.

No snippet de código a seguir, um ouvinte de eventos é usado para reagir a vários eventos de ciclo de vida do widget do aplicativo. Quando uma instalação de widget é detectada, uma sincronização periódica é registrada e quando uma remoção de widget é detectada, a sincronização periódica não é registrada.

Quando ocorrem eventos de sincronização periódica, as instâncias de widget são atualizadas usando a widgets.updateByTag função.

self.addEventListener("widgetinstall", event => {
  event.waitUntil(onWidgetInstall(event.widget));
});

self.addEventListener("widgetuninstall", event => {
  event.waitUntil(onWidgetUninstall(event.widget));
});

async function onWidgetInstall(widget) {
  // Register a periodic sync, if this wasn't done already.
  // We use the same tag for the sync registration and the widget to
  // avoid registering several periodic syncs for the same widget.
  const tags = await self.registration.periodicSync.getTags();
  if (!tags.includes(widget.definition.tag)) {
    await self.registration.periodicSync.register(widget.definition.tag, {
      minInterval: widget.definition.update
    });
  }

  // And also update the instance.
  await updateWidget(widget);
}

async function onWidgetUninstall(widget) {
  // On uninstall, unregister the periodic sync.
  // If this was the last widget instance, then unregister the periodic sync.
  if (widget.instances.length === 1 && "update" in widget.definition) {
    await self.registration.periodicSync.unregister(widget.definition.tag);
  }
}

// Listen to periodicsync events to update all widget instances
// periodically.
self.addEventListener("periodicsync", async event => {
  const widget = await self.widgets.getByTag(event.tag);

  if (widget && "update" in widget.definition) {
    event.waitUntil(updateWidget(widget));
  }
});

async function updateWidget(widget) {
  // Get the template and data URLs from the widget definition.
  const templateUrl = widget.definition.msAcTemplate;
  const dataUrl = widget.definition.data;

  // Fetch the template text and data.
  const template = await (await fetch(templateUrl)).text();
  const data = await (await fetch(dataUrl)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

Aplicativo de demonstração

PWAmp é um aplicativo de demonstração PWA do player de música que define um widget. O widget PWAmp permite que os usuários visualizem a música atual e toquem as músicas anteriores ou próximas.

  1. Se ainda não terminar, instale o WinAppSDK 1.2 e habilite o Modo de Desenvolvedor no Windows 11.

  2. Acesse PWAmp e instale o aplicativo no Windows 11.

  3. Abra o Windows 11 Widgets Board pressionando a tecla do logotipo do Windows + W.

  4. Clique em Adicionar widgets para abrir a tela de configurações de widgets , role até o widget do mini player PWAmp e adicione-o.

  5. Feche a tela de configurações de widgets . O mini player PWAmp agora é exibido no Widgets Board.

O widget PWAmp exibe a música e os botões atuais para reproduzir a música anterior ou a próxima.

Windows Widgets Board, ao lado do aplicativo de demonstração PWAmp. O Widgets Board contém o widget do mini player PWAmp, mostrando a música atual tocando no aplicativo PWAmp

Referência da API do Trabalho de Serviço

O objeto global do trabalho de serviço (ou ServiceWorkerGlobalScope) contém um widgets atributo que expõe os seguintes métodos baseados em promessa:

Método Descrição Parâmetros Valor de retorno
getByTag(tag) Obter um widget por marca A marca widget Uma Promessa que resolve para o objeto widget que corresponde à marca ou undefined.
getByInstanceId(id) Obter um widget por ID da instância A ID da instância do widget Uma Promessa que resolve para o objeto widget correspondente ou undefined.
getByHostId(id) Obter widgets por ID do host A ID do host Uma matriz de objetos widget encontrados nesse host.
matchAll(options) Obter widgets por meio de opções correspondentes Um objeto widgetOptions Uma Promessa que se resolve para uma matriz de objetos widget que correspondem aos options critérios.
updateByInstanceId(id, payload) Atualizar um widget por ID da instância A ID da instância e um objeto widgetPayload Uma Promessa que resolve para undefined ou Error.
updateByTag(tag, payload) Atualizar um widget por marca A marca do widget e um objeto widgetPayload Uma Promessa que resolve para undefined ou Error.

O objeto global do service worker também define os seguintes eventos:

  • widgetinstall: disparado quando o host de widget está instalando um widget.
  • widgetuninstall: disparado quando o host widget está desinstalando um widget.
  • widgetresume: disparado quando o host de widget retoma a renderização de widgets instalados, o que pode acontecer depois que o host suspendeu a renderização de widgets para preservar recursos.
  • widgetclick: disparado quando o usuário executa uma das ações de widget.

Para obter mais informações sobre os objetos fornecidos com esses eventos, consulte o objeto widgetEvent e as definições de objeto widgetClickEvent abaixo.

objeto widget

Cada widget é representado como um widget objeto, que contém as seguintes propriedades:

objeto widgetOptions

Ao usar matchAll(options) para obter vários widgets, um widgetOptions objeto é necessário para filtrar quais widgets retornarão. O widgetOptions objeto contém as seguintes propriedades, todas opcionais:

  • installable: um booliano que indica se os widgets retornados devem ser instalados.
  • installed: um booliano indicando se os widgets retornados estão instalados no host de widget.
  • tag: uma cadeia de caracteres usada para filtrar os widgets retornados por marca.
  • instanceId: uma cadeia de caracteres usada para filtrar os widgets retornados por ID da instância.
  • hostId: uma cadeia de caracteres usada para filtrar os widgets retornados pela ID do host do widget.

objeto widgetPayload

Ao criar ou atualizar uma instância de widget, o trabalhador do serviço deve enviar o modelo e os dados necessários para preencher o widget. O modelo e os dados são chamados de carga. O widgetPayload objeto contém as seguintes propriedades:

  • template: o modelo, como uma cadeia de caracteres, a ser usado para renderizar o widget. Este será o JSON com cadeia de caracteres de um modelo de Cartão Adaptável.
  • data: os dados, como uma cadeia de caracteres, a serem usados com o modelo de widget. Esses dados podem ser dados JSON em cadeia de caracteres.

objeto widgetInstance

Este objeto representa uma determinada instância de um widget em um host widget e contém as seguintes propriedades:

  • id: a cadeia de caracteres GUID interna usada para fazer referência à instância.
  • host: um ponteiro interno para o host de widget que instalou essa instância.
  • updated: um Date objeto que representa a última vez que os dados foram enviados para a instância.
  • payload: um objeto widgetPayload que representa a última carga enviada para esta instância.

objeto widgetDefinition

Esse objeto representa a definição original do widget, encontrado no arquivo de manifesto PWA. As propriedades desse objeto correspondem às propriedades listadas em Definir widgets, acima.

objeto widgetEvent

Esse objeto é passado como um argumento para os ouvintes de eventos de widget do trabalho de serviço do tipo widgetinstall, widgetuninstalle widgetresume.

Para os widgetinstalltipos , widgetuninstalle widgetresume de eventos, o widgetEvent objeto tem as seguintes propriedades:

Propriedade Descrição Tipo
widget A instância do widget que acionou o evento. Widget
instanceId A ID da instância do widget. String
hostId A ID do host do widget. String

objeto widgetClickEvent

Esse objeto é passado como um argumento para os ouvintes de eventos de widget do trabalho de serviço do tipo widgetclick. Você pode abrir a janela do aplicativo em resposta ao widgetclick evento usando clients.openWindow().

O widgetClickEvent objeto tem as seguintes propriedades:

Propriedade Descrição Tipo
action A ação que acionou o evento, conforme definido nos actions.verb campos do modelo de widget. Consulte Definir ações de widget. String
widget A instância do widget que acionou o evento. widgetInstance
hostId A ID do host do widget. String
instanceId A ID da instância do widget. String