-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Migrate shared_preferences_platform_interfaces to null safety #3466
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| ## 2.0.0-nullsafety | ||
|
|
||
| * Migrate to null safety. | ||
|
|
||
| ## 1.0.5 | ||
|
|
||
| * Update Flutter SDK constraint. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,39 +18,29 @@ const MethodChannel _kChannel = | |
| class MethodChannelSharedPreferencesStore | ||
| extends SharedPreferencesStorePlatform { | ||
| @override | ||
| Future<bool> remove(String key) { | ||
| return _invokeBoolMethod('remove', <String, dynamic>{ | ||
| 'key': key, | ||
| }); | ||
| Future<bool> remove(String key) async { | ||
| return await _kChannel.invokeMethod<bool>( | ||
| 'remove', | ||
| <String, dynamic>{'key': key}, | ||
| ) as bool; | ||
| } | ||
|
|
||
| @override | ||
| Future<bool> setValue(String valueType, String key, Object value) { | ||
| return _invokeBoolMethod('set$valueType', <String, dynamic>{ | ||
| 'key': key, | ||
| 'value': value, | ||
| }); | ||
| } | ||
|
|
||
| Future<bool> _invokeBoolMethod(String method, Map<String, dynamic> params) { | ||
| return _kChannel | ||
| .invokeMethod<bool>(method, params) | ||
| // TODO(yjbanov): I copied this from the original | ||
| // shared_preferences.dart implementation, but I | ||
| // actually do not know why it's necessary to pipe the | ||
| // result through an identity function. | ||
| // | ||
| // Source: https://github.com/flutter/plugins/blob/3a87296a40a2624d200917d58f036baa9fb18df8/packages/shared_preferences/lib/shared_preferences.dart#L134 | ||
| .then<bool>((dynamic result) => result); | ||
| Future<bool> setValue(String valueType, String key, Object value) async { | ||
| return await _kChannel.invokeMethod<bool>( | ||
| 'set$valueType', | ||
| <String, dynamic>{'key': key, 'value': value}, | ||
| ) as bool; | ||
| } | ||
|
|
||
| @override | ||
| Future<bool> clear() { | ||
| return _kChannel.invokeMethod<bool>('clear'); | ||
| Future<bool> clear() async { | ||
| return await _kChannel.invokeMethod<bool>('clear') as bool; | ||
| } | ||
|
|
||
| @override | ||
| Future<Map<String, Object>> getAll() { | ||
| return _kChannel.invokeMapMethod<String, Object>('getAll'); | ||
| Future<Map<String, Object>> getAll() async { | ||
| return await _kChannel.invokeMapMethod<String, Object>('getAll') | ||
| as Map<String, Object>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the cast? I thought the purpose of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All the invoke methods return the nullable version of the type parameters. As in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't that just need a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that would work but you would have to add parentheses around the await keyword. Basically the two options are final Map<String, Object> map = (await invokeMapMethod<String, Object>())!;or final Map<String, Object> map = await invokeMapMethod<String, Object>() as Map<String, Object>;I don't really have a preference on which is better. I think the top one is shorter, but the second is easier to read/cleaner.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd vote strongly for the top one, for the same reason that people prefer C++-style casts to C-style casts: it's explicit about what it's doing. Plus, if there were a mistake with the types here (say, a mismatch in the contents of the |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,17 @@ | ||
| name: shared_preferences_platform_interface | ||
| description: A common platform interface for the shared_preferences plugin. | ||
| homepage: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_platform_interface | ||
| version: 1.0.5 | ||
| version: 2.0.0-nullsafety | ||
|
|
||
| dependencies: | ||
| meta: ^1.0.4 | ||
| flutter: | ||
| sdk: flutter | ||
|
|
||
| dev_dependencies: | ||
| flutter_test: | ||
| sdk: flutter | ||
| pedantic: ^1.8.0 | ||
| pedantic: ^1.10.0-nullsafety | ||
|
|
||
| environment: | ||
| sdk: ">=2.1.0 <3.0.0" | ||
| sdk: ">=2.12.0-0 <3.0.0" | ||
| flutter: ">=1.12.8" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,18 +15,18 @@ void main() { | |
| 'plugins.flutter.io/shared_preferences', | ||
| ); | ||
|
|
||
| const Map<String, dynamic> kTestValues = <String, dynamic>{ | ||
| const Map<String, Object> kTestValues = <String, Object>{ | ||
| 'flutter.String': 'hello world', | ||
| 'flutter.Bool': true, | ||
| 'flutter.Int': 42, | ||
| 'flutter.Double': 3.14159, | ||
| 'flutter.StringList': <String>['foo', 'bar'], | ||
| }; | ||
|
|
||
| InMemorySharedPreferencesStore testData; | ||
| late InMemorySharedPreferencesStore testData; | ||
|
|
||
| final List<MethodCall> log = <MethodCall>[]; | ||
| MethodChannelSharedPreferencesStore store; | ||
| late MethodChannelSharedPreferencesStore store; | ||
|
|
||
| setUp(() async { | ||
| testData = InMemorySharedPreferencesStore.empty(); | ||
|
|
@@ -44,9 +44,9 @@ void main() { | |
| return await testData.clear(); | ||
| } | ||
| final RegExp setterRegExp = RegExp(r'set(.*)'); | ||
| final Match match = setterRegExp.matchAsPrefix(methodCall.method); | ||
| if (match.groupCount == 1) { | ||
| final String valueType = match.group(1); | ||
| final Match? match = setterRegExp.matchAsPrefix(methodCall.method); | ||
| if (match != null && match.groupCount == 1) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: couldn't this be
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good suggestion. done |
||
| final String valueType = match.group(1)!; | ||
| final String key = methodCall.arguments['key']; | ||
| final Object value = methodCall.arguments['value']; | ||
| return await testData.setValue(valueType, key, value); | ||
|
|
@@ -59,8 +59,6 @@ void main() { | |
|
|
||
| tearDown(() async { | ||
| await testData.clear(); | ||
| store = null; | ||
| testData = null; | ||
| }); | ||
|
|
||
| test('getAll', () async { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we certain that no platform implementation ever returns null here? It might be a good idea to bullet-proof this with a null check on the results, returning an empty map if the channel's result was null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done