Гид по технологиям

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

6 min read Flutter Обновлено 01 Dec 2025
SharedPreferences в Flutter: руководство по использованию
SharedPreferences в Flutter: руководство по использованию

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

Дизайн ключей и методология

Мини‑методология проектирования ключей:

  1. Используйте префиксы по доменам: settings.theme, user.sessionId, cache.lastSync.
  2. Ограничивайте длину ключа и избегайте пробелов и спецсимволов.
  3. Версионируйте сложные структуры: bookmarks_v1, bookmarks_v2.
  4. Документируйте ключи в одном месте (константы в коде).

Пример констант:

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) и несоответствия типа.
  • Тестируйте миграции ключей при обновлениях формата.

Миграции и совместимость версий

При изменении структуры данных выполняйте миграцию:

  1. Введите новую версию ключа (например, bookmarks_v2).
  2. При первом запуске после обновления прочитайте старые данные, преобразуйте и запишите в новый ключ.
  3. Удалите старые ключи по завершении миграции.

Пример миграционной функции:

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: Шифрование увеличивает сложность и накладные расходы; взвесьте необходимость в зависимости от риска.

Проверка приемлемости и контроль качества

Критерии приёмки:

  • Приложение корректно сохраняет и восстанавливает значения всех заявленных ключей.
  • Миграции данных после обновления приложения выполняются без потери данных.
  • Для чувствительных данных применён надёжный механизм защиты (или документировано, почему он не нужен).
  • Модуль покрыт юнит‑тестами, включая сценарии отсутствия ключа и некорректных типов.

Руководство по инцидентам и откату

Если обнаружена потеря данных после записи:

  1. Проверить логи приложения на наличие ошибок записи.
  2. Убедиться, что приложение не завершилось сразу после вызова set.
  3. Если используется миграция, проверить логи миграции и целостность старых ключей.
  4. При необходимости откатить релиз, включив диагностические логи и добавив дополнительное резервное копирование данных.

Модель принятия решения: использовать 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 для настроек и небольших кэшей.
  • Не храните чувствительные данные без шифрования.
  • Планируйте миграции и тестируйте сценарии отсутствия ключей.

Окончательная рекомендация: проектируйте ключи с префиксами, версионируйте формат и покрывайте поведение тестами, чтобы избежать потерь данных при обновлениях.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство