Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Para obtener un ejemplo de trabajo completo, consulte el ejemplo de Flutter en este repositorio.
En esta guía se muestra cómo usar la winapp CLI con una aplicación flutter para agregar la identidad del paquete y empaquetar la aplicación como MSIX.
La identidad del paquete es un concepto básico en el modelo de Windows app. Permite a la aplicación acceder a api de Windows específicas (como notificaciones, seguridad, API de IA, etc.), tener una experiencia de instalación o desinstalación limpia, etc.
Una compilación estándar de Flutter Windows no tiene identidad de paquete. Esta guía muestra cómo agregarlo para depuración y luego empaquetarlo para su distribución.
Prerrequisitos
SDK de Flutter: instale Flutter siguiendo la guía oficial.
CLI de winapp: instale la
winappCLI mediante winget (o actualice si ya está instalada):winget install Microsoft.winappcli --source winget
1. Crear una nueva aplicación Flutter
Siga la guía de la documentación oficial de Flutter para crear una nueva aplicación y ejecutarla.
Deberías ver la aplicación predeterminada de contador de Flutter.
2. Actualizar código para comprobar la identidad
Actualizaremos la aplicación para comprobar si se ejecuta con la identidad del paquete. Usaremos Dart FFI para llamar a la API de Windows GetCurrentPackageFamilyName.
En primer lugar, agregue el ffi paquete:
flutter pub add ffi
A continuación, reemplace el contenido de lib/main.dart por el código siguiente. Este código intenta recuperar la identidad del paquete actual mediante la API de Windows. Si se ejecuta correctamente, muestra el nombre de familia del paquete en la interfaz de usuario; de lo contrario, muestra "No empaquetado".
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. Ejecutar sin identidad
Ahora, compile y ejecute la aplicación como de costumbre:
flutter build windows
Ejecute el archivo ejecutable directamente (reemplace por flutter_app el nombre del proyecto si es diferente):
.\build\windows\x64\runner\Release\flutter_app.exe
Sugerencia
La salida de compilación se encuentra en la carpeta x64 independientemente de la arquitectura de la máquina, lo que se espera para la compilación de Windows de Flutter.
Debería ver la aplicación con un indicador naranja "No empaquetado". Esto confirma que el ejecutable estándar se está ejecutando sin ninguna identidad de paquete.
4. Inicializar Project con la CLI de winapp
El comando winapp init configura todo lo que necesita de una vez: manifiesto de la aplicación, recursos y, opcionalmente, encabezados de SDK de Aplicaciones para Windows para el desarrollo de C++. El manifiesto define la identidad de la aplicación (nombre, publicador, versión) que Windows usa para conceder acceso a la API.
Ejecute el siguiente comando y siga las indicaciones:
winapp init
Cuando se le solicite,
- Nombre del paquete: presione Entrar para aceptar el valor predeterminado (derivado del nombre del proyecto)
- Nombre del editor: Presione Intro para aceptar el valor predeterminado o escriba su nombre.
- Versión: presione Entrar para aceptar 1.0.0.0.
- Description: presione Entrar para aceptar el valor predeterminado (aplicación de Windows)
- Setup SDK: seleccione "SDK estables" para descargar SDK de Aplicaciones para Windows y generar encabezados de C++ (necesarios para el paso 6)
Este comando hará lo siguiente:
- Crear
Package.appxmanifest: el manifiesto que define la identidad de la aplicación - Crear la carpeta
Assets— iconos necesarios para el empaquetado MSIX y la publicación en la Tienda - Crear una carpeta
.winappcon encabezados y bibliotecas del SDK de Aplicaciones para Windows - Creación de un
winapp.yamlarchivo de configuración para anclar versiones del SDK
Puede abrir Package.appxmanifest para personalizar aún más las propiedades, como el nombre para mostrar, el publicador y las funcionalidades.
5. Depurar con identidad
Para probar las características que requieren identidad (como notificaciones) sin empaquetar completamente la aplicación, puede usar winapp run. Esto registra un paquete de diseño flexible (al igual que una instalación MSIX real) e inicia la aplicación en un solo paso. No se necesita ningún certificado ni firma para la depuración.
Compile la aplicación:
flutter build windowsEjecute con identidad:
winapp run .\build\windows\x64\runner\Release
Sugerencia
winapp run también registra el paquete en el sistema. Este es el motivo por el que MSIX puede aparecer como "ya instalado" al intentar instalarlo más adelante en el paso 7. Use winapp unregister para limpiar los paquetes de desarrollo cuando haya terminado.
Ahora debería ver la aplicación con un indicador verde que muestra:
Package Family Name: flutterapp.debug_xxxxxxxx
Esto confirma que la aplicación se está ejecutando con una identidad de paquete válida.
Sugerencia
Para obtener flujos de trabajo de depuración avanzados (adjuntar depuradores, configuración del IDE, depuración de inicio), consulte la Guía de depuración.
6. Usar SDK de Aplicaciones para Windows (opcional)
Si ha seleccionado para configurar los SDK durante winapp init, ahora tiene acceso a los encabezados C++ del SDK de Aplicaciones para Windows en la carpeta .winapp/include. Dado que el ejecutable de Windows de Flutter está en C++, puedes llamar a las API de SDK de Aplicaciones para Windows desde código nativo y exponerlas a Dart a través de un canal de método. Si solo necesita la identidad del paquete para la distribución, puede ir al paso 7.
Vamos a agregar un ejemplo sencillo que muestra la versión de Aplicación de Windows Runtime.
Creación del complemento nativo
Creación de 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_
Creación de 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();
}
Actualizar CMakeLists.txt
Edite windows/runner/CMakeLists.txt para realizar tres cambios. Busque el add_executable bloque y agregue "winapp_sdk_plugin.cpp" a la lista de archivos de origen:
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"
)
A continuación, agregue estas dos líneas al final del archivo para vincular las bibliotecas de WinRT e incluya los encabezados SDK de Aplicaciones para 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")
Registro del complemento
En windows/runner/flutter_window.cpp, agregue el include en la parte superior del archivo junto con los otros includes.
#include "winapp_sdk_plugin.h"
A continuación, busque la RegisterPlugins llamada en FlutterWindow::OnCreate() y agregue RegisterWinAppSdkPlugin en la línea justo después de ella:
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
Actualizar main.dart
Agregue la siguiente importación en la parte superior de lib/main.dart, junto con las importaciones existentes:
import 'package:flutter/services.dart';
Agregue esta función debajo de la función existente getPackageFamilyName() (fuera de cualquier clase):
/// 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;
}
}
En la _MyHomePageState clase , agregue un nuevo campo junto al existente _packageFamilyName:
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
Actualice initState() para llamar a la nueva función:
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
Por último, muestre la versión en tiempo de ejecución en el método build. Agregue este widget dentro de la Column lista de elementos secundarios, justo después de que Container muestre la identidad del paquete:
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
Compilación y ejecución
Reconstruya la aplicación:
flutter build windows
winapp run .\build\windows\x64\runner\Release
Ahora debería ver un resultado similar a:
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
El directorio />
-
winrt/: encabezados de proyección de C++ de WinRT para acceder a las API de Windows Runtime -
Microsoft.UI.*.h- Encabezados WinUI 3 para componentes de interfaz de usuario modernos -
MddBootstrap.h: arranque de SDK de Aplicaciones para Windows -
WindowsAppSDK-VersionInfo.h- Información de versión - Y muchos más componentes de SDK de Aplicaciones para Windows
Para obtener un uso de SDK de Aplicaciones para Windows más avanzado, consulte la documentación de SDK de Aplicaciones para Windows.
7. Paquete con MSIX
Una vez que esté listo para distribuir la aplicación, puede empaquetarla como MSIX mediante el mismo manifiesto.
Preparar el directorio de paquetes
En primer lugar, compile la aplicación en modo de versión:
flutter build windows
A continuación, cree un directorio con sus archivos de lanzamiento.
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
La salida de compilación de Flutter Windows incluye el archivo ejecutable, flutter_windows.dll, y una carpeta data — todo lo cual es necesario.
Generación de un certificado de desarrollo
Antes de empaquetar, necesita un certificado de desarrollo para firmar. Genere uno si aún no lo ha hecho:
winapp cert generate --if-exists skip
Firmar y empaquetar
Ahora puede empaquetar y firmar:
winapp pack .\dist --cert .\devcert.pfx
Nota: El comando
packusa automáticamente elPackage.appxmanifestdel directorio actual y lo copia en la carpeta de destino antes de paquetizar.
Instalación del certificado
Para poder instalar el paquete MSIX, debe confiar en el certificado de desarrollo en el equipo. Ejecute este comando como administrador (solo tiene que hacerlo una vez por certificado):
winapp cert install .\devcert.pfx
Instalación y ejecución
Sugerencia
Si usó winapp run en el paso 5, es posible que el paquete ya esté registrado en el sistema. Use winapp unregister primero para quitar el registro de desarrollo y, a continuación, instale el paquete de versión.
Instale el paquete haciendo doble clic en el archivo generado .msix o mediante PowerShell:
Add-AppxPackage .\flutterapp.msix
Sugerencia
El nombre de archivo MSIX incluye la versión y la arquitectura (por ejemplo, flutterapplication1_1.0.0.0_x64.msix). Compruebe el directorio para ver el nombre de archivo exacto. Si necesita volver a empaquetar después de que cambie el código, incremente el Version en la Package.appxmanifest: Windows requiere un número de versión superior para actualizar un paquete instalado.
Tips
- Una vez que esté listo para su distribución, puede firmar su MSIX con un certificado de firma de código de una Autoridad de Certificación para que los usuarios no tengan que instalar un certificado autofirmado.
- El servicio Firma de confianza de Azure es una excelente manera de administrar los certificados de forma segura e integrar el inicio de sesión en la canalización de CI/CD.
- El Microsoft Store firmará el MSIX por usted, no es necesario firmar antes del envío.
Pasos siguientes
- Distribute a través de winget: Envíe su MSIX al repositorio Windows Administrador de paquetes Community
-
Publicar en Microsoft Store: Utilice
winapp storepara enviar su paquete - Configurar CI/CD: Usa la acción de GitHub para automatizar el empaquetado en la tubería
- Explore Windows APIs: Con la identidad del paquete, ahora puede usar notificaciones, IA en el dispositivo y otras APIs dependientes de la identidad