Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 86 additions & 47 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,53 +120,92 @@ class MyAppState extends State<MyApp> {
body: Column(
children: [
const Text('Methods:'),
ElevatedButton(
child: const Text('init'),
onPressed: () async {
_logger.finer('Initializing $baseName');
final authStorageSupport =
await _checkAuthenticate(_authStorageInitOptions);
if (authStorageSupport == CanAuthenticateResponse.unsupported) {
_logger.severe(
'Unable to use authenticate. Unable to get storage.');
return;
}
final supportsAuthenticated = authStorageSupport ==
CanAuthenticateResponse.success ||
authStorageSupport == CanAuthenticateResponse.statusUnknown;
if (supportsAuthenticated) {
_authStorage = await BiometricStorage().getStorage(
'${baseName}_authenticated',
options: _authStorageInitOptions,
);
}
_storage = await BiometricStorage()
.getStorage('${baseName}_unauthenticated',
options: StorageFileInitOptions(
authenticationRequired: false,
));
final supportsCustomPrompt =
await _checkAuthenticate(_customPromptInitOptions);
if (supportsCustomPrompt == CanAuthenticateResponse.success) {
_customPrompt = await BiometricStorage()
.getStorage('${baseName}_customPrompt',
options: _customPromptInitOptions,
promptInfo: const PromptInfo(
iosPromptInfo: IosPromptInfo(
saveTitle: 'Custom save title',
accessTitle: 'Custom access title.',
),
androidPromptInfo: AndroidPromptInfo(
title: 'Custom title',
subtitle: 'Custom subtitle',
description: 'Custom description',
negativeButton: 'Nope!',
),
));
}
setState(() {});
_logger.info('initiailzed $baseName');
},
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
child: const Text('init'),
onPressed: () async {
_logger.finer('Initializing $baseName');
final authStorageSupport =
await _checkAuthenticate(_authStorageInitOptions);
if (authStorageSupport ==
CanAuthenticateResponse.unsupported) {
_logger.severe(
'Unable to use authenticate. Unable to get storage.');
return;
}
final supportsAuthenticated =
authStorageSupport == CanAuthenticateResponse.success ||
authStorageSupport ==
CanAuthenticateResponse.statusUnknown;
if (supportsAuthenticated) {
_authStorage = await BiometricStorage().getStorage(
'${baseName}_authenticated',
options: _authStorageInitOptions,
);
}
_storage = await BiometricStorage()
.getStorage('${baseName}_unauthenticated',
options: StorageFileInitOptions(
authenticationRequired: false,
));
final supportsCustomPrompt =
await _checkAuthenticate(_customPromptInitOptions);
if (supportsCustomPrompt ==
CanAuthenticateResponse.success) {
_customPrompt = await BiometricStorage()
.getStorage('${baseName}_customPrompt',
options: _customPromptInitOptions,
promptInfo: const PromptInfo(
iosPromptInfo: IosPromptInfo(
saveTitle: 'Custom save title',
accessTitle: 'Custom access title.',
),
androidPromptInfo: AndroidPromptInfo(
title: 'Custom title',
subtitle: 'Custom subtitle',
description: 'Custom description',
negativeButton: 'Nope!',
),
));
}
setState(() {});
_logger.info('initialized $baseName');
},
),
ElevatedButton(
child: const Text('Delete and dispose all'),
onPressed: () async {
_logger.info('Deleting and disposing all storages');
try {
if (_authStorage != null) {
await _authStorage!.deleteAndDispose();
_authStorage = null;
_logger.finer(
'Deleted and disposed authenticated storage');
}
if (_storage != null) {
await _storage!.deleteAndDispose();
_storage = null;
_logger.finer(
'Deleted and disposed unauthenticated storage');
}
if (_customPrompt != null) {
await _customPrompt!.deleteAndDispose();
_customPrompt = null;
_logger.finer(
'Deleted and disposed custom prompt storage');
}
Comment on lines +182 to +199

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code for deleting and disposing the different storages is quite repetitive. You can refactor this to reduce duplication and improve maintainability by iterating over a list of the storages. This makes the code cleaner and easier to modify if more storage types are added in the future.

                      final storagesToDispose = {
                        'authenticated': _authStorage,
                        'unauthenticated': _storage,
                        'custom prompt': _customPrompt,
                      };

                      for (final entry in storagesToDispose.entries) {
                        if (entry.value != null) {
                          await entry.value!.deleteAndDispose();
                          _logger.finer('Deleted and disposed ${entry.key} storage');
                        }
                      }
                      _authStorage = null;
                      _storage = null;
                      _customPrompt = null;

setState(() {});
_logger.info('All storages deleted and disposed');
} catch (e) {
_logger
.severe('Error deleting and disposing storages: $e');
}
},
),
],
),
...?_appArmorButton(),
...(_authStorage == null
Expand Down
22 changes: 22 additions & 0 deletions lib/src/biometric_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ abstract class BiometricStorage extends PlatformInterface {
String content,
PromptInfo promptInfo,
);

@protected
Future<void> dispose(
String name,
PromptInfo promptInfo,
);
}

class MethodChannelBiometricStorage extends BiometricStorage {
Expand Down Expand Up @@ -425,6 +431,14 @@ class MethodChannelBiometricStorage extends BiometricStorage {
..._promptInfoForCurrentPlatform(promptInfo),
}));

@override
Future<void> dispose(String name, PromptInfo promptInfo) => _transformErrors(
_channel.invokeMethod('dispose', <String, dynamic>{
'name': name,
..._promptInfoForCurrentPlatform(promptInfo),
}),
);
Comment on lines +435 to +440

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This adds a method channel call for dispose. However, based on the provided diffs, the native implementations for this call on Android, iOS, and macOS are missing. This will cause a runtime error when deleteAndDispose is called on these platforms.

Please ensure that the native implementations are added to this pull request. The native dispose handler should clean up any resources associated with the storage instance, such as removing it from the in-memory map of active storages (e.g., storageFiles on Android and stores on iOS/macOS).


Map<String, dynamic> _promptInfoForCurrentPlatform(PromptInfo promptInfo) {
// Don't expose Android configurations to other platforms
if (Platform.isAndroid) {
Expand Down Expand Up @@ -501,4 +515,12 @@ class BiometricStorageFile {
/// Delete the content of this storage.
Future<void> delete({PromptInfo? promptInfo}) =>
_plugin.delete(name, promptInfo ?? defaultPromptInfo);

/// Delete the content of this storage and dispose of the storage instance.
/// After calling this method, this BiometricStorageFile instance should not be used.
/// A subsequent call to BiometricStorage().getStorage() with the same name will create a new instance.
Future<void> deleteAndDispose({PromptInfo? promptInfo}) async {
await delete(promptInfo: promptInfo);
await _plugin.dispose(name, promptInfo ?? defaultPromptInfo);
}
}
5 changes: 5 additions & 0 deletions lib/src/biometric_storage_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,9 @@ class BiometricStoragePluginWeb extends BiometricStorage {
) async {
web.window.localStorage.setItem(name, content);
}

@override
Future<void> dispose(String name, PromptInfo promptInfo) async {
// Nothing to dispose for web implementation
}
}
5 changes: 5 additions & 0 deletions lib/src/biometric_storage_win32.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,9 @@ class Win32BiometricStoragePlugin extends BiometricStorage {
_logger.fine('free done');
}
}

@override
Future<void> dispose(String name, PromptInfo promptInfo) async {
// Nothing to dispose for Win32 implementation
}
}