Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Ett fullständigt arbetsexempel finns i Flutter-exemplet på den här lagringsplatsen.
Den här guiden visar hur du använder winapp CLI med ett Flutter-program för att lägga till paketidentitet och paketera din app som en MSIX.
Paketidentitet är ett grundläggande begrepp i Windows app modellen. Det gör att ditt program kan komma åt specifika Windows API:er (t.ex. meddelanden, säkerhet, AI-API:er osv.), har en ren installations-/avinstallationsupplevelse med mera.
En vanlig Flutter-Windows version saknar paketidentitet. Den här guiden visar hur du lägger till den för felsökning och sedan paketera den för distribution.
Förutsättningar
Flutter SDK: Installera Flutter enligt den officiella guiden.
winapp CLI: Installera
winappCLI via winget (eller uppdatera om det redan är installerat):winget install Microsoft.winappcli --source winget
1. Skapa en ny flutterapp
Följ guiden i de officiella Flutter-dokumenten för att skapa ett nytt program och köra det.
Du ska se räknarappen i Flutter som standard.
2. Uppdatera koden för att kontrollera identiteten
Vi uppdaterar appen för att kontrollera om den körs med paketidentitet. Vi använder Dart FFI för att anropa API:et Windows GetCurrentPackageFamilyName.
Lägg först till ffi paketet:
flutter pub add ffi
Ersätt sedan innehållet i lib/main.dart med följande kod. Den här koden försöker hämta den aktuella paketidentiteten med hjälp av api:et Windows. Om det lyckas visas paketfamiljenamnet i användargränssnittet. annars visas "Inte paketerad".
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. Kör utan identitet
Nu skapar och kör du appen som vanligt:
flutter build windows
Kör den körbara filen direkt (ersätt flutter_app med projektnamnet om det är annorlunda):
.\build\windows\x64\runner\Release\flutter_app.exe
Tips/Råd
Byggutdata finns i mappen x64 oavsett datorns arkitektur – detta förväntas för Flutters Windows version.
Du bör se appen med en orange indikator för "Inte paketerad". Detta bekräftar att den körbara standarden körs utan paketidentitet.
4. Initiera Project med winapp CLI
Kommandot winapp init konfigurerar allt du behöver på en gång: appmanifest, resurser och eventuellt Windows App SDK huvuden för C++-utveckling. Manifestet definierar appens identitet (namn, utgivare, version) som Windows använder för att bevilja API-åtkomst.
Kör följande kommando och följ anvisningarna:
winapp init
När du uppmanas att göra det:
- Paketnamn: Tryck på Retur för att acceptera standardinställningen (härledd från projektnamnet)
- Utgivarens namn: Tryck på Enter för att acceptera standardvärdet eller ange ditt namn
- Version: Tryck på Retur för att acceptera 1.0.0.0
- Description: Tryck på Retur för att acceptera standardvärdet (Windows-applikation)
- Setup SDK:er: Välj "Stabila SDK:er" för att ladda ned Windows App SDK och generera C++-huvuden (krävs för steg 6)
Det här kommandot kommer att:
- Skapa
Package.appxmanifest– manifestet som definierar appens identitet - Skapa
Assetsmapp – ikoner som krävs för MSIX-paketering och Lagringsöverföring - Skapa en
.winapp-mapp med Windows App SDK rubriker och bibliotek - Skapa en
winapp.yamlkonfigurationsfil för att fästa SDK-versioner
Du kan öppna Package.appxmanifest för att ytterligare anpassa egenskaper som visningsnamn, utgivare och funktioner.
5. Felsöka med identitet
Om du vill testa funktioner som kräver identitet (t.ex. meddelanden) utan att helt paketera appen kan du använda winapp run. Detta registrerar ett löst layoutpaket (precis som en riktig MSIX-installation) och startar appen i ett steg. Inget certifikat eller signering krävs för felsökning.
Skapa appen:
flutter build windowsKör med identitet:
winapp run .\build\windows\x64\runner\Release
Tips/Råd
winapp run registrerar även paketet i systemet. Därför kan MSIX visas som "redan installerat" när du försöker installera det senare i steg 7. Använd winapp unregister för att rensa utvecklingspaket när du är färdig.
Nu bör du se appen med en grön indikator som visar:
Package Family Name: flutterapp.debug_xxxxxxxx
Detta bekräftar att din app körs med en giltig paketidentitet!
Tips/Råd
Avancerade felsökningsarbetsflöden (koppla felsökningsprogram, IDE-konfiguration, startfelsökning) finns i felsökningsguiden.
6. Använda Windows App SDK (valfritt)
Om du har valt att konfigurera SDK:erna under winapp init har du nu åtkomst till Windows App SDK C++-huvuden i mappen .winapp/include. Eftersom Flutters Windows-körning är C++, kan du anropa Windows App SDK API:er från inbyggd kod och göra dem tillgängliga för Dart via en metodkanal. Om du bara behöver paketidentitet för distribution kan du gå vidare till steg 7.
Nu ska vi lägga till ett enkelt exempel som visar Windows App Runtime-versionen.
Skapa det inbyggda plugin-programmet
Skapa 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_
Skapa 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();
}
Uppdatera CMakeLists.txt
Redigera windows/runner/CMakeLists.txt för att göra tre ändringar. Leta upp add_executable blocket och lägg till "winapp_sdk_plugin.cpp" i källfillistan:
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"
)
Lägg sedan till dessa två rader i slutet av filen för att länka WinRT-bibliotek och inkludera Windows App SDK rubriker:
# 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")
Registrera plugin-programmet
I windows/runner/flutter_window.cpp lägger du till include-anropet överst i filen med de andra inkluderingsanropen.
#include "winapp_sdk_plugin.h"
Leta sedan upp anropet RegisterPlugins i FlutterWindow::OnCreate() och lägg till RegisterWinAppSdkPlugin på raden direkt efter det.
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
Uppdatera main.dart
Lägg till följande import överst i lib/main.dart, tillsammans med de befintliga importerna:
import 'package:flutter/services.dart';
Lägg till den här funktionen under den befintliga getPackageFamilyName() funktionen (utanför vilken klass som helst):
/// 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;
}
}
_MyHomePageState I klassen lägger du till ett nytt fält bredvid det befintliga _packageFamilyName:
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
Uppdatera initState() för att anropa den nya funktionen:
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
Visa slutligen körningsversionen i build -metoden. Lägg till den här widgeten inuti listan över underordnade Column direkt efter Container som visar paketets identitet:
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
Skapa och kör
Återskapa programmet:
flutter build windows
winapp run .\build\windows\x64\runner\Release
Nu bör du se utdata som:
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
Katalogen .winapp/include innehåller alla nödvändiga rubriker för Windows App SDK, inklusive:
-
winrt/– WinRT C++-projektionshuvud för åtkomst till Windows Runtime API -
Microsoft.UI.*.h– WinUI 3-huvuden för moderna gränssnittskomponenter -
MddBootstrap.h– Windows App SDK bootstrapping -
WindowsAppSDK-VersionInfo.h– Versionsinformation - Och många fler Windows App SDK komponenter
Mer avancerad Windows App SDK användning finns i dokumentationen Windows App SDK.
7. Paket med MSIX
När du är redo att distribuera din app kan du paketera den som en MSIX med samma manifest.
Förbereda paketkatalogen
Börja med att skapa ditt program i versionsläge:
flutter build windows
Skapa sedan en katalog med dina versionsfiler:
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
Flutter-Windows build-utdata innehåller den körbara filen flutter_windows.dll och mappen data – vilka alla behövs.
Generera ett utvecklingscertifikat
Innan du paketerar behöver du ett utvecklingscertifikat för signering. Generera en om du inte redan har gjort det:
winapp cert generate --if-exists skip
Signera och packa
Nu kan du paketera och signera:
winapp pack .\dist --cert .\devcert.pfx
Obs! Kommandot
packanvänderPackage.appxmanifestautomatiskt från din aktuella katalog och kopierar den till målmappen före paketering.
Installera certifikatet
Innan du kan installera MSIX-paketet måste du lita på utvecklingscertifikatet på datorn. Kör det här kommandot som administratör (du behöver bara göra det en gång per certifikat):
winapp cert install .\devcert.pfx
Installera och kör
Tips/Råd
Om du använde winapp run i steg 5 kanske paketet redan är registrerat i systemet. Använd winapp unregister först för att ta bort utvecklingsregistreringen och installera sedan versionspaketet.
Installera paketet genom att dubbelklicka på den genererade .msix filen eller använda PowerShell:
Add-AppxPackage .\flutterapp.msix
Tips/Råd
MSIX-filnamnet innehåller versionen och arkitekturen (t.ex. flutterapplication1_1.0.0.0_x64.msix). Kontrollera katalogen för det exakta filnamnet. Om du behöver packa om efter kodändringar ökar du Version i din Package.appxmanifest – Windows kräver ett högre versionsnummer för att uppdatera ett installerat paket.
Tips
- När du är redo för distribution kan du signera MSIX med ett kodsigneringscertifikat från en certifikatutfärdare så att användarna inte behöver installera ett självsignerat certifikat.
- Tjänsten Betrodd Azure-signering är ett bra sätt att hantera dina certifikat på ett säkert sätt och integrera inloggning i CI/CD-pipelinen.
- Microsoft Store signerar MSIX åt dig, du behöver inte signera innan det skickas in.
Nästa steg
- Distribute via winget: Skicka din MSIX till Windows Správca balíkov Community Repository
-
Publicera till Microsoft Store: Skicka paketet med
winapp store -
Set up CI/CD: Använd
setup-WinAppCliGitHub Action för att automatisera paketeringen i din pipeline - Explore Windows API:er: Med paketidentitet kan du nu använda notiser, AI på enheten och andra identitetsberoende API:er
Windows developer