Вопрос или проблема
Я ищу способ использовать shared_preferences
с riverpod
. Я новичок в riverpod и никогда не работал с shared_preferences раньше, поэтому искал руководство или пример, как их объединить. Я видел этот пример, но в нем использовалась устаревшая SharedPreferences
, а не SharedPreferencesWithCache
или SharedPreferencesAsync
.
Вот три варианта, которые я придумал (с моими ограниченными знаниями):
final sharedPreferencesProvider = Provider((ref)=>SharedPreferencesAsync());
или
final sharedPreferencesProvider = FutureProvider<SharedPreferences>((ref) async {
final prefs = await SharedPreferences.getInstance();
return prefs;
});
или
final sharedPreferencesProvider =
FutureProvider<SharedPreferencesWithCache>((ref) async {
final prefs = await SharedPreferencesWithCache.create(
cacheOptions: const SharedPreferencesWithCacheOptions(allowList: null));
return prefs;
});
Я не уверен, какой из них использовать. Также странно, что поставщик версии с кэшем должен быть FutureProvider. Это означает, что мне придется пройти через все эти заморочки с AsyncValue (см. ниже), в то время как SharedPreferencesWithCache предназначен для того, чтобы не иметь с этим дела, так как вы должны иметь возможность вызывать его синхронно благодаря кэшированию. Может быть, вместо того, чтобы помещать его в FutureProvider, просто сделать его глобальной переменной? Но тогда возникает вопрос: куда поместить эту глобальную переменную?
Text(ref.watch(sharedPreferencesProvider).asData!.value.getString("my_key")!);
Я также хотел бы иметь некий обертку вокруг сырого prefs
, которая кажется немного более надежной, чем постоянные вызовы setString
и т. д., но не уверен, как интегрировать этот класс во все это… Возможно, что-то вроде ранее приведенного примера?
Вы можете упростить это, используя аннотации Riverpod, которые автоматически сгенерируют необходимые провайдеры для вас, так что вам не нужно беспокоиться о типе провайдера. Вот пример, как использовать это с SharedPreferences:
// FutureProvider для доступа к SharedPreferences асинхронно
@Riverpod(keepAlive: true)
Future<SharedPreferences> sharedPreferences(SharedPreferencesRef ref) =>
SharedPreferences.getInstance();
С таким подходом вы можете сохранить свой код чистым, и провайдер будет автоматически обрабатывать асинхронную часть за вас. Он также поддерживает кэширование благодаря опции keepAlive.
Вы можете получить доступ к предпочтениям вот так:
final prefs = ref.watch(sharedPreferencesProvider).asData?.value;
final myValue = prefs?.getString("my_key");
Для получения дополнительной информации о надежной инициализации приложения с Riverpod, вы можете ознакомиться с этой полезной статьей:
Надежная инициализация приложений с Riverpod
Ответ или решение
Для интеграции shared_preferences
с riverpod
можно использовать несколько подходов. В данном ответе я расскажу о том, как правильно организовать работу с shared_preferences
, используя FutureProvider
, а также как создать обертку для удобного доступа к предпочтениям. Мы будем использовать SharedPreferencesAsync
для кэширования.
1. Создание провайдера для SharedPreferences
Первым делом создадим провайдер для SharedPreferences
с использованием riverpod
. Мы воспользуемся аннотацией @Riverpod
, что упростит создание провайдера и управление состоянием.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
part 'shared_preferences_provider.g.dart';
// Создаем FutureProvider для доступа к SharedPreferences
@Riverpod(keepAlive: true)
Future<SharedPreferences> sharedPreferences(SharedPreferencesRef ref) async {
final prefs = await SharedPreferences.getInstance();
return prefs;
}
2. Использование провайдера
Теперь мы можем использовать наш созданный провайдер в приложении. Для этого, когда нам нужны предпочтения, мы можем подписаться на sharedPreferencesProvider
и получать доступ к сохраненным значениям.
Widget myWidget(BuildContext context, WidgetRef ref) {
final asyncPrefs = ref.watch(sharedPreferencesProvider);
return asyncPrefs.when(
data: (prefs) {
final myValue = prefs.getString("my_key") ?? "default_value";
return Text(myValue);
},
loading: () => CircularProgressIndicator(),
error: (error, stack) => Text('Ошибка: $error'),
);
}
3. Обертка для удобного доступа к SharedPreferences
Для более удобного и интуитивного доступа к значениям из SharedPreferences
, вы можете создать обертку, которая будет инкапсулировать вызовы методов setString
, getString
и другие.
class PreferencesService {
final Future<SharedPreferences> _prefs;
PreferencesService(this._prefs);
Future<void> setString(String key, String value) async {
final prefs = await _prefs;
await prefs.setString(key, value);
}
Future<String?> getString(String key) async {
final prefs = await _prefs;
return prefs.getString(key);
}
}
Теперь, когда у нас есть сервис, мы можем использовать его в нашем приложении:
final preferencesServiceProvider = Provider<PreferencesService>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return PreferencesService(prefs);
});
// Использование сервиса в виджете
Widget myPersistentWidget(BuildContext context, WidgetRef ref) {
final prefsService = ref.watch(preferencesServiceProvider);
return FutureBuilder<String?>(
future: prefsService.getString("my_key"),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
final myValue = snapshot.data ?? "default_value";
return Text(myValue);
},
);
}
Заключение
Теперь у вас есть полный пример интеграции shared_preferences
с riverpod
и созданием обертки для удобного доступа к значениям. Использование riverpod
с его асинхронными провайдерами и встроенной поддержкой кэширования делает работу с пользовательскими предпочтениями более удобной и производительной.
Если у вас остались вопросы или нужны дальнейшие разъяснения, пожалуйста, дайте знать!