Flutter — сначала офлайн, с Flutter_Data

Flutter Data — это пакет, который делает ваше приложение в первую очередь автономным.

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

В этой статье мы добавим и подключим пакет flutter_data для получения рецептов при запуске приложения.

Мне потребовалось некоторое время, чтобы понять, что я хотел, и набросать дизайн:

Происходило много всего, и было бы сложно разобраться.

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

  • Шаг 1. Загрузите значения среды из app_config.json при запуске.
  • Шаг 2: Добавьте новые пакеты.
  • Шаг 3: Аннотируйте модель рецепта.
  • Шаг 4: Настройте Flutter Data.
  • Шаг 5: Переопределите клиент Http для использования Dio.
  • Шаг 6: Настройте фиктивный адаптер для разработки и тестирования.

Вы можете найти детали реализации в разделе XP.

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

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

Ta Da

GlobalEnvironmentValues.instance
   .loadValues(await rootBundle.loadString("app_config.json"));

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

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

Как только я установлю аутентификацию, я вернусь и включу Flutter Data для использования облачных сервисов API на основе среды, я ожидаю настроить режим dev_online и dev_local и использовать сборки развертывания для настройки версий UAT и Live.

XP

Детали реализации

Шаг 1. Загрузить значения среды при запуске

Служба GlobalEnvironmentValues ​​загружает значения app_config.json с помощью EnvConfigReader:

import 'dart:convert';

// Access to enviromental values that are loaded from a config file.
// The CI/CD injects the per environment files on deployment.
class EnvConfigReader {
  late Map<String, dynamic> _config;

  EnvConfigReader(String configFileJson) {
    _config = json.decode(configFileJson) as Map<String, dynamic>;
  }

  String value(String key) {
    return _config[key];
  }
}

Шаг 2. Добавьте новые пакеты

# Offline-first data framework with a customizable REST client and powerful model relationships.
flutter_data: ^1.4.7

# A reactive state caching and data-binding framework
# Recommend replacement for provider: that is mentioned by flutter 
# https://docs.flutter.dev/development/data-and-backend/state-mgmt/options
hooks_riverpod: ^1.0.3+1

# Annotations used by code gen to create code for JSON serialization and deserialization
json_annotation: ^4.7.0

# Generate to/from JSON code for a class
json_serializable: ^6.5.4

Шаг 3. Аннотирование модели рецепта

import 'package:flutter_data/flutter_data.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:simbucore/app/mixin/flutter_data_dev_api_server_adapter.dart';

part 'recipe.g.dart';

@JsonSerializable()
@DataRepository([FlutterDataApiServerAdapter])
class Recipe extends DataModel<Recipe> {
  @override
  final int? id;
  final String title;
  final String link;
  final String source;
  final String totalTime;
  final HasMany<Ingredient> ingredients;
  final HasMany<Step> steps;
  final String thumbnail;

Его импорт для добавления ссылки part в файл, который будет сгенерирован, вы увидите ошибку, пока не запустите компоновщик на следующем шаге:

В отношениях необходимо использовать HasMany и BelongsTo, см. документацию для получения дополнительной информации.

Миксин FlutterDataApiServerAdapter устанавливает базовый URL-адрес API, используя значения среды, которые мы теперь загружаем при запуске.

Шаг 4. Настройка Flutter_Data

Создайте репозиторий рецептов

flutter pub run build_runner build

Нам нужно было сгенерировать репозиторий, прежде чем мы сможем инициализировать его при запуске, поскольку генерация кода создает этот метод инициализации в файле, который он генерирует:

Теперь мы можем подключить его при запуске приложения:

Шаг X. Имитация ответов API

Этот переработанный шаг X – фиктивный ответ API теперь заменяет два запланированных шага:

  • Шаг 5. Переопределение Http-клиента для использования Dio
  • Шаг 6. Настройка фиктивного адаптера

После более глубокого погружения в код Flutter Data стало очевидно, что попытка использовать Dio будет означать работу против пакета, большая часть преимуществ заключается в коде, который он использует для таких методов, как watchOne, Save, findAll.

Я нашел то, что мне было нужно, посмотрев, как Flutter Data проверил собственный код и решил переопределить HttpClient:

Есть и другие преимущества, которые принес бы Dio, но похоже, что Flutter Data можно легко расширить с помощью пользовательских адаптеров для обработки таких вещей, как токены аутентификации и повторные попытки. На сайте Flutter Data есть несколько статей и примеров кода.

Почему я предпочитаю использовать Flutter_Data

Использование пакетов — отличный способ упростить и сократить код проекта, облегчив его обслуживание.

Мы увидели это в действии, когда представили микроприложение DigestablePrologue, которое позволило мне удалить сквозной код в основном проекте DigestableMe.

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

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

Переломным моментом для меня стало то, что Flutter_Data предоставил большое количество функций, которые я искал, и идеально подходил для Riverpod:

  • Используйте генерацию кода для создания репозиториев CRUD из аннотированных классов.
  • Локальные базы данных Hive.
  • Обнаружение в автономном режиме.
  • Реактивная привязка данных.
  • Обработка ошибок и повторных попыток.
  • Автоматическая синхронизация с RestAPI при подключении к сети.
  • Проходимые отношения.
  • Работает с Риверпод.
  • Поддерживает JSON: API, который я хотел бы использовать.
  • Встроена сериализация JSON.
  • Возможность реализовать неизменность с помощью Freezd.

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

Быть самоуверенным может быть настоящей силой в команде, но как прийти к такому консенсусу — сложная часть, обычно это требует более тесной совместной работы.

За прошедшие годы я обнаружил, что парное программирование реально помогает при формировании команды и в начале проектов.

API среды

Добавьте файл в корень проекта app_config.json для хранения адреса конечной точки API разработки, он будет перегружен сборкой CodeMagic для поддержки других сред, подробности см. в их статье Среды во Flutter с CD CodeMagic.

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

Значения доступны через поставщика, который считывает значения при запуске приложения.

Гибкое обучение и экспериментирование

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

Когда я представил Flutter_Data в этой статье, я понял, что он справился с обнаружением и мог бы сэкономить мне некоторое время. Однако:

Вы не можете соединить точки, глядя вперед; вы можете соединить их только оглядываясь назад…

Стив Джобс

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

Данные флаттера — отношения

Построитель принудительно аннотировал все связанные модели на графике:

Данные флаттера — флаг IsOfflineError

Файлы локальной базы данных Hive

Hive записывает свои локальные файлы БД здесь:

/Users/<user>/Library/Developer/CoreSimulator/Devices/060DB285-2820-411B-B5F7-76F9739DA934/data/Containers/Data/Application/A72C3081-43DD-4E03-82D0-DC49A3AF1317/Documents/flutter_data

Базовый каталог получается с помощью функции getApplicationDocumentsDirectory в пакете провайдера пути, вы можете найти код в сгенерированном файле main.data.dart.

...
if (!kIsWeb) {
    baseDirFn ??= () => getApplicationDocumentsDirectory()
    .then(
      (dir) => dir.path
      );
  } else {
    baseDirFn ??= () => '';
  }
...

Вы можете установить свой собственный базовый каталог, если переопределите параметр baseDirFn в методе инициализации configureRepositoryLocalStorage.

Войдите в код внешнего пакета

Чтобы найти путь к каталогу, в который у Hive не было разрешений на запись, мне пришлось включить возможность отладки кода пакета.

Я перезапустил Visual Studio Code, чтобы это заработало, но вам может потребоваться только перезапустить приложение.

Задняя конфорка

Диагностика данных флаттера

Я хотел бы иметь возможность включить ведение журнала диагностики, которое выводит ключевые действия, которые выполнялись Flutter Data, такие как извлечение из Hive в автономном режиме, и выводить информацию о любых исключениях, особенно сопоставление из/в json.

Дисциплина и эшафот

После того, как я несколько раз проверю и использую данные Flutter в действии, я создам вокруг них новую дисциплину и улучшу структуру.

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

Задержка и чат

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

Пакеты и зависимости

Внедрив пакет Flutter_Data, я попытался обновить Riverpod до версии 2.1.1 и получил следующее:

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

Это кандидат на некоторые дополнительные отчеты или инструменты, которые помогут нам масштабироваться.

В этом случае оказывается, что для Flutter_Data требуется Riverpod версии 1.x.

Я решил это, указав несколько более высоких номеров версий в pubspec.yaml для Riverpod и Flutter_Data в пакете simbucore, что упростило сообщение об ошибке.

Выполнение команды:

flutter pub outdated

Показывает версию, которая может быть разрешена:

Ссылки

Пакеты