Partilhar via


Criar widgets baseados em PWA

Vários sistemas operativos têm dashboards de widgets que permitem aos utilizadores ler conteúdos e realizar tarefas. Exemplos disto incluem widgets do Ecrã Principal do Android, dashboard do macOS e widgets de Painel de Hoje, Apple Touch Bar, Cartões Diários da Samsung, widgets de Mini-Aplicação e complementos de aplicações de relógio inteligente.

No Windows 11, os widgets são apresentados no Quadro de Widgets, que abre a partir do lado esquerdo da barra de tarefas:

O Widgets Board no Windows 11

No Windows 11, as Aplicações Web Progressivas (PWAs) podem definir widgets, atualizá-los e lidar com interações do utilizador dentro dos mesmos.

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

Um PWA existente não pode simplesmente ser colocado no dashboard do widget tal como está, como pode fazer com a Barra Lateral do Microsoft Edge. Em vez disso, tem de criar uma experiência de widget personalizada adequada para o anfitrião de widget, que atualmente é o Quadro de Widgets do Windows 11. (Poderão existir outros anfitriões widget no futuro.) O Quadro de Widgets do Windows 11 requer que os widgets sejam criados com modelos de Cartão Adaptável em vez de HTML e JavaScript, pelo que o widget tem de ser concebido separadamente do resto da IU da aplicação.

Veja também:

Para criar um widget baseado em PWA e entregá-lo através da Microsoft Store, não é necessário nenhum código C++/C#. Depois de produzir o widget e conseguir instalar e executar o widget com êxito a partir de um ponto final público, pode empacotar a aplicação com PWABuilder.com e enviar a aplicação para a Microsoft Store sem precisar de código adicional. O widget PWA que suporta o widget tem de ser instalável a partir de um ponto final público, uma vez que PWABuilder.com não suporta aplicações de embalagem do localhost.

Veja também:

Instalar o WinAppSDK e ativar o Modo de Programador

Para ativar o desenvolvimento e teste de widgets no seu computador local:

  • Instale o WinAppSDK 1.2.

  • Ativar o Modo de Programador no Windows 11:

    1. Abra Definições.

    2. Na caixa de texto Localizar uma definição , introduza developere, em seguida, clique em Utilizar funcionalidades de programador.

    3. Ativar Modo de Programador:

      As definições de Programador do Windows 11

Definir widgets

Os widgets são definidos no ficheiro de manifesto do PWA com o membro do widgets manifesto. Este membro do 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, uma aplicação de leitor de música define um widget de mini player. Uma definição de widget no manifesto da aplicação Web tem os seguintes campos obrigatórios e opcionais:

Campo Descrição Obrigatório
name O título do widget, apresentado aos utilizadores. Sim
short_name Uma versão abreviada alternativa do nome. Não
description Uma descrição do que o widget faz. Sim
icons Uma matriz de ícones a utilizar para o widget. Se estiver em falta, é utilizado o membro do icons manifesto. Os ícones maiores do que 1024x1024 são ignorados. Não
screenshots Uma matriz de capturas de ecrã que mostram o aspeto do widget. Análogo ao membro do screenshot manifesto. O platform campo de um item de captura de ecrã suporta os Windows valores e any . As imagens com mais de 1024x1024 pixels são ignoradas. Para obter os requisitos de captura de ecrã específicos do Quadro de Widgets do Windows 11, consulte Requisitos de imagem de captura de ecrã em Integrar com o seletor de widget. Sim
tag Uma cadeia utilizada para referenciar o widget na função de trabalho do serviço PWA. Sim
template O modelo a utilizar para apresentar o widget no dashboard de widgets do sistema operativo. Nota: atualmente, esta propriedade é apenas informativa e não é utilizada. Veja ms_ac_template abaixo. Não
ms_ac_template O URL do modelo cartões ajustáveis personalizado a utilizar para apresentar o widget no dashboard de widgets do sistema operativo. Veja Definir um modelo de widget abaixo. Sim
data O URL com o qual os dados para preencher o modelo podem ser encontrados. Se estiver presente, este URL é necessário para devolver JSON válido. Não
type O tipo de MIME para os dados do widget. Não
auth Um booleano que indica se o widget requer autenticação. Não
update A frequência, em segundos, na qual o widget será atualizado. O código na sua função de trabalho de serviço tem de efetuar a atualização; o widget não é atualizado automaticamente. Veja Access widget instances at runtime (Aceder a instâncias de widget no runtime). Não
multiple Um booleano que indica se deve permitir várias instâncias do widget. O padrão é true Não

Definir um modelo de widget

Para facilitar a criação e adaptação de widgets a vários dashboards de widgets do sistema operativo, estes são apresentados através de modelos. Existem dois tipos de modelos:

  • Modelos genéricos, definidos pelos respetivos nomes com o template campo .
  • Modelos personalizados, definidos pelos respetivos URLs através de um campo de modelo personalizado.

Por enquanto, só são suportados modelos de Cartões Ajustáveis personalizados. Os Cartões Ajustáveis são um formato de troca de cartões aberto que pode ser utilizado para trocar conteúdos de IU de forma comum e consistente. Veja Descrição Geral dos Cartões Ajustáveis.

Para definir um modelo de Cartões Ajustáveis personalizado no Windows 11, utilize o ms_ac_template campo na definição de widget que está no manifesto da sua aplicação Web. Embora template não seja atualmente utilizado, é um campo obrigatório.

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

O ms_ac_template valor do campo deve ser um URL válido de um ficheiro de modelo.

Eis um exemplo de um modelo de Cartões Ajustá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, veja Templating de Cartões Adaptáveis.

Em seguida, tem de vincular os dados ao seu modelo.

Vincular dados ao seu modelo

O modelo declara a interface de utilizador de um widget. Em seguida, os dados preenchem esta interface de utilizador.

Para vincular dados ao modelo, utilize o data campo na definição do widget. Este campo deve ser definido como um URL que devolve dados JSON válidos.

O modelo definido na secção anterior contém duas variáveis: song e artist, que estão incluídas na sintaxe da expressão de enlace: ${}. Os dados devolvidos pelo data URL na definição do widget devem conter valores para estas variáveis.

Eis um exemplo do que o data URL pode devolver:

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

Definir ações de widget

Se quiser que o widget permita que os utilizadores executem tarefas, defina um modelo que suporte ações.

Eis um exemplo de uma ação definida num modelo de Cartões Ajustáveis personalizado:

{
  "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"
}

Tenha em atenção o verb campo no modelo JSON acima. Será utilizado ao processar ações de widget no seu código de trabalho de serviço. Veja Processar ações de widget.

Aceder a instâncias de widget no runtime

Pode aceder a widgets e atualizá-los a partir do código de trabalho do serviço PWA. Aceder a widgets no runtime é útil em casos como:

Uma função de trabalho de serviço tem acesso ao self.widgets objeto e a vários eventos de widget que, em conjunto, constituem uma API que utiliza para reagir a alterações e aceder a widgets no runtime.

As secções seguintes fornecem exemplos de código. Para obter uma referência da API, veja a referência da API de trabalho de serviço.

Compor widgets na instalação

Quando um PWA é instalado, os widgets que a aplicação define no seu manifesto são adicionados ao dashboard de widgets, mas ainda não estão instalados. Um widget só é instalado quando o utilizador opta por adicionar o widget a partir do dashboard.

Quando um widget é instalado, não é composto automaticamente com os ms_ac_template campos e data da definição do widget.

Para compor o widget, ouça o widgetinstall evento na sua função de trabalho de serviço e atualize o widget com 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 nas atualizações da função de trabalho de serviço

Quando o código da função de trabalho de serviço é alterado num PWA, o browser deteta essa alteração, instala a nova função de trabalho de serviço e, posteriormente, ativa a função de trabalho de serviço.

Quando isto acontece, é importante atualizar quaisquer instâncias de widget que possam já estar em execução. Os widgets podem ter sido instalados antes de o evento de trabalho de serviço activate ser emitido. Para evitar apresentar widgets vazios, atualize os 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});
}

Processar ações de widget

Se o modelo de widget contiver ações, os utilizadores podem executar essas ações ao clicar em botões no widget composto. Para obter informações sobre como definir ações num modelo, veja Definir ações de widget.

Quando um utilizador executa uma ação de widget, é acionado um widgetclick evento na função de trabalho do serviço PWA. Para processar a ação do utilizador, 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;
  }
});

Por questões de brevidade, o código da aplicação real não é apresentado no fragmento de código acima. Quando as ações previous-song ou next-song são recebidas, é provável que seja necessário enviar uma mensagem para a aplicação através de Client.postMessage para informar a aplicação de que deve começar a reproduzir as músicas anteriores ou seguintes.

Tenha em atenção que a action propriedade do widgetEvent objeto transmitido para o serviço de escuta de eventos acima corresponde à cadeia definida no action.verb campo do modelo de widget.

Para obter mais informações sobre o widgetclick evento e as informações a que pode aceder a partir do mesmo, veja Referência da API de Trabalho de Serviço, abaixo.

Atualizar widgets sobre alterações de aplicações

Nas secções anteriores, aprendeu a atualizar widgets quando ocorreram eventos específicos de widget, ações de widget e atualizações da função de trabalho de serviço. Também pode ser útil atualizar widgets quando algo acontece na aplicação, quando ocorre uma notificação push ou periodicamente.

Nesta secção, irá aprender a utilizar a API de Sincronização de Fundo Periódica para atualizar os widgets periodicamente. Para obter mais informações sobre a API de Sincronização de Fundo Periódica, veja Utilizar a API de Sincronização de Fundo Periódica para obter regularmente conteúdos novos.

No fragmento de código seguinte, é utilizado um serviço de escuta de eventos para reagir a vários eventos de ciclo de vida do widget da aplicação. Quando é detetada uma instalação de widget, é registada uma sincronização periódica e, quando é detetada uma remoção de widget, a sincronização periódica não é registada.

Quando ocorrem eventos de sincronização periódica, as instâncias de widget são atualizadas com 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});
}

Aplicação de demonstração

PWAmp é uma aplicação de demonstração PWA do leitor de música que define um widget. O widget PWAmp permite que os utilizadores visualizem a música atual e reproduzam as músicas anteriores ou seguintes.

  1. Se ainda não tiver terminado, instale o WinAppSDK 1.2 e ative o Modo de Programador no Windows 11.

  2. Aceda a PWAmp e instale a aplicação no Windows 11.

  3. Abra a Placa Widgets do Windows 11 ao premir a tecla do logótipo do Windows + W.

  4. Clique em Adicionar widgets para abrir o ecrã de definições de widgets, desloque-se para o widget mini player PWAmp e adicione-o.

  5. Feche o ecrã de definições de widgets . O mini player PWAmp é agora apresentado no Widgets Board.

O widget PWAmp apresenta a música e os botões atuais para reproduzir a música anterior ou seguinte.

Windows Widgets Board, junto à aplicação de demonstração PWAmp. O Widgets Board contém o widget mini player PWAmp, que mostra a música atual a ser reproduzida na aplicação PWAmp

Referência da API de trabalho de serviço

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

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

O objeto global da função de trabalho de serviço também define os seguintes eventos:

  • widgetinstall: acionado quando o anfitrião do widget está a instalar um widget.
  • widgetuninstall: acionado quando o anfitrião de widget está a desinstalar um widget.
  • widgetresume: acionado quando o anfitrião de widget retoma a composição de widgets instalados, o que pode ocorrer após o anfitrião suspender a composição de widgets para preservar os recursos.
  • widgetclick: acionado quando o utilizador executa uma das ações do widget.

Para obter mais informações sobre os objetos fornecidos com estes eventos, veja o objeto widgetEvent e o objeto widgetClickEvent, abaixo.

objeto widget

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

objeto widgetOptions

Ao utilizar matchAll(options) para obter vários widgets, é necessário um widgetOptions objeto para filtrar os widgets a devolver. O widgetOptions objeto contém as seguintes propriedades, todas opcionais:

  • installable: um booleano que indica se os widgets devolvidos devem ser instaláveis.
  • installed: um booleano que indica se os widgets devolvidos estão instalados no anfitrião do widget.
  • tag: uma cadeia utilizada para filtrar os widgets devolvidos por etiqueta.
  • instanceId: uma cadeia utilizada para filtrar os widgets devolvidos por ID de instância.
  • hostId: uma cadeia utilizada para filtrar os widgets devolvidos pelo ID do anfitrião do widget.

objeto widgetPayload

Ao criar ou atualizar uma instância de widget, a função de trabalho de serviço tem de enviar o modelo e os dados necessários para preencher o widget. O modelo e os dados são denominados payload. O widgetPayload objeto contém as seguintes propriedades:

  • template: o modelo, como uma cadeia, a utilizar para compor o widget. Este será o JSON com cadeias de carateres de um modelo de Cartão Ajustável.
  • data: os dados, como uma cadeia, a utilizar com o modelo de widget. Estes dados podem ser dados JSON com cadeias de carateres.

objeto widgetInstance

Este objeto representa uma determinada instância de um widget num anfitrião widget e contém as seguintes propriedades:

  • id: a cadeia GUID interna utilizada para referenciar a instância.
  • host: um ponteiro interno para o anfitrião de widget que instalou esta 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 o último payload que foi enviado para esta instância.

objeto widgetDefinition

Este objeto representa a definição original do widget, que se encontra no ficheiro de manifesto do PWA. As propriedades deste objeto correspondem às propriedades listadas em Definir widgets, acima.

objeto widgetEvent

Este objeto é transmitido como um argumento para os serviços de escuta de eventos de widget de trabalho de serviço do tipo widgetinstall, widgetuninstalle widgetresume.

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

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

widgetClickEvent object

Este objeto é transmitido como um argumento para os serviços de escuta de eventos de widget de trabalho de serviço do tipo widgetclick. Pode abrir a janela da sua aplicação em resposta ao widgetclick evento, utilizando 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. Veja Definir ações de widget. String
widget A instância do widget que acionou o evento. widgetInstance
hostId O ID do anfitrião do widget. String
instanceId O ID da instância do widget. String