From e7ad8246680a855f1ed75d1df03848bf7478aaad Mon Sep 17 00:00:00 2001 From: Kenzie Davisson Date: Thu, 30 Jan 2025 13:51:19 -0800 Subject: [PATCH 1/6] [shared_preferences] Fix a late initialized error with the example app --- .../shared_preferences/example/lib/main.dart | 78 ++++++++++++++----- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index f48d8de24fc..68103057247 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -44,6 +44,10 @@ class SharedPreferencesDemoState extends State { late Future _counter; int _externalCounter = 0; + /// Completes when the preferences have been initialized, which happens after + /// legacy preferences have been migrated. + final Completer _preferencesReady = Completer(); + Future _incrementCounter() async { final SharedPreferencesWithCache prefs = await _prefs; final int counter = (prefs.getInt('counter') ?? 0) + 1; @@ -59,8 +63,9 @@ class SharedPreferencesDemoState extends State { /// or via some native system. Future _getExternalCounter() async { final SharedPreferencesAsync prefs = SharedPreferencesAsync(); - setState(() async { - _externalCounter = (await prefs.getInt('externalCounter')) ?? 0; + final int externalCounter = (await prefs.getInt('externalCounter')) ?? 0; + setState(() { + _externalCounter = externalCounter; }); } @@ -85,6 +90,7 @@ class SharedPreferencesDemoState extends State { return prefs.getInt('counter') ?? 0; }); _getExternalCounter(); + _preferencesReady.complete(); }); } @@ -95,25 +101,30 @@ class SharedPreferencesDemoState extends State { title: const Text('SharedPreferencesWithCache Demo'), ), body: Center( - child: FutureBuilder( - future: _counter, - builder: (BuildContext context, AsyncSnapshot snapshot) { - switch (snapshot.connectionState) { - case ConnectionState.none: - case ConnectionState.waiting: - return const CircularProgressIndicator(); - case ConnectionState.active: - case ConnectionState.done: - if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } else { - return Text( - 'Button tapped ${snapshot.data ?? 0 + _externalCounter} time${(snapshot.data ?? 0 + _externalCounter) == 1 ? '' : 's'}.\n\n' - 'This should persist across restarts.', - ); - } - } - })), + child: _WaitForInitialization( + initialized: _preferencesReady.future, + builder: (BuildContext context) => FutureBuilder( + future: _counter, + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const CircularProgressIndicator(); + case ConnectionState.active: + case ConnectionState.done: + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return Text( + 'Button tapped ${snapshot.data ?? 0 + _externalCounter} time${(snapshot.data ?? 0 + _externalCounter) == 1 ? '' : 's'}.\n\n' + 'This should persist across restarts.', + ); + } + } + }, + ), + ), + ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', @@ -122,3 +133,28 @@ class SharedPreferencesDemoState extends State { ); } } + +/// Waits for the [initialized] future to complete before rendering [builder]. +class _WaitForInitialization extends StatelessWidget { + const _WaitForInitialization({ + required this.initialized, + required this.builder, + }); + + final Future initialized; + final WidgetBuilder builder; + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: initialized, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting || + snapshot.connectionState == ConnectionState.none) { + return const CircularProgressIndicator(); + } + return builder(context); + }, + ); + } +} From a88794e35894e3939960755233b45e9d339c5635 Mon Sep 17 00:00:00 2001 From: Kenzie Davisson Date: Thu, 30 Jan 2025 13:56:55 -0800 Subject: [PATCH 2/6] changelog and version --- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 ++++ packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 8566cccd410..4787f1285e4 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.2 + +* Fixed a bug in the example app. + ## 2.5.1 * Exposes `SharedPreferencesOptions`. diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 273e097229b..6d6a20f725f 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.5.1 +version: 2.5.2 environment: sdk: ^3.5.0 From e7267ada8d7900078b01901d14dd09502e79f0d1 Mon Sep 17 00:00:00 2001 From: Kenzie Davisson Date: Thu, 30 Jan 2025 14:26:14 -0800 Subject: [PATCH 3/6] add test --- .../example/test/example_app_test.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/shared_preferences/shared_preferences/example/test/example_app_test.dart diff --git a/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart b/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart new file mode 100644 index 00000000000..f522ce8cbc7 --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_example/main.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; + +import '../..//test/shared_preferences_async_test.dart'; + +void main() { + group('SharedPreferences example app', () { + setUp(() { + SharedPreferencesAsyncPlatform.instance = FakeSharedPreferencesAsync(); + }); + + tearDown(() { + SharedPreferencesAsyncPlatform.instance = null; + }); + + testWidgets('builds successfully', (WidgetTester tester) async { + await tester.pumpWidget(const MyApp()); + }); + }); +} From 68d0dc7a34581647b7906535681723859688a167 Mon Sep 17 00:00:00 2001 From: Kenzie Davisson Date: Fri, 31 Jan 2025 11:25:24 -0800 Subject: [PATCH 4/6] review comments --- packages/shared_preferences/shared_preferences/CHANGELOG.md | 2 +- .../shared_preferences/example/test/example_app_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 4787f1285e4..e764e9326b7 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.5.2 -* Fixed a bug in the example app. +* Fixes a bug in the example app. ## 2.5.1 diff --git a/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart b/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart index f522ce8cbc7..8836951ff26 100644 --- a/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart +++ b/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart @@ -6,7 +6,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_example/main.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; -import '../..//test/shared_preferences_async_test.dart'; +import '../../test/shared_preferences_async_test.dart'; void main() { group('SharedPreferences example app', () { From eebbef645aef7f36f2781c919ffe400159d67453 Mon Sep 17 00:00:00 2001 From: Kenzie Davisson Date: Tue, 25 Mar 2025 13:53:54 -0700 Subject: [PATCH 5/6] version and changelog --- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 +++- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 1853e890b60..3449842a263 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,8 @@ +## 2.5.3 +* Fixes a bug in the example app. + ## 2.5.2 -* Fixes a bug in the example app. * Fixes `setState` returning `Future` on `example/main.dart` error in example code. ## 2.5.1 diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 3f535a86a02..ca545d084d8 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.5.2 +version: 2.5.3 environment: sdk: ^3.5.0 From 5c6209a3465bc3788d724e87eb98219acf400f03 Mon Sep 17 00:00:00 2001 From: Kenzie Davisson Date: Tue, 25 Mar 2025 13:58:50 -0700 Subject: [PATCH 6/6] fix test --- .../example/test/example_app_test.dart | 102 +++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart b/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart index 8836951ff26..3c8549b6b9c 100644 --- a/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart +++ b/packages/shared_preferences/shared_preferences/example/test/example_app_test.dart @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_example/main.dart'; +import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; - -import '../../test/shared_preferences_async_test.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { group('SharedPreferences example app', () { @@ -23,3 +24,100 @@ void main() { }); }); } + +// Note: this code is duplicated in +// shared_preferences/test/shared_preferences_async_test.dart. Since we cannot +// import the relative path ../../test/shared_preferences_async_test.dart on the +// web platform, we had to copy it here for use in this test library. +base class FakeSharedPreferencesAsync extends SharedPreferencesAsyncPlatform { + final InMemorySharedPreferencesAsync backend = + InMemorySharedPreferencesAsync.empty(); + final List log = []; + + @override + Future clear( + ClearPreferencesParameters parameters, SharedPreferencesOptions options) { + log.add(MethodCall('clear', [...?parameters.filter.allowList])); + return backend.clear(parameters, options); + } + + @override + Future getBool(String key, SharedPreferencesOptions options) { + log.add(MethodCall('getBool', [key])); + return backend.getBool(key, options); + } + + @override + Future getDouble(String key, SharedPreferencesOptions options) { + log.add(MethodCall('getDouble', [key])); + return backend.getDouble(key, options); + } + + @override + Future getInt(String key, SharedPreferencesOptions options) { + log.add(MethodCall('getInt', [key])); + return backend.getInt(key, options); + } + + @override + Future> getKeys( + GetPreferencesParameters parameters, SharedPreferencesOptions options) { + log.add(MethodCall('getKeys', [...?parameters.filter.allowList])); + return backend.getKeys(parameters, options); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, SharedPreferencesOptions options) { + log.add(MethodCall( + 'getPreferences', [...?parameters.filter.allowList])); + return backend.getPreferences(parameters, options); + } + + @override + Future getString(String key, SharedPreferencesOptions options) { + log.add(MethodCall('getString', [key])); + return backend.getString(key, options); + } + + @override + Future?> getStringList( + String key, SharedPreferencesOptions options) { + log.add(MethodCall('getStringList', [key])); + return backend.getStringList(key, options); + } + + @override + Future setBool( + String key, bool value, SharedPreferencesOptions options) { + log.add(MethodCall('setBool', [key, value])); + return backend.setBool(key, value, options); + } + + @override + Future setDouble( + String key, double value, SharedPreferencesOptions options) { + log.add(MethodCall('setDouble', [key, value])); + return backend.setDouble(key, value, options); + } + + @override + Future setInt(String key, int value, SharedPreferencesOptions options) { + log.add(MethodCall('setInt', [key, value])); + return backend.setInt(key, value, options); + } + + @override + Future setString( + String key, String value, SharedPreferencesOptions options) { + log.add(MethodCall('setString', [key, value])); + return backend.setString(key, value, options); + } + + @override + Future setStringList( + String key, List value, SharedPreferencesOptions options) { + log.add(MethodCall('setStringList', [key, value])); + return backend.setStringList(key, value, options); + } +}