SharedPreferences в Flutter: руководство по использованию

Быстрые ссылки
Adding SharedPreferences
Adding Data to SharedPreferences
Retrieving Persisted Data
Caveats and Limitations
Writing Unit Tests
Summary
SharedPreferences — это модуль, который приносит в кроссплатформенные приложения Flutter поведение одноимённого Android API. На iOS и macOS используется похожая система NSUserDefaults; также есть реализации для Linux и Windows, поэтому пакет работает везде, где поддерживается Flutter.
В этом руководстве показано, как использовать SharedPreferences для сохранения простых настроек внутри приложения. Модуль подходит для небольших фрагментов несущественного состояния: пользовательских предпочтений, сохранённых закладок и кэшированных конфигурационных значений от сервера.
Добавление SharedPreferences
Добавьте модуль SharedPreferences в проект с помощью Flutter CLI:
flutter pub add shared_preferencesИмпортируйте библиотеку в ваших Dart‑файлах:
import 'package:shared_preferences/shared_preferences.dart';После импорта вы сможете обращаться к API SharedPreferences в этом файле.
Добавление данных в SharedPreferences
Для начала получите экземпляр класса SharedPreferences, связанный с конфигурационным файлом на диске приложения. Вызовите статический метод getInstance(), чтобы загрузить и распарсить файл. Метод асинхронный, поэтому используйте await внутри async функции.
Пример инициализации в main():
import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
// Пример записи
await prefs.setBool('darkTheme', true);
// Продолжайте запуск приложения
}SharedPreferences работает с парами ключ‑значение и поддерживает пять типов: int, String, bool, double и List. Сохранение выполняется через соответствующие методы‑сеттеры: первый параметр — ключ, второй — значение.
await prefs.setBool('darkTheme', true);
await prefs.setString('theme', 'dark');
await prefs.setInt('sessionId', 1000);
await prefs.setDouble('lastTemperature', 18.5);
await prefs.setStringList('featureFlags', ['darkTheme', 'redesign']);Сеттеры не принимают null. Для удаления записи используйте remove():
await prefs.remove('sessionId');Чтобы удалить все сохранённые настройки, вызовите clear() — операция асинхронная и завершается после удаления файла на диске:
await prefs.clear();Сложные типы
Нативно нельзя сохранять сложные объекты, карты или списки с типами, отличными от String. Преобразуйте такие данные в JSON и сохраните как String:
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
final bookmarkedArticles = [10, 15, 18];
final prefs = await SharedPreferences.getInstance();
await prefs.setString('bookmarks', jsonEncode(bookmarkedArticles));
}При чтении используйте jsonDecode() для восстановления исходной структуры.
Получение сохранённых данных
Доступ к значениям выполняется через геттеры. Для каждого типа есть соответствующий метод, поэтому не требуется явного приведения типов.
final theme = prefs.getString('theme'); // 'dark' или null
final sessionId = prefs.getInt('sessionId'); // 1000 или null
final lastTemperature = prefs.getDouble('lastTemperature'); // 18.5 или nullВажно использовать геттер, соответствующий типу сохранённого значения — если тип не совпадёт, будет выброшено исключение.
Если ключ отсутствует, возвращается null. Перед запросом можно проверить наличие ключа с помощью containsKey():
if (prefs.containsKey('theme')) {
// ...
}Также можно перечислить все ключи:
final keys = prefs.getKeys();Это полезно при миграциях после обновления приложения — вы увидите, какие значения уже сохранены на диске.
Примеры: чтение/запись с обработкой ошибок и дефолтами
Ниже пример функции-обёртки, которая возвращает дефолтное значение при отсутствии ключа и логирует ошибку при несоответствии типа:
T? getPref(SharedPreferences prefs, String key, {T? defaultValue}) {
try {
final value = prefs.get(key);
if (value is T) return value;
if (value == null) return defaultValue;
return defaultValue;
} catch (e) {
// Логирование: нельзя использовать print в production, применяйте логгер
print('Ошибка чтения $key: $e');
return defaultValue;
}
} Ограничения и подводные камни
SharedPreferences хорош для простых случаев, но не подходит для всего подряд. Ниже список типичных ограничений и рисков.
Important: SharedPreferences не предназначен для секьюрного хранения или больших объёмов данных.
- Публичный доступ: реализации платформ сохраняют данные в местах, доступных пользователю при руте/джейлбрейке. Сами значения обычно хранятся в открытом виде. Шифрование перед сохранением повышает защиту, но ключи всё ещё остаются видимыми.
- Асинхронность записи: хотя геттеры/сеттеры выглядят синхронно, записи могут быть выполнены асинхронно на уровне диска. Немедленное завершение приложения после set может привести к потере записи.
- Размер: SharedPreferences не оптимизирован для больших наборов данных. Для больших или сложных структур используйте файлы, SQLite или специализированные хранилища.
- Целостность и транзакции: нет механизма транзакций или отката; сложные операции должны сами обеспечивать атомарность.
Когда SharedPreferences не подойдёт:
- Когда нужны продвинутые запросы и индексы — используйте SQLite.
- Когда данные критичны для безопасности — используйте Keychain/Keystore или зашифрованное хранилище.
- Когда требуется высокая производительность для больших объёмов — храните в бинарных файлах или БД.
Альтернативные подходы
- Files API: запись пользовательского конфигурационного файла в приватный каталог приложения.
- SQLite (sqflite, drift): подходит для структурированных данных и сложных запросов.
- Hive/Isar: быстрые NoSQL‑решения для локального хранения с поддержкой бинарного формата.
- EncryptedSharedPreferences / flutter_secure_storage: для чувствительных данных используйте хранилища с шифрованием.
Дизайн ключей и методология
Мини‑методология проектирования ключей:
- Используйте префиксы по доменам: settings.theme, user.sessionId, cache.lastSync.
- Ограничивайте длину ключа и избегайте пробелов и спецсимволов.
- Версионируйте сложные структуры: bookmarks_v1, bookmarks_v2.
- Документируйте ключи в одном месте (константы в коде).
Пример констант:
class PrefKeys {
static const theme = 'settings.theme';
static const sessionId = 'user.sessionId';
static const bookmarks = 'bookmarks_v1';
}Тестирование и мокирование
Для юнит‑тестов пакет содержит утилиту setMockInitialValues(), позволяющую подменить значения, которые вернёт getInstance():
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
final values = {'foo': 'bar'};
SharedPreferences.setMockInitialValues(values);
final instance = await SharedPreferences.getInstance();
final foo = instance.getString('foo'); // 'bar'
} Рекомендации по тестированию:
- Мокируйте начальные значения для тестов компонентов, зависящих от настроек.
- Проверяйте сценарии отсутствия ключа (null) и несоответствия типа.
- Тестируйте миграции ключей при обновлениях формата.
Миграции и совместимость версий
При изменении структуры данных выполняйте миграцию:
- Введите новую версию ключа (например, bookmarks_v2).
- При первом запуске после обновления прочитайте старые данные, преобразуйте и запишите в новый ключ.
- Удалите старые ключи по завершении миграции.
Пример миграционной функции:
Future migrateBookmarksIfNeeded(SharedPreferences prefs) async {
if (prefs.containsKey('bookmarks_v2')) return; // уже перенесено
final old = prefs.getString('bookmarks');
if (old != null) {
final list = jsonDecode(old) as List;
// Выполните преобразование структуры здесь
await prefs.setString('bookmarks_v2', jsonEncode(list));
await prefs.remove('bookmarks');
}
} Безопасность и конфиденциальность
- Не храните пароли, токены или PII в SharedPreferences в открытом виде.
- Для чувствительных данных используйте платформенные хранилища ключей (Keychain/Keystore) или плагин с шифрованием (flutter_secure_storage).
- Если вы шифруете значения на уровне приложения, безопасно управляйте ключами шифрования и не сохраняйте их рядом с данными.
- Учитывайте локальные законодательные требования по хранению персональных данных (например, GDPR) при обработке идентифицируемой информации.
Notes: Шифрование увеличивает сложность и накладные расходы; взвесьте необходимость в зависимости от риска.
Проверка приемлемости и контроль качества
Критерии приёмки:
- Приложение корректно сохраняет и восстанавливает значения всех заявленных ключей.
- Миграции данных после обновления приложения выполняются без потери данных.
- Для чувствительных данных применён надёжный механизм защиты (или документировано, почему он не нужен).
- Модуль покрыт юнит‑тестами, включая сценарии отсутствия ключа и некорректных типов.
Руководство по инцидентам и откату
Если обнаружена потеря данных после записи:
- Проверить логи приложения на наличие ошибок записи.
- Убедиться, что приложение не завершилось сразу после вызова set.
- Если используется миграция, проверить логи миграции и целостность старых ключей.
- При необходимости откатить релиз, включив диагностические логи и добавив дополнительное резервное копирование данных.
Модель принятия решения: использовать SharedPreferences или нет
flowchart TD
A[Нужно ли хранить данные локально?] -->|Нет| B[Не нужно использовать SharedPreferences]
A -->|Да| C[Данные простые ключ‑значение?]
C -->|Да| D[SharedPreferences подходит]
C -->|Нет| E[Используйте SQLite/Hive/файлы]
D --> F[Данные чувствительны?]
F -->|Да| G[Используйте безопасное хранилище или шифрование]
F -->|Нет| H[Продолжайте с SharedPreferences]Примеры использования по ролям
Для разработчика интерфейса:
- Читаю тему при запуске и применяю её до отрисовки UI.
- Подписываюсь на изменение настроек через менеджер состояния.
Для бэкенд‑инженера мобильного приложения:
- Кэширую конфиг серверов и флаги функций.
- Версионирую ключи и обеспечиваю миграцию.
Для инженера по QA:
- Тестирую поведение при пустом кеше, при старых ключах и при одновременной записи.
- Проверяю сценарии с перезапуском приложения сразу после записи.
Краткое резюме
SharedPreferences — удобный инструмент для хранения простых пар ключ‑значение в приложениях Flutter. Он прост в использовании и хорошо подходит для предпочтений пользователей, флагов и небольших кэшей. Избегайте его применения для чувствительных, объёмных или структурированных данных. Для таких случаев выбирайте специализированные хранилища, добавляйте шифрование и планируйте миграции.
Summary:
- Используйте SharedPreferences для настроек и небольших кэшей.
- Не храните чувствительные данные без шифрования.
- Планируйте миграции и тестируйте сценарии отсутствия ключей.
Окончательная рекомендация: проектируйте ключи с префиксами, версионируйте формат и покрывайте поведение тестами, чтобы избежать потерь данных при обновлениях.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone