Trigger background sync on connectivity recovery
This commit is contained in:
@@ -30,6 +30,9 @@ enabled by default.
|
|||||||
In REST mode, auto-triggers are reachability-gated to avoid unnecessary sync
|
In REST mode, auto-triggers are reachability-gated to avoid unnecessary sync
|
||||||
attempts while offline.
|
attempts while offline.
|
||||||
|
|
||||||
|
When connectivity returns (offline -> online), sync now triggers immediately in
|
||||||
|
addition to startup/resume/periodic ticks.
|
||||||
|
|
||||||
Control flags:
|
Control flags:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+30
-2
@@ -2,6 +2,34 @@
|
|||||||
|
|
||||||
Updated: 2026-02-17
|
Updated: 2026-02-17
|
||||||
|
|
||||||
|
## Latest Milestone (2026-02-17): Connectivity Event-Driven Background Sync
|
||||||
|
|
||||||
|
Implemented online-transition-triggered sync so background sync reacts faster
|
||||||
|
when network returns:
|
||||||
|
|
||||||
|
- Added connectivity stream support to reachability abstraction:
|
||||||
|
- `lib/core/network/reachability/network_reachability.dart`
|
||||||
|
- new `watch(...)` API for reachability change events
|
||||||
|
- Added IO implementation using connectivity plugin + internet probe:
|
||||||
|
- `lib/core/network/reachability/network_reachability_io.dart`
|
||||||
|
- `connectivity_plus` integrated as transport signal source
|
||||||
|
- Updated background runner:
|
||||||
|
- `lib/features/sync/sync_background_runner.dart`
|
||||||
|
- now listens for offline -> online transitions and triggers immediate sync
|
||||||
|
- keeps existing startup/resume/periodic behavior
|
||||||
|
- Added coverage:
|
||||||
|
- `test/features/sync/sync_background_runner_test.dart`
|
||||||
|
- verifies transition-trigger behavior and avoids duplicate triggers when
|
||||||
|
already online
|
||||||
|
- Updated setup docs:
|
||||||
|
- `docs/SETUP.md` background sync section now includes connectivity-return
|
||||||
|
behavior note
|
||||||
|
|
||||||
|
Validation for this milestone:
|
||||||
|
|
||||||
|
- `flutter analyze` -> pass
|
||||||
|
- `flutter test` -> pass
|
||||||
|
|
||||||
## Latest Milestone (2026-02-17): Sync Repair Test Stability + Mobile CRUD/Sync Flow Coverage
|
## Latest Milestone (2026-02-17): Sync Repair Test Stability + Mobile CRUD/Sync Flow Coverage
|
||||||
|
|
||||||
Completed two test-depth upgrades:
|
Completed two test-depth upgrades:
|
||||||
@@ -313,8 +341,8 @@ Validation for this milestone:
|
|||||||
- future enhancement: auto-select and highlight the exact row in main feature
|
- future enhancement: auto-select and highlight the exact row in main feature
|
||||||
screens after save.
|
screens after save.
|
||||||
3. Sync trigger maturity:
|
3. Sync trigger maturity:
|
||||||
- implemented via reachability gating; future improvement is explicit OS
|
- implemented via reachability gating + online-transition event triggers.
|
||||||
connectivity event subscription.
|
- future: add richer platform-specific network quality awareness if needed.
|
||||||
4. Test depth:
|
4. Test depth:
|
||||||
- expanded with mobile compact CRUD+sync flow and rejected-repair flow.
|
- expanded with mobile compact CRUD+sync flow and rejected-repair flow.
|
||||||
- future: add desktop + web-specific end-to-end sync error/retry scenario.
|
- future: add desktop + web-specific end-to-end sync error/retry scenario.
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import 'package:relationship_saver/core/network/reachability/network_reachabilit
|
|||||||
abstract class NetworkReachability {
|
abstract class NetworkReachability {
|
||||||
/// Returns `true` when network looks reachable.
|
/// Returns `true` when network looks reachable.
|
||||||
Future<bool> isReachable({Duration timeout = const Duration(seconds: 2)});
|
Future<bool> isReachable({Duration timeout = const Duration(seconds: 2)});
|
||||||
|
|
||||||
|
/// Emits reachability updates when platform network status changes.
|
||||||
|
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns platform implementation for network reachability probing.
|
/// Returns platform implementation for network reachability probing.
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:relationship_saver/core/network/reachability/network_reachability.dart';
|
import 'package:relationship_saver/core/network/reachability/network_reachability.dart';
|
||||||
|
|
||||||
class IoNetworkReachability implements NetworkReachability {
|
class IoNetworkReachability implements NetworkReachability {
|
||||||
|
IoNetworkReachability({Connectivity? connectivity})
|
||||||
|
: _connectivity = connectivity ?? Connectivity();
|
||||||
|
|
||||||
|
final Connectivity _connectivity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isReachable({
|
Future<bool> isReachable({
|
||||||
Duration timeout = const Duration(seconds: 2),
|
Duration timeout = const Duration(seconds: 2),
|
||||||
@@ -19,6 +25,35 @@ class IoNetworkReachability implements NetworkReachability {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||||
|
final Stream<dynamic> changes =
|
||||||
|
_connectivity.onConnectivityChanged as Stream<dynamic>;
|
||||||
|
return changes
|
||||||
|
.asyncMap((dynamic event) async {
|
||||||
|
final Iterable<ConnectivityResult> results =
|
||||||
|
_normalizeConnectivityResults(event);
|
||||||
|
final bool hasTransport = results.any(
|
||||||
|
(ConnectivityResult result) => result != ConnectivityResult.none,
|
||||||
|
);
|
||||||
|
if (!hasTransport) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isReachable(timeout: timeout);
|
||||||
|
})
|
||||||
|
.distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<ConnectivityResult> _normalizeConnectivityResults(dynamic event) {
|
||||||
|
if (event is ConnectivityResult) {
|
||||||
|
return <ConnectivityResult>[event];
|
||||||
|
}
|
||||||
|
if (event is List<ConnectivityResult>) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
return const <ConnectivityResult>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkReachability createPlatformNetworkReachability() =>
|
NetworkReachability createPlatformNetworkReachability() =>
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ class StubNetworkReachability implements NetworkReachability {
|
|||||||
}) async {
|
}) async {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||||
|
return const Stream<bool>.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkReachability createPlatformNetworkReachability() =>
|
NetworkReachability createPlatformNetworkReachability() =>
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ class WebNetworkReachability implements NetworkReachability {
|
|||||||
// Web environments vary in available probes; keep optimistic fallback.
|
// Web environments vary in available probes; keep optimistic fallback.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||||
|
return const Stream<bool>.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkReachability createPlatformNetworkReachability() =>
|
NetworkReachability createPlatformNetworkReachability() =>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:relationship_saver/core/config/app_config.dart';
|
import 'package:relationship_saver/core/config/app_config.dart';
|
||||||
|
import 'package:relationship_saver/core/network/reachability/network_reachability.dart';
|
||||||
import 'package:relationship_saver/core/network/reachability/network_reachability_provider.dart';
|
import 'package:relationship_saver/core/network/reachability/network_reachability_provider.dart';
|
||||||
import 'package:relationship_saver/features/sync/sync_auto_trigger_controller.dart';
|
import 'package:relationship_saver/features/sync/sync_auto_trigger_controller.dart';
|
||||||
import 'package:relationship_saver/features/sync/sync_coordinator.dart';
|
import 'package:relationship_saver/features/sync/sync_coordinator.dart';
|
||||||
@@ -22,10 +23,13 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
|||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
late final SyncAutoTriggerController _controller;
|
late final SyncAutoTriggerController _controller;
|
||||||
Timer? _periodicTimer;
|
Timer? _periodicTimer;
|
||||||
|
StreamSubscription<bool>? _reachabilitySubscription;
|
||||||
|
bool? _lastReachable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
final reachability = ref.read(networkReachabilityProvider);
|
||||||
_controller = SyncAutoTriggerController(
|
_controller = SyncAutoTriggerController(
|
||||||
syncNow: () => ref.read(syncCoordinatorProvider).syncNow(),
|
syncNow: () => ref.read(syncCoordinatorProvider).syncNow(),
|
||||||
now: DateTime.now,
|
now: DateTime.now,
|
||||||
@@ -36,11 +40,12 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
|||||||
if (AppConfig.useFakeBackend) {
|
if (AppConfig.useFakeBackend) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return ref.read(networkReachabilityProvider).isReachable();
|
return reachability.isReachable();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
_configurePeriodicRunner();
|
_configurePeriodicRunner();
|
||||||
|
_configureReachabilityRunner(reachability);
|
||||||
if (AppConfig.enableBackgroundSync) {
|
if (AppConfig.enableBackgroundSync) {
|
||||||
unawaited(_controller.trigger(ignoreCooldown: true));
|
unawaited(_controller.trigger(ignoreCooldown: true));
|
||||||
}
|
}
|
||||||
@@ -60,6 +65,7 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
_periodicTimer?.cancel();
|
_periodicTimer?.cancel();
|
||||||
|
_reachabilitySubscription?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,4 +86,22 @@ class _SyncBackgroundRunnerState extends ConsumerState<SyncBackgroundRunner>
|
|||||||
unawaited(_controller.trigger());
|
unawaited(_controller.trigger());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _configureReachabilityRunner(NetworkReachability reachability) {
|
||||||
|
if (!AppConfig.enableBackgroundSync) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_reachabilitySubscription = reachability.watch().listen((bool reachable) {
|
||||||
|
final bool? previous = _lastReachable;
|
||||||
|
_lastReachable = reachable;
|
||||||
|
if (reachable && previous == false) {
|
||||||
|
unawaited(_controller.trigger(ignoreCooldown: true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
unawaited(_primeReachabilityState(reachability));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _primeReachabilityState(NetworkReachability reachability) async {
|
||||||
|
_lastReachable = await reachability.isReachable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import connectivity_plus
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
import flutter_secure_storage_darwin
|
import flutter_secure_storage_darwin
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
|||||||
@@ -145,6 +145,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
|
connectivity_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: connectivity_plus
|
||||||
|
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
|
connectivity_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: connectivity_plus_platform_interface
|
||||||
|
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -560,6 +576,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.4"
|
version: "0.17.4"
|
||||||
|
nm:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nm
|
||||||
|
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.0"
|
||||||
node_preamble:
|
node_preamble:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ dependencies:
|
|||||||
flutter_riverpod: ^3.2.1
|
flutter_riverpod: ^3.2.1
|
||||||
shared_preferences: ^2.5.4
|
shared_preferences: ^2.5.4
|
||||||
hive_flutter: ^1.1.0
|
hive_flutter: ^1.1.0
|
||||||
|
connectivity_plus: ^7.0.0
|
||||||
timezone: ^0.10.1
|
timezone: ^0.10.1
|
||||||
flutter_local_notifications: ^20.1.0
|
flutter_local_notifications: ^20.1.0
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:relationship_saver/core/network/reachability/network_reachability.dart';
|
||||||
|
import 'package:relationship_saver/core/network/reachability/network_reachability_provider.dart';
|
||||||
|
import 'package:relationship_saver/features/sync/sync_background_runner.dart';
|
||||||
|
import 'package:relationship_saver/features/sync/sync_coordinator.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('triggers sync when reachability transitions from offline to online', (
|
||||||
|
WidgetTester tester,
|
||||||
|
) async {
|
||||||
|
final _TestReachability reachability = _TestReachability(initial: false);
|
||||||
|
addTearDown(reachability.dispose);
|
||||||
|
|
||||||
|
int syncCalls = 0;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
networkReachabilityProvider.overrideWithValue(reachability),
|
||||||
|
syncCoordinatorProvider.overrideWith(
|
||||||
|
(Ref ref) =>
|
||||||
|
_TestSyncCoordinator(ref, onSyncNow: () async {
|
||||||
|
syncCalls += 1;
|
||||||
|
return _okResult();
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SyncBackgroundRunner(child: SizedBox()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||||
|
|
||||||
|
// Startup trigger always runs once when background sync is enabled.
|
||||||
|
expect(syncCalls, 1);
|
||||||
|
|
||||||
|
reachability.emit(false);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 40));
|
||||||
|
expect(syncCalls, 1);
|
||||||
|
|
||||||
|
reachability.emit(true);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||||
|
expect(syncCalls, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('does not trigger extra sync while connectivity stays online', (
|
||||||
|
WidgetTester tester,
|
||||||
|
) async {
|
||||||
|
final _TestReachability reachability = _TestReachability(initial: true);
|
||||||
|
addTearDown(reachability.dispose);
|
||||||
|
|
||||||
|
int syncCalls = 0;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
networkReachabilityProvider.overrideWithValue(reachability),
|
||||||
|
syncCoordinatorProvider.overrideWith(
|
||||||
|
(Ref ref) =>
|
||||||
|
_TestSyncCoordinator(ref, onSyncNow: () async {
|
||||||
|
syncCalls += 1;
|
||||||
|
return _okResult();
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SyncBackgroundRunner(child: SizedBox()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||||
|
|
||||||
|
expect(syncCalls, 1);
|
||||||
|
|
||||||
|
reachability.emit(true);
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||||
|
|
||||||
|
expect(syncCalls, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TestSyncCoordinator extends SyncCoordinator {
|
||||||
|
_TestSyncCoordinator(super.ref, {required this.onSyncNow});
|
||||||
|
|
||||||
|
final Future<SyncRunResult> Function() onSyncNow;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SyncRunResult> syncNow() => onSyncNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TestReachability implements NetworkReachability {
|
||||||
|
_TestReachability({required bool initial}) : _current = initial;
|
||||||
|
|
||||||
|
final StreamController<bool> _controller = StreamController<bool>.broadcast();
|
||||||
|
bool _current;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isReachable({Duration timeout = const Duration(seconds: 2)}) async {
|
||||||
|
return _current;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<bool> watch({Duration timeout = const Duration(seconds: 2)}) {
|
||||||
|
return _controller.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(bool reachable) {
|
||||||
|
_current = reachable;
|
||||||
|
_controller.add(reachable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> dispose() async {
|
||||||
|
await _controller.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncRunResult _okResult() {
|
||||||
|
return const SyncRunResult(
|
||||||
|
success: true,
|
||||||
|
operation: 'sync',
|
||||||
|
message: 'ok',
|
||||||
|
pendingAfter: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,9 +6,12 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
connectivity_plus
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user