Compare commits
2 Commits
1bf59e02cc
...
191274c65a
| Author | SHA1 | Date |
|---|---|---|
|
|
191274c65a | |
|
|
ab18b544c2 |
|
|
@ -9,6 +9,8 @@ import 'providers/theme_provider.dart';
|
||||||
import 'screens/main_screen.dart';
|
import 'screens/main_screen.dart';
|
||||||
import 'screens/license_screen.dart';
|
import 'screens/license_screen.dart';
|
||||||
import 'services/migration_service.dart';
|
import 'services/migration_service.dart';
|
||||||
|
import 'services/license_service.dart';
|
||||||
|
import 'providers/license_provider.dart';
|
||||||
|
|
||||||
/// 店舗向けビルドかどうかを判定するビルド時フラグ
|
/// 店舗向けビルドかどうかを判定するビルド時フラグ
|
||||||
///
|
///
|
||||||
|
|
@ -74,9 +76,16 @@ void main() async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ちらつき防止: runApp の前にキャッシュ済みライセンス状態を取得し、
|
||||||
|
// licenseStatusProvider が loading を経由せず即 AsyncData になるよう override する
|
||||||
|
final cachedLicenseStatus = await LicenseService.getCachedStatusOnly();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
const ProviderScope(
|
ProviderScope(
|
||||||
child: MyApp(),
|
overrides: [
|
||||||
|
licenseInitialStatusProvider.overrideWithValue(cachedLicenseStatus),
|
||||||
|
],
|
||||||
|
child: const MyApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,52 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../services/license_service.dart';
|
import '../services/license_service.dart';
|
||||||
|
|
||||||
/// ライセンス状態の非同期プロバイダー
|
/// 起動時にキャッシュから事前ロードした値を保持するプロバイダー
|
||||||
///
|
///
|
||||||
/// アプリ起動時に一度だけVPSに問い合わせ、結果をキャッシュする。
|
/// main() で override して初期値を渡すことで、
|
||||||
/// 手動更新は [licenseStatusProvider].invalidate() を呼ぶ。
|
/// licenseStatusProvider が loading 状態を経由せず即座に AsyncData になる。
|
||||||
final licenseStatusProvider = FutureProvider<LicenseStatus>((ref) async {
|
final licenseInitialStatusProvider = Provider<LicenseStatus?>((ref) => null);
|
||||||
|
|
||||||
|
/// ライセンス状態プロバイダー
|
||||||
|
///
|
||||||
|
/// - build() は licenseInitialStatusProvider に値がある場合は同期で返す(ちらつきなし)
|
||||||
|
/// - 同時にバックグラウンドでサーバー検証を実行し、差異があれば状態を更新する
|
||||||
|
final licenseStatusProvider =
|
||||||
|
AsyncNotifierProvider<LicenseStatusNotifier, LicenseStatus>(
|
||||||
|
LicenseStatusNotifier.new,
|
||||||
|
);
|
||||||
|
|
||||||
|
class LicenseStatusNotifier extends AsyncNotifier<LicenseStatus> {
|
||||||
|
@override
|
||||||
|
FutureOr<LicenseStatus> build() {
|
||||||
|
final initial = ref.read(licenseInitialStatusProvider);
|
||||||
|
if (initial != null) {
|
||||||
|
// キャッシュ値を同期で返すことで loading 状態をスキップ
|
||||||
|
_refreshFromServer();
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
// override なし(テスト等): 通常フロー
|
||||||
return LicenseService.checkStatus();
|
return LicenseService.checkStatus();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/// バックグラウンドでサーバー検証を実行し、変化があれば状態を更新する
|
||||||
|
Future<void> _refreshFromServer() async {
|
||||||
|
try {
|
||||||
|
final fresh = await LicenseService.checkStatus();
|
||||||
|
if (state.hasValue && state.value != fresh) {
|
||||||
|
state = AsyncData(fresh);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// ネットワークエラーはキャッシュ値を維持
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Pro版かどうか(ナビゲーション・機能解放の分岐に使う)
|
/// Pro版かどうか(ナビゲーション・機能解放の分岐に使う)
|
||||||
|
///
|
||||||
|
/// licenseStatusProvider が同期で AsyncData を返すため、
|
||||||
|
/// アプリ起動時に false をちらつかせることなく正しい値を返す。
|
||||||
final isProProvider = Provider<bool>((ref) {
|
final isProProvider = Provider<bool>((ref) {
|
||||||
final statusAsync = ref.watch(licenseStatusProvider);
|
final statusAsync = ref.watch(licenseStatusProvider);
|
||||||
return statusAsync.maybeWhen(
|
return statusAsync.maybeWhen(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'device_service.dart';
|
import 'device_service.dart';
|
||||||
|
|
@ -22,43 +23,81 @@ enum LicenseStatus {
|
||||||
|
|
||||||
/// ライセンス管理サービス
|
/// ライセンス管理サービス
|
||||||
///
|
///
|
||||||
|
/// ## ストレージ方針
|
||||||
|
/// - ライセンスキー本体: flutter_secure_storage(暗号化)
|
||||||
|
/// - 状態キャッシュ: SharedPreferences(平文でもリスクなし)
|
||||||
|
///
|
||||||
/// ## 状態管理の優先順位
|
/// ## 状態管理の優先順位
|
||||||
/// 1. オンライン時: VPSに問い合わせた結果を使用 + SharedPreferencesにキャッシュ
|
/// 1. 起動時: SecureStorage のキャッシュを即時返却(ちらつき防止)
|
||||||
/// 2. オフライン時: SharedPreferencesのキャッシュを使用 (Pro状態を維持)
|
/// 2. バックグラウンド: VPS で再検証し、差異があれば状態を更新
|
||||||
|
/// 3. オフライン時: SharedPreferences のキャッシュを維持
|
||||||
///
|
///
|
||||||
/// ## ライセンスキー形式
|
/// ## ライセンスキー形式
|
||||||
/// PONSHU-XXXX-XXXX-XXXX (6バイト = 12hex文字, 大文字)
|
/// PONSHU-XXXX-XXXX-XXXX (6バイト = 12hex文字, 大文字)
|
||||||
class LicenseService {
|
class LicenseService {
|
||||||
static const _prefLicenseKey = 'ponshu_license_key';
|
static const _secureKeyName = 'ponshu_license_key';
|
||||||
static const _prefCachedStatus = 'ponshu_license_status_cache';
|
static const _prefCachedStatus = 'ponshu_license_status_cache';
|
||||||
static const _prefCachedAt = 'ponshu_license_cached_at';
|
static const _prefCachedAt = 'ponshu_license_cached_at';
|
||||||
static const _cacheValidSeconds = 24 * 60 * 60; // 24時間キャッシュ有効
|
static const _prefMigratedV1 = 'ponshu_license_migrated_v1';
|
||||||
|
static const _cacheValidSeconds = 24 * 60 * 60; // 24時間
|
||||||
|
|
||||||
|
static const _storage = FlutterSecureStorage(
|
||||||
|
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========== Migration ==========
|
||||||
|
|
||||||
|
/// SharedPreferences → flutter_secure_storage の一回限りのマイグレーション
|
||||||
|
static Future<void> _migrateIfNeeded() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
if (prefs.getBool(_prefMigratedV1) == true) return;
|
||||||
|
|
||||||
|
// 旧ストレージにキーがあれば移行して削除
|
||||||
|
final oldValue = prefs.getString(_secureKeyName);
|
||||||
|
if (oldValue != null && oldValue.isNotEmpty) {
|
||||||
|
await _storage.write(key: _secureKeyName, value: oldValue);
|
||||||
|
await prefs.remove(_secureKeyName);
|
||||||
|
debugPrint('[License] Migrated license key to secure storage.');
|
||||||
|
}
|
||||||
|
await prefs.setBool(_prefMigratedV1, true);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Public API ==========
|
// ========== Public API ==========
|
||||||
|
|
||||||
/// アプリ起動時に呼ぶ: ライセンス状態を確認して返す
|
/// キャッシュのみを即時返却(サーバー問い合わせなし)
|
||||||
static Future<LicenseStatus> checkStatus() async {
|
///
|
||||||
|
/// 起動時のちらつき防止用。main() で await してから runApp() に渡す。
|
||||||
|
static Future<LicenseStatus> getCachedStatusOnly() async {
|
||||||
|
await _migrateIfNeeded();
|
||||||
|
final savedKey = await _storage.read(key: _secureKeyName) ?? '';
|
||||||
|
if (savedKey.isEmpty) return LicenseStatus.free;
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final savedKey = prefs.getString(_prefLicenseKey) ?? '';
|
return _getCachedStatus(prefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// アプリ起動時(バックグラウンド): VPS でライセンス状態を検証して返す
|
||||||
|
static Future<LicenseStatus> checkStatus() async {
|
||||||
|
await _migrateIfNeeded();
|
||||||
|
final savedKey = await _storage.read(key: _secureKeyName) ?? '';
|
||||||
|
|
||||||
// ライセンスキーが保存済み → サーバーで検証
|
|
||||||
if (savedKey.isNotEmpty) {
|
if (savedKey.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final status = await _validateKeyWithServer(savedKey);
|
final status = await _validateKeyWithServer(savedKey);
|
||||||
if (status == LicenseStatus.offline) {
|
if (status == LicenseStatus.offline) {
|
||||||
// ネットワーク不通: キャッシュを上書きせずに返す
|
|
||||||
debugPrint('[License] Server unreachable, using cache');
|
debugPrint('[License] Server unreachable, using cache');
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
return _getCachedStatus(prefs);
|
return _getCachedStatus(prefs);
|
||||||
}
|
}
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await _cacheStatus(prefs, status);
|
await _cacheStatus(prefs, status);
|
||||||
return status;
|
return status;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('[License] Server unreachable, using cache: $e');
|
debugPrint('[License] Server unreachable, using cache: $e');
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
return _getCachedStatus(prefs);
|
return _getCachedStatus(prefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ライセンスキーなし → 無料版
|
|
||||||
return LicenseStatus.free;
|
return LicenseStatus.free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,8 +115,8 @@ class LicenseService {
|
||||||
final status = await _validateKeyWithServer(key);
|
final status = await _validateKeyWithServer(key);
|
||||||
|
|
||||||
if (status == LicenseStatus.pro) {
|
if (status == LicenseStatus.pro) {
|
||||||
|
await _storage.write(key: _secureKeyName, value: key);
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setString(_prefLicenseKey, key);
|
|
||||||
await _cacheStatus(prefs, LicenseStatus.pro);
|
await _cacheStatus(prefs, LicenseStatus.pro);
|
||||||
debugPrint('[License] Activated successfully.');
|
debugPrint('[License] Activated successfully.');
|
||||||
return (success: true, message: '');
|
return (success: true, message: '');
|
||||||
|
|
@ -96,14 +135,14 @@ class LicenseService {
|
||||||
|
|
||||||
/// ライセンスキーがローカルに保存されているか
|
/// ライセンスキーがローカルに保存されているか
|
||||||
static Future<bool> hasLicenseKey() async {
|
static Future<bool> hasLicenseKey() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
await _migrateIfNeeded();
|
||||||
return (prefs.getString(_prefLicenseKey) ?? '').isNotEmpty;
|
return ((await _storage.read(key: _secureKeyName)) ?? '').isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ライセンスをリセット(デバッグ用)
|
/// ライセンスをリセット(デバッグ用)
|
||||||
static Future<void> reset() async {
|
static Future<void> reset() async {
|
||||||
|
await _storage.delete(key: _secureKeyName);
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.remove(_prefLicenseKey);
|
|
||||||
await prefs.remove(_prefCachedStatus);
|
await prefs.remove(_prefCachedStatus);
|
||||||
await prefs.remove(_prefCachedAt);
|
await prefs.remove(_prefCachedAt);
|
||||||
debugPrint('[License] Reset complete.');
|
debugPrint('[License] Reset complete.');
|
||||||
|
|
@ -128,7 +167,11 @@ class LicenseService {
|
||||||
final data = jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
|
final data = jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
|
||||||
|
|
||||||
if (data['valid'] == true) return LicenseStatus.pro;
|
if (data['valid'] == true) return LicenseStatus.pro;
|
||||||
if ((data['error'] as String? ?? '').contains('無効化')) return LicenseStatus.revoked;
|
// revoked フィールド(boolean)を優先し、error メッセージ文字列にも対応
|
||||||
|
if (data['revoked'] == true ||
|
||||||
|
(data['error'] as String? ?? '').contains('無効化')) {
|
||||||
|
return LicenseStatus.revoked;
|
||||||
|
}
|
||||||
return LicenseStatus.free;
|
return LicenseStatus.free;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -148,14 +191,13 @@ class LicenseService {
|
||||||
|
|
||||||
if (cached == null) return LicenseStatus.free;
|
if (cached == null) return LicenseStatus.free;
|
||||||
|
|
||||||
// オンライン時は _validateKeyWithServer が常に上書きするため、
|
// オンライン時は checkStatus が常に上書きするため、
|
||||||
// _getCachedStatus はオフライン時専用のフォールバックとして動作する。
|
// _getCachedStatus はオフライン時専用のフォールバックとして動作する。
|
||||||
//
|
//
|
||||||
// TTL 判定(_cacheValidSeconds = 24h):
|
// TTL 判定(_cacheValidSeconds = 24h):
|
||||||
// - free / offline は期限切れで free にフォールバック
|
// - free / offline: 期限切れで free にフォールバック
|
||||||
// - pro : 購入者をオフライン時に締め出さないため永続扱い
|
// - pro : 購入者をオフライン時に締め出さないため永続扱い
|
||||||
// - revoked: 不正防止を優先するため永続扱い
|
// - revoked: 不正防止を優先するため永続扱い
|
||||||
// (将来 TTL を設けたい場合は isNoExpiryStatus を条件分岐ごと差し替える)
|
|
||||||
if (cachedAt != null) {
|
if (cachedAt != null) {
|
||||||
final age = DateTime.now().difference(DateTime.parse(cachedAt));
|
final age = DateTime.now().difference(DateTime.parse(cachedAt));
|
||||||
final isNoExpiryStatus = cached == LicenseStatus.pro.name || cached == LicenseStatus.revoked.name;
|
final isNoExpiryStatus = cached == LicenseStatus.pro.name || cached == LicenseStatus.revoked.name;
|
||||||
|
|
|
||||||
52
pubspec.lock
52
pubspec.lock
|
|
@ -515,6 +515,54 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
flutter_secure_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage
|
||||||
|
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.2.4"
|
||||||
|
flutter_secure_storage_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_linux
|
||||||
|
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
|
flutter_secure_storage_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_macos
|
||||||
|
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
flutter_secure_storage_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_platform_interface
|
||||||
|
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
flutter_secure_storage_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_web
|
||||||
|
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_windows
|
||||||
|
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
flutter_speed_dial:
|
flutter_speed_dial:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -801,10 +849,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: js
|
name: js
|
||||||
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.6.7"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.45+52
|
version: 1.0.46+53
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.10.1
|
sdk: ^3.10.1
|
||||||
|
|
@ -46,7 +46,7 @@ dependencies:
|
||||||
device_info_plus: ^10.1.0
|
device_info_plus: ^10.1.0
|
||||||
http: ^1.2.0
|
http: ^1.2.0
|
||||||
crypto: ^3.0.3 # SHA-256 ハッシュ計算用(キャッシュ)
|
crypto: ^3.0.3 # SHA-256 ハッシュ計算用(キャッシュ)
|
||||||
lucide_icons: ^0.257.0
|
lucide_icons: 0.257.0
|
||||||
reorderable_grid_view: ^2.2.5
|
reorderable_grid_view: ^2.2.5
|
||||||
camera: ^0.11.3
|
camera: ^0.11.3
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
|
|
@ -61,6 +61,7 @@ dependencies:
|
||||||
package_info_plus: ^8.1.2
|
package_info_plus: ^8.1.2
|
||||||
gal: ^2.3.0
|
gal: ^2.3.0
|
||||||
shared_preferences: ^2.5.4
|
shared_preferences: ^2.5.4
|
||||||
|
flutter_secure_storage: ^9.2.2
|
||||||
|
|
||||||
# Phase 9: Google Drive Backup
|
# Phase 9: Google Drive Backup
|
||||||
googleapis: ^13.2.0
|
googleapis: ^13.2.0
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <gal/gal_plugin_c_api.h>
|
#include <gal/gal_plugin_c_api.h>
|
||||||
#include <printing/printing_plugin.h>
|
#include <printing/printing_plugin.h>
|
||||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
|
|
@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
GalPluginCApiRegisterWithRegistrar(
|
GalPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("GalPluginCApi"));
|
registry->GetRegistrarForPlugin("GalPluginCApi"));
|
||||||
PrintingPluginRegisterWithRegistrar(
|
PrintingPluginRegisterWithRegistrar(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
|
flutter_secure_storage_windows
|
||||||
gal
|
gal
|
||||||
printing
|
printing
|
||||||
share_plus
|
share_plus
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue