ecard download for android

This commit is contained in:
Surendiran 2026-05-28 15:49:09 +05:30
parent 653316dfa1
commit cbb2f9f33d
22 changed files with 627 additions and 37 deletions

View File

@ -41,6 +41,7 @@ android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
coreLibraryDesugaringEnabled true
}
kotlinOptions {
@ -133,4 +134,5 @@ dependencies {
implementation "com.google.firebase:firebase-messaging:24.1.1"
implementation("com.google.firebase:firebase-appcheck-playintegrity")
implementation 'androidx.browser:browser:1.3.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
}

View File

@ -1,9 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="nh.ind.nhance.benefits">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="nh.ind.nhance.benefits">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>

View File

@ -1,5 +1,32 @@
package nh.ind.nhance.benefits
import android.media.MediaScannerConnection
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterFragmentActivity()
class MainActivity: FlutterFragmentActivity() {
private val mediaScanChannel = "nhance/media_scan"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, mediaScanChannel)
.setMethodCallHandler { call, result ->
if (call.method == "scanFile") {
val path = call.argument<String>("path")
if (path.isNullOrBlank()) {
result.error("invalid_path", "Path is required", null)
return@setMethodCallHandler
}
MediaScannerConnection.scanFile(
applicationContext,
arrayOf(path),
null
) { _, _ -> }
result.success(true)
} else {
result.notImplemented()
}
}
}
}

View File

@ -40,6 +40,7 @@ import 'package:nhance_app_pwa/pages/session/SetPinBiometric.dart';
import 'package:nhance_app_pwa/pages/session/changePin.dart';
import 'package:nhance_app_pwa/pages/session/settingUpPinAndBiometric.dart';
import 'package:nhance_app_pwa/pages/setPassword.dart';
import 'package:nhance_app_pwa/pages/helpers/app_permission_service.dart';
import 'package:nhance_app_pwa/pages/verify.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -207,6 +208,7 @@ void _applyAppFlavorFromDartDefine() {
Future<void> startApp() async {
WidgetsFlutterBinding.ensureInitialized();
_applyAppFlavorFromDartDefine();
await AppPermissionService.requestInitialPermissions();
await TokenService.clearLegacyWebAuthFromSharedPreferences();

View File

@ -0,0 +1,16 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
class AppPermissionService {
AppPermissionService._();
static Future<void> requestInitialPermissions() async {
if (kIsWeb || !Platform.isAndroid) return;
await Permission.notification.request();
// Keep app-open flow lightweight: no redirection to Android settings page.
await Permission.storage.request();
}
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
class CustomDownloadSnackbar {
static void show(
BuildContext context, {
required String message,
Duration duration = const Duration(seconds: 2),
}) {
final messenger = ScaffoldMessenger.of(context);
messenger
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(
message,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
behavior: SnackBarBehavior.floating,
margin: const EdgeInsets.fromLTRB(16, 0, 16, 24),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
duration: duration,
backgroundColor: const Color(0xFF1F1F1F),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
dismissDirection: DismissDirection.down,
),
);
}
}

View File

@ -0,0 +1,23 @@
class DownloadResult {
final bool success;
final String? message;
final String? savedPath;
const DownloadResult({
required this.success,
this.message,
this.savedPath,
});
factory DownloadResult.success({String? message, String? savedPath}) {
return DownloadResult(
success: true,
message: message ?? 'Download complete',
savedPath: savedPath,
);
}
factory DownloadResult.failure(String message) {
return DownloadResult(success: false, message: message);
}
}

View File

@ -0,0 +1,72 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:open_filex/open_filex.dart';
class EcardDownloadNotificationService {
EcardDownloadNotificationService._();
static final FlutterLocalNotificationsPlugin _plugin =
FlutterLocalNotificationsPlugin();
static bool _initialized = false;
static Future<void> ensureInitialized() async {
if (_initialized || kIsWeb) return;
const androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings();
const settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await _plugin.initialize(
settings,
onDidReceiveNotificationResponse: (NotificationResponse response) async {
final filePath = response.payload;
if (filePath == null || filePath.isEmpty) return;
await OpenFilex.open(filePath);
},
);
await _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
await _plugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(alert: true, badge: true, sound: true);
_initialized = true;
}
static Future<void> showDownloadCompleted({
required String filePath,
required String fileName,
}) async {
if (kIsWeb) return;
await ensureInitialized();
const androidDetails = AndroidNotificationDetails(
'ecard_downloads_channel',
'E-Card Downloads',
channelDescription: 'Notifications for completed eCard downloads',
importance: Importance.max,
priority: Priority.high,
styleInformation: DefaultStyleInformation(true, true),
);
const iosDetails = DarwinNotificationDetails();
await _plugin.show(
DateTime.now().millisecondsSinceEpoch ~/ 1000,
'eCard downloaded',
'Tap to open $fileName',
const NotificationDetails(
android: androidDetails,
iOS: iosDetails,
),
payload: filePath,
);
}
}

View File

@ -0,0 +1,24 @@
import 'download_result.dart';
import 'ecard_download_service_stub.dart'
if (dart.library.io) 'ecard_download_service_io.dart'
if (dart.library.html) 'ecard_download_service_web.dart' as impl;
class EcardDownloadService {
const EcardDownloadService();
Future<bool> ensureStorageAccess() {
return impl.ensureStorageAccessImpl();
}
Future<DownloadResult> downloadEcard({
required String url,
required String fileName,
Map<String, String>? headers,
}) {
return impl.downloadEcardImpl(
url: url,
fileName: fileName,
headers: headers,
);
}
}

View File

@ -0,0 +1,116 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/services.dart';
import 'download_result.dart';
final Dio _dio = Dio();
const MethodChannel _mediaScanChannel = MethodChannel('nhance/media_scan');
Future<bool> ensureStorageAccessImpl() async {
if (Platform.isAndroid) {
// Request notification permission (best-effort for Android 13+).
await Permission.notification.request();
// Prefer broad access for writing to public Download directory.
if (await Permission.manageExternalStorage.isGranted) return true;
final manage = await Permission.manageExternalStorage.request();
if (manage.isGranted) return true;
// Fallback for older devices.
final storage = await Permission.storage.request();
return storage.isGranted;
}
return true;
}
Future<DownloadResult> downloadEcardImpl({
required String url,
required String fileName,
Map<String, String>? headers,
}) async {
try {
final hasPermission = await ensureStorageAccessImpl();
if (!hasPermission) {
return DownloadResult.failure(
'Storage permission required',
);
}
final Directory downloadDir = await _resolveDownloadDirectory();
if (!await downloadDir.exists()) {
await downloadDir.create(recursive: true);
}
final safeFileName = _sanitizeFileName(fileName);
final filePath = '${downloadDir.path}/$safeFileName';
final file = File(filePath);
if (await file.exists()) {
await file.delete();
}
await _dio.download(
url,
filePath,
options: Options(
headers: headers,
responseType: ResponseType.bytes,
),
deleteOnError: true,
);
if (!await file.exists()) {
return DownloadResult.failure('Download failed');
}
final length = await file.length();
if (length == 0) {
await file.delete();
return DownloadResult.failure('Download failed');
}
await _scanFileForAndroidMediaIndex(filePath);
return DownloadResult.success(
savedPath: filePath,
message: 'File saved under Download/$safeFileName',
);
} on FileSystemException {
return DownloadResult.failure('Storage permission issue');
} on DioException catch (e) {
final hasStatusCode = e.response?.statusCode != null;
final message = hasStatusCode
? 'Download failed (${e.response!.statusCode})'
: 'Download failed';
return DownloadResult.failure(message);
} catch (_) {
return DownloadResult.failure('Download failed');
}
}
String _sanitizeFileName(String fileName) {
final replaced = fileName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_').trim();
if (replaced.isEmpty) return 'ecard.pdf';
return replaced.toLowerCase().endsWith('.pdf') ? replaced : '$replaced.pdf';
}
Future<Directory> _resolveDownloadDirectory() async {
if (Platform.isAndroid) {
final dir = Directory('/storage/emulated/0/Download');
if (!await dir.exists()) {
await dir.create(recursive: true);
}
return dir;
}
return getApplicationDocumentsDirectory();
}
Future<void> _scanFileForAndroidMediaIndex(String filePath) async {
if (!Platform.isAndroid) return;
try {
await _mediaScanChannel.invokeMethod<void>('scanFile', {'path': filePath});
} catch (_) {
// Non-fatal: file is already saved even if scan fails.
}
}

View File

@ -0,0 +1,11 @@
import 'download_result.dart';
Future<bool> ensureStorageAccessImpl() async => true;
Future<DownloadResult> downloadEcardImpl({
required String url,
required String fileName,
Map<String, String>? headers,
}) async {
return DownloadResult.failure('Download is not supported on this platform');
}

View File

@ -0,0 +1,18 @@
import 'download_result.dart';
import 'file_download_helper.dart';
Future<bool> ensureStorageAccessImpl() async => true;
Future<DownloadResult> downloadEcardImpl({
required String url,
required String fileName,
Map<String, String>? headers,
}) async {
// Keep existing web flow unchanged.
return downloadFileFromUrl(
url,
fileName: fileName,
headers: headers,
openAfterDownload: false,
);
}

View File

@ -0,0 +1,10 @@
String fileNameFromUrl(String url, {String defaultName = 'ecard.pdf'}) {
try {
final segment = Uri.parse(url).pathSegments.lastWhere(
(s) => s.isNotEmpty,
orElse: () => '',
);
if (segment.contains('.')) return Uri.decodeComponent(segment);
} catch (_) {}
return defaultName;
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'download_result.dart';
import 'file_download_stub.dart'
if (dart.library.io) 'file_download_io.dart'
if (dart.library.html) 'file_download_web.dart' as impl;
export 'download_result.dart';
/// Downloads without opening a browser tab.
/// Web: saves via browser download. Mobile: saves locally and opens the
/// system file viewer (not an in-app WebView or browser tab).
Future<DownloadResult> downloadFileFromUrl(
String url, {
String? fileName,
Map<String, String>? headers,
bool openAfterDownload = true,
}) {
return impl.downloadFileFromUrlImpl(
url,
fileName: fileName,
headers: headers,
openAfterDownload: kIsWeb ? false : openAfterDownload,
);
}

View File

@ -0,0 +1,52 @@
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'download_result.dart';
import 'file_download_common.dart';
Future<DownloadResult> downloadFileFromUrlImpl(
String url, {
String? fileName,
Map<String, String>? headers,
bool openAfterDownload = false,
}) async {
try {
final response = await http.get(Uri.parse(url), headers: headers);
if (response.statusCode != 200) {
return DownloadResult.failure(
'Download failed (${response.statusCode})',
);
}
final name = fileName ?? fileNameFromUrl(url);
final dir = await _resolveSaveDirectory();
final file = File('${dir.path}/$name');
await file.writeAsBytes(response.bodyBytes, flush: true);
if (openAfterDownload) {
await OpenFilex.open(file.path);
}
return DownloadResult.success(
savedPath: file.path,
message: openAfterDownload ? 'E-Card downloaded' : 'E-Card saved',
);
} catch (e) {
return DownloadResult.failure('Could not download file');
}
}
Future<Directory> _resolveSaveDirectory() async {
if (Platform.isAndroid) {
final downloads = await getDownloadsDirectory();
if (downloads != null) return downloads;
}
if (Platform.isIOS) {
return getApplicationDocumentsDirectory();
}
final downloads = await getDownloadsDirectory();
return downloads ?? await getApplicationDocumentsDirectory();
}

View File

@ -0,0 +1,10 @@
import 'download_result.dart';
Future<DownloadResult> downloadFileFromUrlImpl(
String url, {
String? fileName,
Map<String, String>? headers,
bool openAfterDownload = false,
}) async {
return DownloadResult.failure('Download is not supported on this platform');
}

View File

@ -0,0 +1,48 @@
import 'dart:html' as html;
import 'package:http/http.dart' as http;
import 'download_result.dart';
import 'file_download_common.dart';
Future<DownloadResult> downloadFileFromUrlImpl(
String url, {
String? fileName,
Map<String, String>? headers,
bool openAfterDownload = false,
}) async {
try {
final name = fileName ?? fileNameFromUrl(url);
final response = await http.get(Uri.parse(url), headers: headers);
if (response.statusCode != 200) {
return DownloadResult.failure(
'Download failed (${response.statusCode})',
);
}
final blob = html.Blob([response.bodyBytes]);
final objectUrl = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.AnchorElement(href: objectUrl)
..download = name
..style.display = 'none';
html.document.body?.append(anchor);
anchor.click();
anchor.remove();
html.Url.revokeObjectUrl(objectUrl);
return DownloadResult.success(message: 'E-Card downloaded');
} catch (_) {
try {
final name = fileName ?? fileNameFromUrl(url);
final anchor = html.AnchorElement(href: url)
..download = name
..style.display = 'none';
html.document.body?.append(anchor);
anchor.click();
anchor.remove();
return DownloadResult.success(message: 'E-Card download started');
} catch (e) {
return DownloadResult.failure('Could not download file');
}
}
}

View File

@ -11,6 +11,9 @@ import 'package:nhance_app_pwa/pages/postEnrollment/service/svg_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../customAppBar/customFooter.dart';
import '../helpers/custom_download_snackbar.dart';
import '../helpers/ecard_download_service.dart';
import '../helpers/ecard_download_notification_service.dart';
import '../../customAppBar/responsive.dart';
import '../../customAppBar/tabs.dart';
import '../../customAppBar/toastHelper.dart';
@ -33,6 +36,7 @@ class policies extends StatefulWidget {
class _policiesState extends State<policies> {
late ApiService apiService;
final EcardDownloadService _ecardDownloadService = const EcardDownloadService();
final session = SessionManager();
int _currentIndex = 0;
bool isActive = true;
@ -52,6 +56,7 @@ class _policiesState extends State<policies> {
dynamic argumentsData;
bool ECardHide = true;
bool _isExpanded = false;
bool _isDownloadingEcard = false;
void _onTabChanged(int index) {
setState(() {
@ -70,6 +75,12 @@ class _policiesState extends State<policies> {
super.initState();
apiService = ApiService(context); // Initialize ApiService here
_loadToken();
EcardDownloadNotificationService.ensureInitialized();
if (!kIsWeb) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _ecardDownloadService.ensureStorageAccess();
});
}
}
@override
@ -80,6 +91,7 @@ class _policiesState extends State<policies> {
Future<void> _loadToken() async {
logDebug('_loadToken');
final String? token = await TokenService.getPostToken();
_token = token;
final SharedPreferences prefs = await SharedPreferences.getInstance();
if (token != null && token.isNotEmpty) {
// Decode the JWT token received from the API response
@ -94,37 +106,105 @@ class _policiesState extends State<policies> {
}
Future<void> getEcardDownload(id) async {
final clientPolicyID = widget.arguments?['client_policy_id'];
final policyNo = widget.arguments?['policy_no'];
final eCarDParams = {
// 'id': empPrimaryId,
'id': id,
'emp_code': empCodeString,
'client_policy_id': clientPolicyID,
'policy_no': policyNo
};
final response = await apiService.getEcardRequest(eCarDParams);
logDebug('check 1');
final ecardDownloadUrl = response['data']['eCardDownload'];
final message = response['data']['message'];
if (ecardDownloadUrl != null) {
logDebug('✅ Link: $ecardDownloadUrl');
await _launchURL(ecardDownloadUrl,context); // Only launch if status is success
// ToastHelper.showSuccessToast(context, message);
} else {
logDebug('❌ Error: $message');
ToastHelper.showErrorToast(context, message);
}
}
if (_isDownloadingEcard) return;
if (!mounted) return;
setState(() => _isDownloadingEcard = true);
Future<void> _launchURL(String url, BuildContext context) async {
logDebug('url $url');
try {
final Uri uri = Uri.parse(url);
await launchUrl(uri, mode: LaunchMode.externalApplication);
final clientPolicyID = widget.arguments?['client_policy_id'];
final policyNo = widget.arguments?['policy_no'];
final eCarDParams = {
'id': id,
'emp_code': empCodeString,
'client_policy_id': clientPolicyID,
'policy_no': policyNo,
};
final response = await apiService.getEcardRequest(eCarDParams);
if (!mounted) return;
final data = response['data'];
if (data is! Map) {
ToastHelper.showErrorToast(context, 'Unable to download E-Card');
return;
}
final ecardDownloadUrl = data['eCardDownload'];
final message = data['message']?.toString();
if (ecardDownloadUrl == null || ecardDownloadUrl.toString().isEmpty) {
logDebug('❌ Error: $message');
ToastHelper.showErrorToast(
context,
message ?? 'E-Card is not available',
);
return;
}
final url = ecardDownloadUrl.toString();
logDebug('✅ E-Card URL: $url');
final safePolicyNo = policyNo?.toString().replaceAll(RegExp(r'[^\w\-.]'), '_');
final fileName = safePolicyNo == null || safePolicyNo.isEmpty
? 'ecard.pdf'
: 'ecard_$safePolicyNo.pdf';
if (!kIsWeb) {
CustomDownloadSnackbar.show(
context,
message: 'Downloading eCard...',
);
}
final result = await _ecardDownloadService.downloadEcard(
url: url,
fileName: fileName,
headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
if (_token != null && _token.toString().isNotEmpty)
'Authorization': 'Bearer ${_token.toString()}',
},
);
if (!mounted) return;
if (result.success) {
if (kIsWeb) {
ToastHelper.showSuccessToast(
context,
result.message ?? 'E-Card downloaded',
);
} else {
CustomDownloadSnackbar.show(
context,
message: result.message ?? 'eCard downloaded successfully',
);
if (result.savedPath != null && result.savedPath!.isNotEmpty) {
await EcardDownloadNotificationService.showDownloadCompleted(
filePath: result.savedPath!,
fileName: fileName,
);
}
}
} else {
if (kIsWeb) {
ToastHelper.showErrorToast(
context,
result.message ?? 'Could not download E-Card',
);
} else {
CustomDownloadSnackbar.show(
context,
message: result.message ?? 'Download failed',
);
}
}
} catch (e) {
logDebug('Could not launch URL: $e');
logDebug('E-Card download error: $e');
if (mounted) {
ToastHelper.showErrorToast(
context,
'Unable to download E-Card. Please try again.',
);
}
} finally {
if (mounted) setState(() => _isDownloadingEcard = false);
}
}
@ -151,12 +231,15 @@ class _policiesState extends State<policies> {
}
}
void _openUrl(String url) async {
if (url.isNotEmpty && await canLaunch(url)) {
await launch(url,
webOnlyWindowName: '_blank'); // Opens in a new tab (web only)
Future<void> _openUrl(String url) async {
final uri = Uri.tryParse(url);
if (uri == null) {
logDebug('Could not launch URL');
return;
}
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
// Handle invalid or null URL
logDebug('Could not launch URL');
}
}
@ -402,7 +485,9 @@ class _policiesState extends State<policies> {
? 200
: 180,
child: TextButton(
onPressed: () {
onPressed: _isDownloadingEcard
? null
: () {
getEcardDownload(empPrimaryId);
// if (argumentsData['eCardDownload'] !=
// null) {
@ -703,7 +788,9 @@ class _policiesState extends State<policies> {
child: MouseRegion(
cursor: SystemMouseCursors.click, // Web pointer
child: GestureDetector(
onTap: () {
onTap: _isDownloadingEcard
? null
: () {
getEcardDownload(detail['id']);
},
child: Tooltip(

View File

@ -8,6 +8,7 @@ import Foundation
import file_picker
import file_selector_macos
import flutter_inappwebview_macos
import flutter_local_notifications
import flutter_secure_storage_macos
import google_sign_in_ios
import local_auth_darwin
@ -24,6 +25,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))

View File

@ -75,6 +75,10 @@ dependencies:
ribbon_widget: ^1.0.5
flutter_html: ^3.0.0
video_player: ^2.10.1
path_provider: ^2.1.5
open_filex: ^4.7.0
flutter_local_notifications: ^19.4.2
permission_handler: ^12.0.1
dev_dependencies:
flutter_test:

View File

@ -10,6 +10,7 @@
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <local_auth_windows/local_auth_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <printing/printing_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
@ -22,6 +23,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
LocalAuthPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
PrintingPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PrintingPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -7,11 +7,13 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_inappwebview_windows
flutter_secure_storage_windows
local_auth_windows
permission_handler_windows
printing
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_local_notifications_windows
)
set(PLUGIN_BUNDLED_LIBRARIES)