Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Untuk contoh kerja lengkap, lihat sampel Flutter di repositori ini.
Panduan ini menunjukkan cara menggunakan winapp CLI dengan aplikasi Flutter untuk menambahkan identitas paket dan mengemas aplikasi Anda sebagai MSIX.
Identitas paket adalah konsep inti dalam model Windows app. Ini memungkinkan aplikasi Anda untuk mengakses API Windows tertentu (seperti Notifikasi, Keamanan, API AI, dll), memiliki pengalaman penginstalan/penghapusan instalasi yang bersih, dan banyak lagi.
Build standar Flutter untuk Windows tidak memiliki identitas paket. Panduan ini menunjukkan cara menambahkannya untuk penelusuran kesalahan lalu mengemasnya untuk didistribusikan.
Prasyarat
Flutter SDK: Instal Flutter mengikuti panduan resmi.
winapp CLI: Instal
winappCLI melalui winget (atau perbarui jika sudah diinstal):winget install Microsoft.winappcli --source winget
1. Buat Aplikasi Flutter Baru
Ikuti panduan di dokumen Flutter resmi untuk membuat aplikasi baru dan menjalankannya.
Anda seharusnya melihat aplikasi penghitung Flutter bawaan.
2. Perbarui Kode untuk Memeriksa Identitas
Kami akan memperbarui aplikasi untuk memeriksa apakah aplikasi berjalan dengan identitas paket. Kami akan menggunakan Dart FFI untuk memanggil API Windows GetCurrentPackageFamilyName.
Pertama, tambahkan ffi paket:
flutter pub add ffi
Selanjutnya, ganti konten lib/main.dart dengan kode berikut. Kode ini mencoba mengambil identitas paket saat ini menggunakan API Windows. Jika berhasil, maka akan menampilkan Nama Keluarga Paket di UI; jika tidak, ini menunjukkan "Tidak dibungkus".
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. Jalankan Tanpa Identitas
Sekarang, bangun dan jalankan aplikasi seperti biasa:
flutter build windows
Jalankan executable secara langsung (ganti flutter_app dengan nama proyek Anda jika berbeda):
.\build\windows\x64\runner\Release\flutter_app.exe
Tip
Hasil build berada di folder x64 tanpa memandang arsitektur komputer Anda — ini diharapkan untuk build Windows Flutter.
Anda akan melihat aplikasi dengan indikator oranye "Tidak dikemas". Ini mengonfirmasi bahwa executable standar berjalan tanpa identitas paket apa pun.
4. Inisialisasi Project dengan winapp CLI
Perintah winapp init menyiapkan semua yang Anda butuhkan dalam sekali jalan: manifes aplikasi, aset, dan secara opsional SDK Aplikasi Windows header untuk pengembangan C++. Manifes menentukan identitas aplikasi Anda (nama, penerbit, versi) yang Windows gunakan untuk memberikan akses API.
Jalankan perintah berikut dan ikuti perintah:
winapp init
Ketika diminta:
- Nama paket: Tekan Enter untuk menerima default (berasal dari nama proyek Anda)
- Nama penerbit: Tekan Enter untuk menerima pengaturan standar atau masukkan nama Anda
- Versi: Tekan Enter untuk menerima 1.0.0.0
- Description: Tekan Enter untuk menerima default (Aplikasi Windows)
- Setup SDK: Pilih "SDK Stabil" untuk mengunduh SDK Aplikasi Windows dan menghasilkan header C++ (diperlukan untuk langkah 6)
Perintah ini akan:
- Buat
Package.appxmanifest— manifes yang menentukan identitas aplikasi Anda - Buat
Assetsfolder — ikon yang diperlukan untuk pengemasan MSIX dan pengiriman Store - Buat folder
.winappdengan header dan pustaka SDK Aplikasi Windows - Membuat
winapp.yamlfile konfigurasi untuk menyematkan versi SDK
Anda dapat membuka Package.appxmanifest untuk menyesuaikan properti lebih lanjut seperti nama tampilan, penerbit, dan kemampuan.
5. Debugging dengan Identifikasi
Untuk menguji fitur yang memerlukan identitas (seperti Pemberitahuan) tanpa sepenuhnya mengemas aplikasi, Anda dapat menggunakan winapp run. Ini mendaftarkan paket tata letak tidak terikat (sama seperti penginstalan MSIX nyata) dan memulai aplikasi dalam satu langkah. Tidak diperlukan sertifikat atau penandatanganan untuk debug.
Buat aplikasi:
flutter build windowsJalankan dengan identitas:
winapp run .\build\windows\x64\runner\Release
Tip
winapp run juga mendaftarkan paket pada sistem Anda. Inilah sebabnya mengapa MSIX mungkin muncul sebagai "sudah diinstal" ketika Anda mencoba menginstalnya nanti di langkah 7. Gunakan winapp unregister untuk membersihkan paket pengembangan setelah selesai.
Anda sekarang akan melihat aplikasi dengan indikator hijau yang menunjukkan:
Package Family Name: flutterapp.debug_xxxxxxxx
Ini mengonfirmasi bahwa aplikasi Anda berjalan dengan identitas paket yang valid!
Tip
Untuk alur kerja penelusuran kesalahan tingkat lanjut (melampirkan debugger, penyiapan IDE, penelusuran kesalahan startup), lihat Panduan Penelusuran Kesalahan.
6. Menggunakan SDK Aplikasi Windows (Opsional)
Jika Anda memilih untuk mengatur SDK selama winapp init, Anda sekarang memiliki akses ke header C++ SDK Aplikasi Windows di folder .winapp/include. Karena runner Windows Flutter adalah C++, Anda dapat memanggil API SDK Aplikasi Windows dari kode asli dan mengeksposnya ke Dart melalui saluran metode. Jika Anda hanya memerlukan identitas paket untuk distribusi, Anda dapat melompat ke langkah 7.
Mari kita tambahkan contoh sederhana yang menampilkan versi runtime Windows App.
Membuat Plugin Asli
Buat 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_
Buat 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();
}
Memperbarui CMakeLists.txt
Edit windows/runner/CMakeLists.txt untuk membuat tiga perubahan.
add_executable Temukan blok dan tambahkan "winapp_sdk_plugin.cpp" ke daftar file sumber:
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"
)
Kemudian tambahkan dua baris ini di akhir file untuk menautkan pustaka WinRT dan sertakan header SDK Aplikasi 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")
Daftarkan Plugin
Di windows/runner/flutter_window.cpp, tambahkan include di bagian atas file bersama dengan include lainnya:
#include "winapp_sdk_plugin.h"
Kemudian temukan RegisterPlugins panggilan masuk FlutterWindow::OnCreate() dan tambahkan RegisterWinAppSdkPlugin di baris tepat setelah itu:
RegisterPlugins(flutter_controller_->engine());
RegisterWinAppSdkPlugin(flutter_controller_->engine()); // <-- add this line
Memperbarui main.dart
Tambahkan impor berikut di bagian lib/main.dartatas , bersama dengan impor yang ada:
import 'package:flutter/services.dart';
Tambahkan fungsi ini di bawah fungsi yang ada getPackageFamilyName() (di luar kelas apa pun):
/// 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;
}
}
Di dalam kelas _MyHomePageState, tambahkan bidang baru di sebelah _packageFamilyName yang sudah ada.
late final String? _packageFamilyName;
String? _runtimeVersion; // <-- add this line
Perbarui initState() untuk memanggil fungsi baru:
@override
void initState() {
super.initState();
_packageFamilyName = getPackageFamilyName();
// Fetch the runtime version asynchronously
getWindowsAppRuntimeVersion().then((version) {
setState(() {
_runtimeVersion = version;
});
});
}
Terakhir, tampilkan versi runtime di dalam metode build. Tambahkan widget ini dalam daftar anak Column, tepat setelah Container yang menunjukkan identitas paket.
if (_runtimeVersion != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'Windows App Runtime: $_runtimeVersion',
style: Theme.of(context).textTheme.bodyLarge,
),
),
Kompilasi dan Jalankan
Membangun kembali aplikasi:
flutter build windows
winapp run .\build\windows\x64\runner\Release
Anda sekarang akan melihat output seperti:
Package Family Name: flutterapp.debug_xxxxxxxx
Windows App Runtime: 8000.731.1532.0
Direktori .winapp/include berisi semua header yang diperlukan untuk SDK Aplikasi Windows, termasuk:
-
winrt/- Header proyeksi WinRT C++ untuk akses ke API Windows Runtime -
Microsoft.UI.*.h- Header WinUI 3 untuk komponen UI modern -
MddBootstrap.h- Pemulaian SDK Aplikasi Windows -
WindowsAppSDK-VersionInfo.h- Informasi versi - Dan banyak lagi komponen SDK Aplikasi Windows
Untuk penggunaan SDK Aplikasi Windows yang lebih canggih, lihat dokumentasi SDK Aplikasi Windows.
7. Paket dengan MSIX
Setelah siap mendistribusikan aplikasi, Anda dapat mengemasnya sebagai MSIX menggunakan manifes yang sama.
Menyiapkan Direktori Paket
Pertama, buat aplikasi Anda dalam mode rilis:
flutter build windows
Kemudian, buat direktori dengan file rilis Anda:
mkdir dist
copy .\build\windows\x64\runner\Release\* .\dist\ -Recurse
Output build Flutter Windows mencakup file executable, flutter_windows.dll, dan folder data — yang semuanya diperlukan.
Membuat Sertifikat Pengembangan
Sebelum pengemasan, Anda memerlukan sertifikat pengembangan untuk penandatanganan. Hasilkan satu jika Anda belum melakukannya.
winapp cert generate --if-exists skip
Tanda tangani dan Kemas
Sekarang Anda dapat mengemas dan menandatangani:
winapp pack .\dist --cert .\devcert.pfx
Catatan: Perintah
packsecara otomatis menggunakanPackage.appxmanifestpada direktori saat ini Anda dan menyalinnya ke folder target sebelum pengemasan.
Menginstal Sertifikat
Sebelum Anda dapat menginstal paket MSIX, Anda perlu mempercayai sertifikat pengembangan pada komputer Anda. Jalankan perintah ini sebagai administrator (Anda hanya perlu melakukan ini sekali per sertifikat):
winapp cert install .\devcert.pfx
Instal dan Jalankan
Tip
Jika Anda menggunakan winapp run di langkah 5, paket mungkin sudah terdaftar di sistem Anda. Gunakan winapp unregister terlebih dahulu untuk menghapus pendaftaran pengembangan, lalu instal paket rilis.
Instal paket dengan mengeklik dua kali file yang dihasilkan .msix , atau menggunakan PowerShell:
Add-AppxPackage .\flutterapp.msix
Tip
Nama file MSIX mencakup versi dan arsitektur (misalnya, flutterapplication1_1.0.0.0_x64.msix). Periksa direktori Anda untuk nama file yang tepat. Jika Anda perlu mengemas ulang setelah perubahan kode, tingkatkan Version di Package.appxmanifest — Windows memerlukan nomor versi yang lebih tinggi untuk memperbarui paket yang diinstal.
Tips
- Setelah siap untuk didistribusikan, Anda dapat menandatangani MSIX dengan sertifikat penandatanganan kode dari Otoritas Sertifikat sehingga pengguna Anda tidak perlu menginstal sertifikat yang ditandatangani sendiri.
- Layanan Penandatanganan Tepercaya Azure adalah cara yang bagus untuk mengelola sertifikat Anda dengan aman dan mengintegrasikan penandatanganan ke dalam alur CI/CD Anda.
- Microsoft Store akan menandatangani MSIX untuk Anda, tidak perlu menandatangani sebelum pengiriman.
Langkah Selanjutnya
- Distribute melalui winget: Kirimkan MSIX Anda ke Repositori Komunitas Windows Package Manager
-
Publish ke Microsoft Store: Gunakan
winapp storeuntuk mengirimkan paket Anda -
Siapkan CI/CD: Gunakan Tindakan
setup-WinAppCliGitHub untuk mengotomatiskan kemasan di alur Anda - Explore Windows API: Dengan identitas paket, Anda sekarang dapat menggunakan Notifications, on-device AI, dan API identity-dependent lainnya
Windows developer