完全な実際の例については、このリポジトリの Flutter サンプル を確認してください。
このガイドでは、Flutter アプリケーションで winapp CLI を使用してパッケージ ID を追加し、アプリを MSIX としてパッケージ化する方法について説明します。
パッケージ ID は、Windows app モデルの主要な概念です。 これにより、アプリケーションは特定のWindows API (通知、セキュリティ、AI API など) にアクセスでき、クリーンなインストール/アンインストール エクスペリエンスが提供されます。
標準の Flutter Windows ビルドにはパッケージ ID がありません。 このガイドでは、デバッグ用に追加し、配布用にパッケージ化する方法を示します。
[前提条件]
Flutter SDK: 公式ガイドに従って Flutter をインストールします。
winapp CLI: winget を使用して
winappCLI をインストールします (既にインストールされている場合は更新します)。winget install Microsoft.winappcli --source winget
1. 新しい Flutter アプリを作成する
Flutter の公式ドキュメントのガイドに従って、新しいアプリケーションを作成して実行します。
既定の Flutter カウンター アプリが表示されます。
2. コードを更新して ID を確認する
アプリがパッケージ ID で実行されているかどうかを確認するように更新します。 Dart FFI を使用して、Windows GetCurrentPackageFamilyName API を呼び出します。
まず、 ffi パッケージを追加します。
flutter pub add ffi
次に、 lib/main.dart の内容を次のコードに置き換えます。 このコードは、Windows API を使用して現在のパッケージ ID の取得を試みます。 成功すると、UI にパッケージ ファミリ名が表示されます。それ以外の場合は、"パッケージ化されていません" と表示されます。
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. ID なしで実行する
次に、通常どおりにアプリをビルドして実行します。
flutter build windows
実行可能ファイルを直接実行します (異なる場合は、 flutter_app をプロジェクト名に置き換えます)。
.\build\windows\x64\runner\Release\flutter_app.exe
ヒント
ビルド出力は、コンピューターのアーキテクチャに関係なく、x64 フォルダーにあります。これは Flutter のWindows ビルドに必要です。
オレンジ色の "パッケージ化されていない" インジケーターが表示されたアプリが表示されます。 これにより、標準の実行可能ファイルがパッケージ ID なしで実行されていることが確認されます。
4. winapp CLI を使用してProjectを初期化する
winapp init コマンドは、アプリ マニフェスト、アセット、および必要に応じて C++ 開発用のWindows アプリ SDKヘッダーなど、必要なものをすべて一度に設定します。 マニフェストは、API アクセスを許可するために使用Windowsアプリの ID (名前、発行元、バージョン) を定義します。
次のコマンドを実行し、プロンプトに従います。
winapp init
プロンプトが表示されたら、次を実行します。
- パッケージ名: Enter キーを押して既定値を受け入れます (プロジェクト名から派生)
- Publisher名: Enter キーを押して既定値をそのまま使用するか、名前を入力します
- バージョン: Enter キーを押して 1.0.0.0 を受け入れる
- Description: Enter キーを押して既定値 (Windows アプリケーション) をそのまま使用します。
- Setup SDK: "Stable SDK" を選択してWindows アプリ SDKをダウンロードし、C++ ヘッダーを生成します (手順 6 で必要)
このコマンドは次の操作を行います:
-
Package.appxmanifestの作成 - アプリの ID を定義するマニフェスト -
Assetsフォルダーの作成 - MSIX パッケージ化とストアの申請に必要なアイコン - Windows アプリ SDK ヘッダーとライブラリを含む
.winappフォルダーを作成する - SDK バージョンをピン留めするための
winapp.yaml構成ファイルを作成する
Package.appxmanifestを開いて、表示名、発行元、機能などのプロパティをさらにカスタマイズできます。
5. ID を使用したデバッグ
アプリを完全にパッケージ化せずに ID (通知など) を必要とする機能をテストするには、 winapp runを使用できます。 これにより、ルーズ レイアウト パッケージ (実際の MSIX インストールと同様) が登録され、1 つの手順でアプリが起動されます。 デバッグに証明書や署名は必要ありません。
アプリをビルドします。
flutter build windowsID を使用して実行する:
winapp run .\build\windows\x64\runner\Release
ヒント
winapp run は、パッケージをシステムに登録します。 このため、手順 7 で後で MSIX をインストールしようとすると、MSIX が "既にインストール済み" と表示される可能性があります。 完了したら、 winapp unregister を使用して開発パッケージをクリーンアップします。
緑色のインジケーターが表示されたアプリが表示されます。
Package Family Name: flutterapp.debug_xxxxxxxx
これにより、アプリが有効なパッケージ ID で実行されていることを確認できます。
ヒント
高度なデバッグ ワークフロー (デバッガーのアタッチ、IDE セットアップ、スタートアップ デバッグ) については、 デバッグ ガイドを参照してください。
6. Windows アプリ SDKの使用 (省略可能)
winapp init中に SDK をセットアップすることを選択した場合は、.winapp/include フォルダー内の Windows アプリ SDK C++ ヘッダーにアクセスできるようになりました。 Flutter のWindows ランナーは C++ であるため、ネイティブ コードから Windows アプリ SDK API を呼び出し、メソッド チャネルを介して Dart に公開できます。 配布にパッケージ ID が必要な場合は、手順 7 に進むことができます。
Windows アプリランタイムバージョンを表示する簡単な例を追加しましょう。
ネイティブ プラグインを作成する
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_
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 の更新
windows/runner/CMakeLists.txtを編集して、3 つの変更を行います。
add_executable ブロックを見つけて、ソース ファイルの一覧に"winapp_sdk_plugin.cpp"を追加します。
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"
)
次に、ファイルの末尾に次の 2 行を追加して WinRT ライブラリをリンクし、Windows アプリ SDKヘッダーを含めます。
# 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")
プラグインを登録する
windows/runner/flutter_window.cppで、ファイルの先頭に他のインクルードと共にインクルードを追加してください。
#include "winapp_sdk_plugin.h"
次に、RegisterPluginsでFlutterWindow::OnCreate()呼び出しを見つけて、その直後の行にRegisterWinAppSdkPluginを追加します。
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
main.dart を更新する
既存のインポートと共に、 lib/main.dartの上部に次のインポートを追加します。
import 'package:flutter/services.dart';
既存の getPackageFamilyName() 関数の下にこの関数を追加します (任意のクラスの外部)。
/// 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 クラスで、既存の_packageFamilyNameの横に新しいフィールドを追加します。
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
initState()を更新して新しい関数を呼び出します。
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
最後に、 build メソッドにランタイム バージョンを表示します。 パッケージ ID を示すColumnのすぐ後に、Containerの子リスト内に次のウィジェットを追加します。
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
ビルドと実行
アプリケーションをリビルドします。
flutter build windows
winapp run .\build\windows\x64\runner\Release
次のような出力が表示されます。
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
.winapp/include ディレクトリには、次のようなWindows アプリ SDKに必要なすべてのヘッダーが含まれています。
-
winrt/- Windows ランタイム API にアクセスするための WinRT C++ プロジェクション ヘッダー -
Microsoft.UI.*.h- 最新の UI コンポーネント用の WinUI 3 ヘッダー -
MddBootstrap.h- Windows アプリ SDK のブートストラッピング -
WindowsAppSDK-VersionInfo.h- バージョン情報 - さらに多くのWindows アプリ SDKコンポーネント
Windows アプリ SDKの詳細については、Windows アプリ SDKドキュメントを参照してください。
7. MSIX を使用したパッケージ化
アプリを配布する準備ができたら、同じマニフェストを使用して MSIX としてパッケージ化できます。
パッケージ ディレクトリを準備する
まず、リリース モードでアプリケーションをビルドします。
flutter build windows
次に、リリース ファイルを含むディレクトリを作成します。
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
Flutter Windows ビルド出力には、実行可能ファイル、flutter_windows.dll、および data フォルダーが含まれます。これらはすべて必要です。
開発証明書を生成する
パッケージ化する前に、署名用の開発証明書が必要です。 まだ生成していない場合は生成します。
winapp cert generate --if-exists skip
署名とパッケージ
これで、パッケージ化と署名を行うことができます。
winapp pack .\dist --cert .\devcert.pfx
注:
packコマンドは、現在のディレクトリのPackage.appxmanifestを自動的に使用し、パッケージ化する前にターゲット フォルダーにコピーします。
証明書をインストールする
MSIX パッケージをインストールする前に、コンピューター上の開発証明書を信頼する必要があります。 このコマンドを管理者として実行します (証明書ごとに 1 回だけ実行する必要があります)。
winapp cert install .\devcert.pfx
インストールと実行
ヒント
手順 5 で winapp run を使用した場合は、パッケージがシステムに既に登録されている可能性があります。 最初 winapp unregister 使用して開発登録を削除してから、リリース パッケージをインストールします。
生成された .msix ファイルをダブルクリックするか、PowerShell を使用してパッケージをインストールします。
Add-AppxPackage .\flutterapp.msix
ヒント
MSIX ファイル名には、バージョンとアーキテクチャ (例: flutterapplication1_1.0.0.0_x64.msix) が含まれます。 ディレクトリで正確なファイル名を確認します。 コードの変更後に再パッケージ化する必要がある場合は、Version で Package.appxmanifest をインクリメントします。インストールされているパッケージを更新するには、Windows ではより大きなバージョン番号が必要です。
ヒント
- 配布の準備ができたら、証明機関のコード署名証明書を使用して MSIX に署名し、ユーザーが自己署名証明書をインストールする必要がないようにすることができます。
- Azure の信頼された署名 サービスは、証明書を安全に管理し、CI/CD パイプラインへの署名を統合するための優れた方法です。
- Microsoft Storeは MSIX に署名します。送信前に署名する必要はありません。
次のステップ
winget を使用して配布: MSIX をWindows パッケージ マネージャー Community Repository に送信しますMicrosoft Store に発行する :を使用してパッケージを送信する -
CI/CD の設定:
setup-WinAppCliGitHub Action を使用して、パイプライン内のパッケージングを自動化します Explore Windows API : パッケージ ID を使用して、 これで、Notifications 、on-device AI 、およびその他のidentity 依存 API
Windows developer