Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Para um exemplo completo e funcional, consulte o exemplo Flutter neste repositório.
Este guia demonstra como usar a winapp CLI com uma aplicação Flutter para adicionar identidade de pacote e empacotar a sua aplicação como MSIX.
A identidade de pacote é um conceito central no modelo de Windows app. Permite que a sua aplicação aceda a APIs específicas do Windows (como Notificações, Segurança, APIs de IA, etc.), tenha uma experiência limpa de instalação/desinstalação e muito mais.
Uma build padrão do Flutter Windows não tem identidade de pacote. Este guia mostra como adicioná-lo para depuração e depois empacotar para distribuição.
Pré-requisitos
Flutter SDK: Instale o Flutter seguindo o guia oficial.
Winapp CLI: Instalar a
winappCLI via winget (ou atualizar se já estiver instalado):winget install Microsoft.winappcli --source winget
1. Criar uma nova aplicação Flutter
Siga o guia na documentação oficial do Flutter para criar uma nova aplicação e executá-la.
Deverias ver a aplicação padrão do contador Futter.
2. Atualizar o código para verificar a identidade
Vamos atualizar a aplicação para verificar se está a correr com identidade de pacote. Vamos usar o Dart FFI para chamar a API Windows GetCurrentPackageFamilyName.
Primeiro, adicione o ffi pacote:
flutter pub add ffi
De seguida, substitua o conteúdo de lib/main.dart pelo seguinte código. Este código tenta recuperar a identidade atual do pacote usando a API do Windows. Se tiver sucesso, mostra o Nome da Família do Pacote na interface; caso contrário, mostra "Não embalado".
import 'dart:ffi';
import 'dart:io' show Platform;
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
/// Returns the Package Family Name if running with package identity, or null.
String? getPackageFamilyName() {
if (!Platform.isWindows) return null;
final kernel32 = DynamicLibrary.open('kernel32.dll');
final getCurrentPackageFamilyName = kernel32.lookupFunction<
Int32 Function(Pointer<Uint32>, Pointer<Uint16>),
int Function(
Pointer<Uint32>, Pointer<Uint16>)>('GetCurrentPackageFamilyName');
final length = calloc<Uint32>();
try {
// First call to get required buffer length
final result =
getCurrentPackageFamilyName(length, Pointer<Uint16>.fromAddress(0));
if (result != 122) return null; // ERROR_INSUFFICIENT_BUFFER = 122
// Second call with buffer to get the name
final namePtr = calloc<Uint16>(length.value);
try {
final result2 = getCurrentPackageFamilyName(length, namePtr);
if (result2 == 0) {
return namePtr.cast<Utf16>().toDartString(); // ERROR_SUCCESS = 0
}
return null;
} finally {
calloc.free(namePtr);
}
} finally {
calloc.free(length);
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
late final String? _packageFamilyName;
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(bottom: 24),
decoration: BoxDecoration(
color: _packageFamilyName != null
? Colors.green.shade50
: Colors.orange.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: _packageFamilyName != null
? Colors.green
: Colors.orange,
),
),
child: Text(
_packageFamilyName != null
? 'Package Family Name:\n$_packageFamilyName'
: 'Not packaged',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge,
),
),
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
3. Correr sem identidade
Agora, construa e execute a aplicação como de costume:
flutter build windows
Executa o executável diretamente (substitui flutter_app pelo nome do teu projeto se for diferente):
.\build\windows\x64\runner\Release\flutter_app.exe
Sugestão
A saída da build está na pasta x64, independentemente da arquitetura da tua máquina — isto é esperado para a build Windows do Flutter.
Deverias ver a aplicação com um indicador laranja de "Não embalado". Isto confirma que o executável padrão está a correr sem qualquer identidade de pacote.
4. Inicializar o Project com o winapp CLI
O comando winapp init configura tudo o que precisas de uma só vez: manifesto da app, assets e, opcionalmente, cabeçalhos SDK de Aplicações Windows para desenvolvimento em C++. O manifesto define a identidade da sua aplicação (nome, publicador, versão) que o Windows usa para conceder acesso à API.
Execute o seguinte comando e siga as indicações:
winapp init
Quando solicitado:
- Nome do pacote: Pressione Enter para aceitar o padrão (derivado do nome do seu projeto)
- Nome do Publicador: Pressione Enter para aceitar o padrão ou inserir o seu nome
- Versão: Pressione Enter para aceitar 1.0.0.0
- Description: Pressione Enter para aceitar a aplicação padrão (Windows Application)
- Setup SDKs: Selecione "Stable SDKs" para descarregar SDK de Aplicações Windows e gerar cabeçalhos C++ (necessário para o passo 6)
Este comando irá fazer:
- Criar
Package.appxmanifest— o manifesto que define a identidade da sua aplicação - Criar
Assetspasta — ícones necessários para o empacotamento MSIX e envio para a loja - Crie uma pasta
.winappcom cabeçalhos e bibliotecas SDK de Aplicações Windows - Criar um
winapp.yamlficheiro de configuração para fixar versões do SDK
Pode abrir Package.appxmanifest para personalizar ainda mais propriedades como o nome de visualização, publicador e capacidades.
5. Depuração com Identidade
Para testar funcionalidades que exigem identidade (como Notificações) sem embalar totalmente a aplicação, pode usar winapp run. Isto regista um pacote de layout solto (tal como numa instalação real de MSIX) e inicia a aplicação num só passo. Não é necessário nenhum certificado ou assinatura para a depuração.
Constrói a aplicação:
flutter build windowsExecutar com identidade:
winapp run .\build\windows\x64\runner\Release
Sugestão
winapp run Também regista o pacote no seu sistema. É por isso que o MSIX pode aparecer como "já instalado" quando tenta instalá-lo mais tarde, no passo 7. Use winapp unregister para limpar os pacotes de desenvolvimento ao terminar.
Agora deve ver a aplicação com um indicador verde a mostrar:
Package Family Name: flutterapp.debug_xxxxxxxx
Isto confirma que a sua aplicação está a correr com uma identidade de pacote válida!
Sugestão
Para fluxos de trabalho avançados de depuração (anexação de depuradores, configuração do IDE, depuração de arranque), consulte o Guia de Depuração.
6. Utilização do SDK de Aplicações Windows (Opcional)
Se escolheste configurar os SDKs durante winapp init, agora tens acesso a cabeçalhos SDK de Aplicações Windows C++ na pasta .winapp/include. Como o runner do Windows do Fluter é C++, podes chamar APIs do SDK de Aplicações Windows a partir de código nativo e expô-las ao Dart através de um canal de método. Se só precisares de identidade de pacote para distribuição, podes saltar para o passo 7.
Vamos acrescentar um exemplo simples que mostra a versão do Aplicação do Windows Runtime.
Criar o plugin nativo
Criar windows/runner/winapp_sdk_plugin.h:
#ifndef RUNNER_WINAPP_SDK_PLUGIN_H_
#define RUNNER_WINAPP_SDK_PLUGIN_H_
#include <flutter/flutter_engine.h>
// Registers a method channel for querying Windows App SDK info.
void RegisterWinAppSdkPlugin(flutter::FlutterEngine* engine);
#endif // RUNNER_WINAPP_SDK_PLUGIN_H_
Criar windows/runner/winapp_sdk_plugin.cpp:
#include "winapp_sdk_plugin.h"
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <winrt/Microsoft.Windows.ApplicationModel.WindowsAppRuntime.h>
#include <string>
void RegisterWinAppSdkPlugin(flutter::FlutterEngine* engine) {
auto channel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
engine->messenger(), "com.example/winapp_sdk",
&flutter::StandardMethodCodec::GetInstance());
channel->SetMethodCallHandler(
[](const flutter::MethodCall<flutter::EncodableValue>& call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (call.method_name() == "getRuntimeVersion") {
try {
// Flutter already initializes COM in main.cpp, so we skip
// winrt::init_apartment() here — the apartment is already set up.
auto version = winrt::Microsoft::Windows::ApplicationModel::
WindowsAppRuntime::RuntimeInfo::AsString();
std::string versionStr = winrt::to_string(version);
result->Success(flutter::EncodableValue(versionStr));
} catch (const winrt::hresult_error& e) {
result->Error("WINRT_ERROR", winrt::to_string(e.message()));
} catch (...) {
result->Error("UNKNOWN_ERROR",
"Failed to get Windows App Runtime version");
}
} else {
result->NotImplemented();
}
});
// prevent channel destruction by releasing ownership
channel.release();
}
Atualizar CMakeLists.txt
Editar windows/runner/CMakeLists.txt para realizar três modificações. Encontre o bloco add_executable e adicione "winapp_sdk_plugin.cpp" à lista de ficheiros de origem:
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"winapp_sdk_plugin.cpp" # <-- add this line
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
Depois adiciona estas duas linhas no final do ficheiro para ligar as bibliotecas do WinRT e inclui os cabeçalhos do SDK de Aplicações Windows:
# Link Windows Runtime libraries for WinRT
target_link_libraries(${BINARY_NAME} PRIVATE "WindowsApp.lib")
# Windows App SDK headers from winapp CLI
target_include_directories(${BINARY_NAME} PRIVATE
"${CMAKE_SOURCE_DIR}/../.winapp/include")
Registar o Plugin
Em windows/runner/flutter_window.cpp, adicione o include no topo do ficheiro com os outros includes.
#include "winapp_sdk_plugin.h"
Depois, encontre a RegisterPlugins chamada em FlutterWindow::OnCreate() e adicione RegisterWinAppSdkPlugin na linha logo a seguir:
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
Atualizar main.dart
Adicione a seguinte importação no topo de lib/main.dart, juntamente com as importações existentes:
import 'package:flutter/services.dart';
Adicione esta função abaixo da função existente getPackageFamilyName() (fora de qualquer classe):
/// Queries the Windows App Runtime version via a native method channel.
Future<String?> getWindowsAppRuntimeVersion() async {
if (!Platform.isWindows) return null;
try {
const channel = MethodChannel('com.example/winapp_sdk');
final version = await channel.invokeMethod<String>('getRuntimeVersion');
return version;
} catch (_) {
return null;
}
}
Na _MyHomePageState classe, adicione um novo campo ao lado do existente _packageFamilyName:
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
Atualize initState() para chamar a nova função.
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
Finalmente, mostrar a versão em tempo de execução no build método. Adicione este widget dentro da lista de Column filhos, logo a seguir a Container, que mostra a identidade do pacote.
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
Compilar e Executar
Reconstrua a aplicação:
flutter build windows
winapp run .\build\windows\x64\runner\Release
Agora deverá ver resultados como:
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
O diretório .winapp/include contém todos os cabeçalhos necessários para SDK de Aplicações Windows, incluindo:
-
winrt/- Cabeçalhos de projeção C++ do WinRT para acesso às APIs do Windows Runtime -
Microsoft.UI.*.h- Cabeçalhos WinUI 3 para componentes modernos de UI -
MddBootstrap.h- Inicialização do SDK do Windows App -
WindowsAppSDK-VersionInfo.h- Informação de versão - E muitos mais componentes do SDK de Aplicações Windows
Para uso mais avançado de SDK de Aplicações Windows, consulte a documentação SDK de Aplicações Windows.
7. Pacote com MSIX
Quando estiver pronto para distribuir a sua aplicação, pode empacotá-la como MSIX usando o mesmo manifesto.
Preparar o Diretório de Pacotes
Primeiro, constrói a tua aplicação em modo de lançamento:
flutter build windows
Depois, crie um diretório com os seus ficheiros de lançamento:
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
A saída da compilação do Flutter Windows inclui o executável flutter_windows.dll e uma pasta data — todas necessárias.
Gerar um Certificado de Desenvolvimento
Antes de empacotar, precisa de um certificado de desenvolvimento para assinar. Gera uma, caso ainda não o tenha feito:
winapp cert generate --if-exists skip
Assinar e Embalar
Agora pode embalar e assinar:
winapp pack .\dist --cert .\devcert.pfx
Nota: O
packcomando usa automaticamente oPackage.appxmanifestdo teu diretório atual e copia-o para a pasta de destino antes de ser embalado.
Instalar o Certificado
Antes de poderes instalar o pacote MSIX, precisas de confiar no certificado de desenvolvimento da tua máquina. Executa este comando como administrador (só precisas de o fazer uma vez por certificado):
winapp cert install .\devcert.pfx
Instalar e Executar
Sugestão
Se usou winapp run no passo 5, a encomenda pode já estar registada no seu sistema. Use winapp unregister primeiro para remover o registo de desenvolvimento, depois instale o pacote de lançamento.
Instale o pacote fazendo duplo clique no ficheiro gerado .msix , ou usando o PowerShell:
Add-AppxPackage .\flutterapp.msix
Sugestão
O nome do ficheiro MSIX inclui a versão e a arquitetura (por exemplo, flutterapplication1_1.0.0.0_x64.msix). Verifique o seu diretório para o nome exato do ficheiro. Se precisares de reempacotar após alterações de código, incrementa o Version no teu Package.appxmanifest — Windows requer um número de versão superior para atualizar um pacote instalado.
Tips
- Quando estiver pronto para a distribuição, pode assinar o seu MSIX com um certificado de assinatura de código de uma Autoridade Certificadora, para que os seus utilizadores não tenham de instalar um certificado auto-assinado.
- O serviço Assinatura Confiável do Azure é uma excelente forma de gerir os seus certificados de forma segura e integrar a assinatura no seu pipeline CI/CD.
- A Microsoft Store assina o MSIX por si, não precisa de assinar antes de submeter.
Próximas Etapas
- Distribua via winget: Submeta o seu MSIX ao Repositório Comunitário Windows Gestor de Pacotes
-
Publicar no Microsoft Store: Use
winapp storepara submeter o seu pacote - Configurar CI/CD: Use o GitHub Action para automatizar o empacotamento no seu pipeline
- Explore Windows APIs: Com a identidade do pacote, pode agora usar Notifications, on-device AI e outras APIs dependentes da identidade
Windows developer