Интеграция shared_preferences в Riverpod

Вопрос или проблема

Я ищу способ использовать 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 с его асинхронными провайдерами и встроенной поддержкой кэширования делает работу с пользовательскими предпочтениями более удобной и производительной.

Если у вас остались вопросы или нужны дальнейшие разъяснения, пожалуйста, дайте знать!

Оцените материал
Добавить комментарий

Капча загружается...