Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Pour obtenir un exemple de travail complet, consultez l’exemple Flutter dans ce référentiel.
Ce guide montre comment utiliser l’interface winapp CLI avec une application Flutter pour ajouter une identité de package et empaqueter votre application en tant que MSIX.
L’identité de package est un concept de base dans le modèle Windows app. Elle permet à votre application d’accéder à des API Windows spécifiques (telles que notifications, sécurité, API IA, etc.), d’avoir une expérience d’installation/désinstallation propre, etc.
Une build Windows Flutter standard n’a pas d’identité de package. Ce guide montre comment l’ajouter pour le débogage, et ensuite l'emballer pour la distribution.
Prerequisites
Kit de développement logiciel (SDK) Flutter : Installez Flutter en suivant le guide officiel.
winapp CLI : installez l’interface
winappCLI via winget (ou mettez à jour si elle est déjà installée) :winget install Microsoft.winappcli --source winget
1. Créer une nouvelle application Flutter
Suivez le guide de la documentation officielle Flutter pour créer une application et l’exécuter.
Vous devez voir l'application de compteur par défaut de Flutter.
2. Mettre à jour le code pour vérifier l’identité
Nous allons mettre à jour l’application pour vérifier si elle s’exécute avec l’identité du package. Nous allons utiliser Dart FFI pour appeler l'API Windows GetCurrentPackageFamilyName.
Tout d’abord, ajoutez le ffi package :
flutter pub add ffi
Ensuite, remplacez le contenu de lib/main.dart par le code suivant. Ce code tente de récupérer l’identité de package actuelle à l’aide de l’API Windows. S’il réussit, il affiche le nom de la famille de packages dans l’interface utilisateur ; sinon, il affiche « Non empaqueté ».
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. Exécuter sans identité
À présent, générez et exécutez l’application comme d’habitude :
flutter build windows
Exécutez l’exécutable directement (remplacez flutter_app par le nom de votre projet si différent) :
.\build\windows\x64\runner\Release\flutter_app.exe
Conseil / Astuce
La sortie de build se trouve dans le dossier x64, quelle que soit l'architecture de votre ordinateur. Cela est attendu pour la build Windows de Flutter.
Vous devez voir l’application avec un indicateur orange « Non empaqueté ». Cela confirme que l’exécutable standard est en cours d’exécution sans identité de package.
4. Initialiser le projet avec l’interface CLI winapp
La commande winapp init configure tout ce dont vous avez besoin en une seule étape : manifeste d’application, ressources et éventuellement SDK d'application Windows en-têtes pour le développement C++. Le manifeste définit l'identité de votre application (nom, éditeur, version) que Windows utilise pour accorder l'accès à l'API.
Exécutez la commande suivante et suivez les instructions :
winapp init
Lorsque vous y êtes invité :
- Nom du package : appuyez sur Entrée pour accepter la valeur par défaut (dérivée du nom de votre projet)
- Publisher nom : appuyez sur Entrée pour accepter la valeur par défaut ou entrer votre nom
- Version : appuyez sur Entrée pour accepter la version 1.0.0.0
- Description : appuyez sur Entrée pour accepter la valeur par défaut (application Windows)
- Setup SDK : sélectionnez « Sdk stables » pour télécharger SDK d'application Windows et générer des en-têtes C++ (nécessaires pour l’étape 6)
Cette commande va :
- Créer
Package.appxmanifest: manifeste qui définit l’identité de votre application - Créer un
Assetsdossier : icônes requises pour l’empaquetage MSIX et la soumission du Windows Store - Créer un dossier
.winappavec des en-têtes et des bibliothèques SDK d'application Windows - Créer un fichier de configuration
winapp.yamlpour l’épinglage des versions du Kit de développement logiciel (SDK)
Vous pouvez ouvrir Package.appxmanifest pour personnaliser davantage les propriétés telles que le nom d’affichage, l’éditeur et les fonctionnalités.
5. Déboguer avec l’identité numérique
Pour tester les fonctionnalités qui nécessitent une identité (comme notifications) sans empaqueter entièrement l’application, vous pouvez utiliser winapp run. Cela inscrit un package de disposition libre (comme une véritable installation MSIX) et lance l’application en une seule étape. Aucun certificat ou signature n’est nécessaire pour le débogage.
Générez l’application :
flutter build windowsExécuter avec l’identité :
winapp run .\build\windows\x64\runner\Release
Conseil / Astuce
winapp run inscrit également le package sur votre système. C’est pourquoi MSIX peut apparaître comme étant « déjà installé » lorsque vous essayez de l’installer ultérieurement à l’étape 7. Utilisez winapp unregister pour nettoyer les packages de développement quand vous avez terminé.
Vous devez maintenant voir l’application avec un indicateur vert montrant :
Package Family Name: flutterapp.debug_xxxxxxxx
Cela confirme que votre application est en cours d’exécution avec une identité de package valide !
Conseil / Astuce
Pour les flux de travail de débogage avancés (attachement de débogueurs, configuration de l’IDE, débogage au démarrage), consultez le Guide de débogage.
6. Utilisation de SDK d'application Windows (facultatif)
Si vous avez choisi de configurer les SDK pendant winapp init, vous avez désormais accès aux en-têtes C++ de SDK d'application Windows dans le dossier .winapp/include. Étant donné que l'exécuteur de Windows Flutter est C++, vous pouvez appeler SDK d'application Windows API à partir du code natif et les exposer à Dart via un canal de méthode. Si vous avez simplement besoin d’une identité de package pour la distribution, vous pouvez passer à l’étape 7.
Ajoutons un exemple simple qui affiche la version application Windows Runtime.
Créer le plug-in natif
Créez 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_
Créez 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();
}
Mettre à jour CMakeLists.txt
Modifiez windows/runner/CMakeLists.txt pour apporter trois modifications. Recherchez le bloc add_executable et ajoutez "winapp_sdk_plugin.cpp" à la liste des fichiers sources.
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"
)
Ajoutez ensuite ces deux lignes à la fin du fichier pour lier des bibliothèques WinRT et incluez les en-têtes SDK d'application 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")
Inscrire le plug-in
Dans windows/runner/flutter_window.cpp, ajoutez l'instruction include en haut du fichier avec les autres includes :
#include "winapp_sdk_plugin.h"
Ensuite, recherchez l'appel RegisterPlugins dans FlutterWindow::OnCreate() et ajoutez RegisterWinAppSdkPlugin à la ligne juste après :
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
Mettre à jour main.dart
Ajoutez l’importation suivante en haut de lib/main.dart, en même temps que les importations existantes :
import 'package:flutter/services.dart';
Ajoutez cette fonction sous la fonction existante getPackageFamilyName() (en dehors de n’importe quelle 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;
}
}
Dans la _MyHomePageState classe, ajoutez un nouveau champ en regard de l’existant _packageFamilyName:
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
Mettre à jour initState() pour appeler la nouvelle fonction :
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
Enfin, affichez la version du runtime dans la build méthode. Ajoutez ce widget à l’intérieur de la liste des éléments Column, juste après le Container qui affiche l'identité du package.
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
Compiler et exécuter
Régénérez l’application :
flutter build windows
winapp run .\build\windows\x64\runner\Release
Vous devriez maintenant voir une sortie ressemblant à :
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
Le répertoire .winapp/include contient tous les en-têtes nécessaires pour SDK d'application Windows, notamment :
-
winrt/- En-têtes de projection C++ pour WinRT permettant d'accéder aux API Windows Runtime -
Microsoft.UI.*.h- En-têtes WinUI 3 pour les composants d’interface utilisateur modernes -
MddBootstrap.h- initialisation du SDK d'applications Windows -
WindowsAppSDK-VersionInfo.h- Informations sur la version - Et bien d’autres composants SDK d'application Windows
Pour une utilisation plus avancée SDK d'application Windows, consultez la documentation SDK d'application Windows.
7. Package avec MSIX
Une fois que vous êtes prêt à distribuer votre application, vous pouvez la empaqueter en tant que MSIX à l’aide du même manifeste.
Préparer le répertoire du package
Tout d’abord, générez votre application en mode mise en production :
flutter build windows
Ensuite, créez un répertoire avec vos fichiers de mise en production :
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
La sortie de build Flutter Windows inclut l’exécutable, flutter_windows.dll et un dossier data, qui sont tous nécessaires.
Générer un certificat de développement
Avant d’empaqueter, vous avez besoin d’un certificat de développement pour la signature. Générez-en un si vous ne l’avez pas déjà fait :
winapp cert generate --if-exists skip
Signer et Emballer
Vous pouvez maintenant empaqueter et signer :
winapp pack .\dist --cert .\devcert.pfx
Remarque : la commande
packutilise automatiquement lePackage.appxmanifestdepuis votre répertoire actuel et la copie dans le dossier cible avant l’empaquetage.
Installer le certificat
Avant de pouvoir installer le package MSIX, vous devez approuver le certificat de développement sur votre ordinateur. Exécutez cette commande en tant qu’administrateur (vous devez effectuer cette opération une seule fois par certificat) :
winapp cert install .\devcert.pfx
Installer et exécuter
Conseil / Astuce
Si vous avez utilisé winapp run à l’étape 5, le package peut déjà être inscrit sur votre système. Utilisez winapp unregister d’abord pour supprimer l’inscription de développement, puis installer le package de mise en production.
Installez le package en double-cliquant sur le fichier généré .msix ou à l’aide de PowerShell :
Add-AppxPackage .\flutterapp.msix
Conseil / Astuce
Le nom de fichier MSIX inclut la version et l’architecture (par exemple, flutterapplication1_1.0.0.0_x64.msix). Recherchez le nom de fichier exact dans votre répertoire. Si vous devez repackager une fois le code modifié, incrémentez le Version dans votre Package.appxmanifest , Windows nécessite un numéro de version supérieur pour mettre à jour un package installé.
Conseils
- Une fois que vous êtes prêt à être distribué, vous pouvez signer votre MSIX avec un certificat de signature de code auprès d’une autorité de certification afin que vos utilisateurs n’aient pas besoin d’installer un certificat auto-signé.
- Le service Signatures de confiance Azure est un excellent moyen de gérer vos certificats en toute sécurité et d’intégrer la connexion à votre pipeline CI/CD.
- Le Microsoft Store signera le MSIX pour vous, sans avoir à le signer vous-même avant la soumission.
Prochaines étapes
- Distribute via winget : envoyez votre MSIX au dépôt Windows Gestionnaire de package Community Repository
-
Publish dans le Microsoft Store : utilisez
winapp storepour envoyer votre package -
Configurer CI/CD : utilisez l’action
setup-WinAppCliGitHub pour automatiser l’empaquetage dans votre pipeline - Explorez les API Windows : avec l’identité du package, vous pouvez maintenant utiliser les Notifications, l’IA sur appareil et d’autres API dépendantes de l’identité
Windows developer