Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Bekijk het Flutter-voorbeeld in deze repository voor een volledig werkvoorbeeld.
In deze handleiding ziet u hoe u de winapp CLI gebruikt met een Flutter-toepassing om pakketidentiteit toe te voegen en uw app als een MSIX-pakket te verpakken.
Pakketidentiteit is een kernconcept in het Windows app model. Hiermee heeft uw toepassing toegang tot specifieke Windows API's (zoals meldingen, beveiliging, AI-API's, enzovoort), een schone installatie-/verwijderingservaring en meer.
Een standaard Flutter Windows build heeft geen pakketidentiteit. In deze handleiding ziet u hoe u deze toevoegt voor foutopsporing en deze vervolgens inpakt voor distributie.
Vereiste voorwaarden
Flutter SDK: Installeer Flutter volgens de officiële handleiding.
winapp CLI: Installeer de
winappCLI via winget (of werk bij als deze al is geïnstalleerd):winget install Microsoft.winappcli --source winget
1. Een nieuwe flutter-app maken
Volg de handleiding in de officiële Flutter-documenten om een nieuwe toepassing te maken en uit te voeren.
U zou de standaard Flutter-tellerapp moeten zien.
2. Code bijwerken om identiteit te controleren
We werken de app bij om te controleren of deze wordt uitgevoerd met pakketidentiteit. We gebruiken Dart FFI om de Windows GetCurrentPackageFamilyName API aan te roepen.
Voeg eerst het ffi pakket toe:
flutter pub add ffi
Vervang vervolgens de inhoud van lib/main.dart door de volgende code. Met deze code wordt geprobeerd de huidige pakketidentiteit op te halen met behulp van de Windows-API. Als dit lukt, wordt de familienaam van het pakket weergegeven in de gebruikersinterface; anders wordt 'Niet verpakt' weergegeven.
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. Uitvoeren zonder identiteit
Bouw en voer de app nu zoals gewoonlijk uit:
flutter build windows
Voer het uitvoerbare bestand rechtstreeks uit (vervang flutter_app met je projectnaam indien deze anders is):
.\build\windows\x64\runner\Release\flutter_app.exe
Aanbeveling
De build-uitvoer bevindt zich in de map x64, ongeacht de architectuur van uw computer. Dit wordt verwacht voor de Windows build van Flutter.
U ziet de app met een oranje indicator 'Niet verpakt'. Hiermee wordt bevestigd dat het standaard uitvoerbare bestand wordt uitgevoerd zonder pakketidentiteit.
4. Initialiseer Project met winapp CLI
Met de opdracht winapp init stelt u alles in wat u nodig hebt: app-manifest, assets en optioneel Windows App SDK headers voor C++-ontwikkeling. Het manifest definieert de identiteit van uw app (naam, uitgever, versie) die Windows gebruikt om API-toegang te verlenen.
Voer de volgende opdracht uit en volg de aanwijzingen:
winapp init
Wanneer u hierom wordt gevraagd:
- Pakketnaam: Druk op Enter om de standaardwaarde te accepteren (afgeleid van uw projectnaam)
- Publisher naam: Druk op Enter om de standaardinstelling te accepteren of voer uw naam in
- Versie: Druk op Enter om 1.0.0.0 te accepteren
- Description: Druk op Enter om de standaardwaarde (Windows toepassing) te accepteren
- Setup SDK's: Selecteer 'Stabiele SDK's' om Windows App SDK te downloaden en C++-headers te genereren (nodig voor stap 6)
Met deze opdracht wordt het volgende uitgevoerd:
- Maken
Package.appxmanifest: het manifest waarmee de identiteit van uw app wordt gedefinieerd - Map maken
Assets: pictogrammen die vereist zijn voor MSIX-pakketten en Store-inzending - Een map
.winappmaken met Windows App SDK headers en bibliotheken -
winapp.yamlEen configuratiebestand maken voor het vastmaken van SDK-versies
U kunt openen Package.appxmanifest om eigenschappen zoals de weergavenaam, uitgever en mogelijkheden verder aan te passen.
5. Debuggen met identiteit
Als u functies wilt testen waarvoor identiteit (zoals meldingen) is vereist zonder de app volledig te verpakken, kunt u dit gebruiken winapp run. Hiermee wordt een losse indelingspakket geregistreerd (net als bij een echte MSIX-installatie) en wordt de app in één stap gestart. Er is geen certificaat of ondertekening nodig voor foutopsporing.
Bouw de app:
flutter build windowsUitvoeren met identiteit:
winapp run .\build\windows\x64\runner\Release
Aanbeveling
winapp run registreert ook het pakket op uw systeem. Daarom kan de MSIX worden weergegeven als 'al geïnstalleerd' wanneer u deze later in stap 7 probeert te installeren. Gebruik winapp unregister om ontwikkelingspakketten op te schonen na voltooiing.
U ziet nu de app met een groene indicator die het volgende weergeeft:
Package Family Name: flutterapp.debug_xxxxxxxx
Hiermee wordt bevestigd dat uw app wordt uitgevoerd met een geldige pakketidentiteit.
Aanbeveling
Zie voor geavanceerde foutopsporingswerkstromen (zoals het koppelen van debuggers, IDE-installatie en het opstarten van debuggen) de handleiding voor foutopsporing.
6. Windows App SDK gebruiken (optioneel)
Als u hebt geselecteerd om de SDK's in te stellen tijdens winapp init, hebt u nu toegang tot Windows App SDK C++-headers in de map .winapp/include. Omdat De Windows runner van Flutter C++ is, kunt u Windows App SDK API's aanroepen vanuit systeemeigen code en deze beschikbaar maken voor Dart via een methodekanaal. Als u alleen pakketidentiteit nodig hebt voor distributie, kunt u doorgaan naar stap 7.
Laten we een eenvoudig voorbeeld toevoegen waarin de Windows-app Runtime-versie wordt weergegeven.
De native invoegtoepassing maken
Maken 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_
Maken 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();
}
CMakeLists.txt bijwerken
Bewerken windows/runner/CMakeLists.txt om drie wijzigingen aan te brengen. Zoek het add_executable blok en voeg deze toe "winapp_sdk_plugin.cpp" aan de lijst met bronbestanden:
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"
)
Voeg vervolgens deze twee regels toe aan het einde van het bestand om WinRT-bibliotheken te koppelen en de Windows App SDK headers op te nemen:
# 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")
De invoegtoepassing registreren
Voeg windows/runner/flutter_window.cpp de include toe boven aan het bestand met de andere includes.
#include "winapp_sdk_plugin.h"
Zoek vervolgens de RegisterPlugins aanroep in FlutterWindow::OnCreate() en voeg RegisterWinAppSdkPlugin toe aan de regel direct erna:
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
Update main.dart
Voeg de volgende import toe aan de bovenkant van lib/main.dart, naast de bestaande imports:
import 'package:flutter/services.dart';
Voeg deze functie toe onder de bestaande getPackageFamilyName() functie (buiten een klasse):
/// 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;
}
}
Voeg in de _MyHomePageState klasse een nieuw veld toe naast het bestaande _packageFamilyNameveld:
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
Werk initState() bij om de nieuwe functie aan te roepen:
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
Ten slotte geeft u de runtimeversie weer in de build methode. Voeg deze widget toe in de Column child-elementenlijst, direct na de Container die de pakketidentiteit toont:
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
Bouwen en uitvoeren
Bouw de toepassing opnieuw op:
flutter build windows
winapp run .\build\windows\x64\runner\Release
U zou nu een uitvoer moeten zien die er als volgt uitziet:
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
De map .winapp/include bevat alle benodigde headers voor Windows App SDK, waaronder:
-
winrt/- WinRT C++ projectieheaders voor toegang tot Windows Runtime API's -
Microsoft.UI.*.h- WinUI 3-headers voor moderne UI-onderdelen -
MddBootstrap.h- Windows App SDK opstarten -
WindowsAppSDK-VersionInfo.h- Versie-informatie - En nog veel meer Windows App SDK onderdelen
Raadpleeg de documentatie Windows App SDK voor geavanceerdere Windows App SDK gebruik.
7. Pakket met MSIX
Zodra u klaar bent om uw app te distribueren, kunt u deze verpakken als een MSIX met hetzelfde manifest.
De pakketmap voorbereiden
Bouw eerst uw toepassing in de releasemodus:
flutter build windows
Maak vervolgens een map met uw releasebestanden:
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
De Build-uitvoer van Flutter Windows bevat het uitvoerbare bestand, flutter_windows.dll en een map data, die allemaal nodig zijn.
Een ontwikkelingscertificaat genereren
Voordat u gaat verpakken, hebt u een ontwikkelingscertificaat nodig voor ondertekening. Genereer er een als u dat nog niet hebt gedaan:
winapp cert generate --if-exists skip
Ondertekenen en inpakken
U kunt nu het volgende inpakken en ondertekenen:
winapp pack .\dist --cert .\devcert.pfx
Opmerking: De
packopdracht maakt automatisch gebruik van dePackage.appxmanifesthuidige map en kopieert deze naar de doelmap voordat deze wordt verpakt.
Het certificaat installeren
Voordat u het MSIX-pakket kunt installeren, moet u het ontwikkelingscertificaat op uw computer vertrouwen. Voer deze opdracht uit als beheerder (u hoeft dit slechts één keer per certificaat te doen):
winapp cert install .\devcert.pfx
Installeren en uitvoeren
Aanbeveling
Als u in stap 5 hebt gebruikt winapp run , is het pakket mogelijk al geregistreerd op uw systeem. Gebruik winapp unregister eerst om de ontwikkelingsregistratie te verwijderen en installeer vervolgens het releasepakket.
Installeer het pakket door te dubbelklikken op het gegenereerde .msix bestand of met behulp van PowerShell:
Add-AppxPackage .\flutterapp.msix
Aanbeveling
De MSIX-bestandsnaam bevat de versie en architectuur (bijvoorbeeld flutterapplication1_1.0.0.0_x64.msix). Controleer uw map op de exacte bestandsnaam. Als u na codewijzigingen opnieuw moet verpakken, verhoogt u de Version in uw Package.appxmanifest. Windows vereist een hoger versienummer om een geïnstalleerd pakket bij te werken.
Tips
- Zodra u klaar bent voor distributie, kunt u uw MSIX ondertekenen met een certificaat voor ondertekening van programmacode van een certificeringsinstantie, zodat uw gebruikers geen zelfondertekend certificaat hoeven te installeren.
- De Vertrouwde ondertekening van Azure-service is een uitstekende manier om uw certificaten veilig te beheren en aanmelding te integreren in uw CI/CD-pijplijn.
- De Microsoft Store zal de MSIX voor u ondertekenen, u hoeft deze niet te ondertekenen voordat u hem indient.
Volgende stappen
- Distribute via winget: Dien uw MSIX in bij de Windows Pakketbeheer Community-opslagplaats
-
Publiceren naar de Microsoft Store: gebruik
winapp storeom uw pakket in te dienen -
Set up CI/CD: Gebruik de GitHub Actie
setup-WinAppCliom automatisering van het verpakken in uw pijplijn te realiseren. - Explore Windows API's: Met pakketidentiteit kunt u nu meldingen, on-device AI en andere identiteitsafhankelijke API's gebruiken
Windows developer