Не все данные создаются одинаково - и не все данные должны храниться одинаково. Подобно изучению HashMaps, связанных списков, куч и т. Д., Важно знать различные способы хранения данных в вашем приложении Flutter, чтобы обеспечить наилучшее взаимодействие с пользователем.

Например, что произойдет, если вам не нужно десериализовать весь объект - только одно поле или значение? Нет смысла извлекать из памяти весь config.json, когда все, что вам нужно, - это одно логическое поле, например isDarkMode=false. Напротив, нет смысла хранить большие отдельные объекты в таблице - если у вас когда-либо будет только одна строка, какой смысл разрабатывать всю схему?

Я рассмотрю каждый из четырех вариантов локального хранения данных:

  • Какую нишу заполняет хранилище
  • Реалистичные варианты использования
  • Примеры с рекомендациями по упаковке

Если вы собираетесь написать приложение Flutter, я рекомендую добавить эту страницу в закладки, чтобы вы всегда могли ссылаться на нее, пытаясь понять, как хранить данные.

Простое ключевое значение - общие настройки

Скажем, пользователь открывает ваше приложение; они открывают настройки и переводят приложение в темный режим. Они возвращаются через минуту или две, но их ослепляет яркая вспышка в световом режиме.

Пользователь пишет резкий 1-звездочный обзор вашего приложения и обвиняет свою потерю зрения в том, что вы не запомнили, что он был в темном режиме.

Каким-то образом нам нужно хранить такие мелкие детали, для которых не нужны большие объекты или сильно структурированные базы данных - простые поля, такие как «IsDarkMode» или «LastTabUsed» или аналогичные. Вещи, которые не требуют безопасного хранения, но их приятно иметь, и, как правило, они довольно крошечные (подумайте об отдельных полях).

Эти данные идеально подходят для SharedPreferences! Хотя это флаттер-пакет, он включает в себя собственные API-интерфейсы для iOS и Android, которые предназначены для выполнения именно того, что я только что описал. Эти данные можно быстро получить, но они небезопасны и задерживаются при чрезвычайно высоких нагрузках.

Я использую это в своем приложении, помимо прочего, для хранения некоторых локальных настроек (вы отключили жест? Вы хотите отключить автокоррекцию при написании сообщений?) Имейте в виду, что в средах с ограниченным объемом памяти система выиграла ' Не стесняйтесь удалять их, если они занимают много места - это означает, что ни одно из ваших общих предпочтений не должно быть жизненно важной функциональностью приложения.

Пример общих настроек (украдено из документов):

SharedPreferences prefs = await SharedPreferences.getInstance();
  int counter = (prefs.getInt('counter') ?? 0) + 1;
  print('Pressed $counter times.');
  await prefs.setInt('counter', counter);

Чувствительная ключевая ценность - безопасное хранилище

В отличие от предыдущего SharedPreferences, введите flutter_secure_storage. Это еще один пакет, который является оболочкой как для iOS, так и для Android API.

Представьте, что кто-то взламывает телефон пользователя и проверяет данные, стоящие за shared_preferences - они увидят тривиальные данные, которые не имеют никакого значения и никоим образом не помогают хакеру получить преимущество.

Теперь представьте, что вы храните токен обновления или локальный PIN-код в общих настройках - теперь хакер может использовать эти учетные данные для выдачи себя за пользователя и, таким образом, успешно взломал ваше приложение.

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

Войдите в безопасное хранилище. Безопасное хранилище предназначено для вещей, которые важны для пользователя. Это также простое хранилище значений ключей, как и предыдущий вариант, то есть оно не предназначено для больших данных или больших значений.

(Боковой совет: если есть большой объект, который вам нужно сохранить в памяти, но он чувствителен, может быть лучше не помещать его в безопасное хранилище: вместо этого сериализуйте объект, зашифруйте его с помощью шифра, такого как AES или аналогичный, и сохраните его в файловом хранилище. Затем сохраните ключ в безопасном хранилище. Таким образом, если хакер доберется до него, он по-прежнему будет бесполезен для них, но будет полностью читаемым с вызовом файлового хранилища и безопасного хранилища.)

В своих приложениях я только лично сталкивался с одним вариантом использования безопасного хранилища, а именно с токенами обновления. Однако я могу представить себе множество других вариантов использования: например, если у вас есть клиентское приложение, которое надежно хранит данные, например KeepSafe или подобное, вам как-то нужно сравнить пароль с хешем. Вы можете сохранить хэш в SecureStorage, а затем использовать пароль в памяти в качестве ключа для расшифровки остальных данных.

Пример безопасного хранилища (адаптировано из документации):

final storage = new FlutterSecureStorage();
String myVal = "Thanks for reading my article!";
await storage.write(key: key, value: value);
String readValue = await storage.read(key: key);
assert(myVal == readValue); //passes

Высокоструктурированные таблицы - Hive / SQLite

Я предпочитаю использовать Hive во внешнем интерфейсе, но это действительно зависит от вас - если вам нравится жесткость, которую дает вам использование реляционных таблиц, дерзайте! В общем, для большого количества данных (не менее 1 ГБ - и это низкая оценка) перейдите по пути SQLite (или, если выделено жирным шрифтом, isar), в противном случае все в порядке. используя Hive.

Если пользователь находится в активном групповом чате, вы не хотите повторно получать из бэкэнда все 3k + сообщений перед загрузкой приложения - это создает излишне долгое время ожидания и требует удаленного подключения при каждой загрузке. Также не имеет смысла загружать все данные одним пакетом - например, зачем вам загружать сообщение двухлетней давности, если пользователь просматривает только сообщения, отправленные сегодня?

Это варианты использования для табличных хранилищ: многие объекты, которые можно загружать по частям - данные, которые достаточно структурированы, чтобы вы могли предоставить только пару ключей и найти то, что вы ищете, но вы не можете реально сохранить все это в один объект.

Если у меня более одной строки (т. Е. Я храню много пользовательских данных, а не только тот, который использует приложение), я всегда иду по пути создания таблицы для этих данных.

Нет примера для этого, потому что есть много пакетов, которые служат для этой цели. Вместо этого вот ссылки pub.dev из 3, которые я рекомендую:

  • Улей - База данных ключевых значений
  • Isar - АЛЬФА большие, гибкие реляционные базы данных (сделано создателем Hive)
  • Sqflite - база данных на базе SQLite с простыми SQL-запросами

Большой временный / постоянный единый объект - локальное файловое хранилище

Это первый вариант, который не требует пакета, но установка path_provider значительно упростит вашу жизнь при использовании этого метода хранения файлов.

Значение ключа отлично подходит для множества несвязанных данных, но что происходит, когда вам нужно сохранить имя пользователя… и фамилию, и дату рождения, и псевдоним… Не имеет смысла хранить это в формате значения ключа - и было бы бессмысленно создавать всю локальную таблицу базы данных для хранения только вашего локального пользователя.

Войдите в локальное хранилище файлов! Это именно то, на что это похоже - вы можете хранить файлы локально. В своих сценариях использования я обычно сериализую объекты в jsons, а затем сохраняю их как объект json в памяти. Это позволяет мне с легкостью сериализовать и десериализовать без потери данных, и я могу работать с объектами сколь угодно больших размеров.

В этом маркере есть два разных типа файловых хранилищ - временные и постоянные.

Для временного использования подумайте о данных, которые не нужно создавать резервные копии - вы можете легко повторно получить их из базы данных, если это необходимо, но для вас и вашей серверной части было бы проще, если бы пользователь просто сохранил их локально.

Это то, что вам необходимо сохранить навсегда - эти данные никогда не будут очищены и постоянно хранятся на устройстве до тех пор, пока приложение не будет удалено (или вы удалите их вручную!). Если вы не храните здесь большие объемы данных, у вас, вероятно, не возникнет проблем, но лучше выбрать каталог с умом, чтобы Apple или Android не злились на вас за злоупотребление их системами.

Для меня это предназначено для одиночек или мест, где у меня есть «постоянные ключи». В моем приложении я храню здесь свои локальные пользовательские данные (ключ API, имя и т. Д.) И пользовательские данные (время, проведенное в приложении, отправленные сообщения), а также данные, содержащие первичные ключи моей базы данных Hive - при загрузке приложения. с самого начала, и все мои базы данных улья проиндексированы как Box('{groupUuid}'), мне нужно где-то хранить все uuid группы. Я сохраняю его в файле csv во временном локальном хранилище (потому что я могу восстановить группы, в которых находится пользователь, из бэкэнда).

Пример (адаптированный из моего собственного кода):

final filename = "local_user";
final file = File('${(await getApplicationDocumentsDirectory()).path}/$filename.json');
file.writeAsString(json.encode(user.toJson()));
User.fromJson(file.readAsString());

Бонус! Общие данные - удаленно

Вот бонус: когда мы говорим о том, где хранить данные локально, полезно сделать шаг назад и вспомнить, что локальное хранение данных - не единственный вариант. Вы также можете хранить данные удаленно - на своих серверах или в базах данных где-нибудь в облаке. Как правило, если вы ответите утвердительно на любой из следующих вопросов, вам захочется сохранить на своем сервере:

  • Вы проводите какой-либо анализ данных?
  • Вы делитесь этими данными между пользователями?
  • Если пользователь удаляет приложение, должны ли эти данные сохраняться?
  • Следует ли передавать эти данные между устройствами (мобильными, настольными и т. Д.)?

Однозначное «да» на любой из этих вопросов означает, что вы, вероятно, захотите сохранить хотя бы часть данных на сервере. Однако это не означает, что вы не можете иметь его на устройстве: я часто использую локальное хранилище данных в качестве своего рода кеша - если данные должны быть разделены между пользователями, они загружаются в облако, но в этом нет никакого смысла. удалив его локально, если пользователь все еще хочет его увидеть.

Таким образом, вы можете выполнять двойную работу (писать схему для своей базы данных и для внешнего интерфейса), но, в конце концов, мы пытаемся доставить удовольствие нашим пользователям, так что для нас немного лишней работы?

Вот и все! Надеюсь, я рассмотрел все основы локального хранения данных во Flutter. Если вы обнаружите ошибку, не стесняйтесь оставлять комментарий - я хочу исправить ее как можно скорее.

Я всегда хочу научиться чему-то, поэтому, если я что-то пропустил, оставьте комментарий, чтобы я мог поучиться у вас.

Если вы ищете мои рекомендации по пакетам, вот 7 пакетов Flutter, которые мне нравятся:

Https://levelup.gitconnected.com/the-7-flutter-packages-i-cant-live-without-9c18ac8540bd

Я также написал статью о моих любимых решениях для управления состоянием:

Https://levelup.gitconnected.com/flutter-state-management-in-2021-when-to-use-what-98722093b8bc