Flutter — Создание идеального проекта Boilerplate с нуля

Dec 01 2022
Предоставлено: Nguyễn Thành Minh (Android Developer) Мы все были в этом — я имею в виду, что мы все пытались создать шаблонный проект либо для работы, либо в качестве личного проекта. Хороший шаблонный проект сэкономит нам много времени, решив общие проблемы, такие как вызов API, обработка данных из баз данных и, возможно, самое главное — сделает наш код организованным и согласованным, чтобы людям было легче пытаться понять наш код.

Предоставлено: Нгуен Тхань Минь (разработчик Android)

Мы все были в этом — я имею в виду, что мы все пытались создать шаблонный проект либо для работы, либо в качестве личного проекта. Хороший шаблонный проект сэкономит нам много времени, решив общие проблемы, такие как вызов API, обработка данных из баз данных и, возможно, самое главное — сделает наш код организованным и согласованным, чтобы людям было легче пытаться понять наш код. Эта серия предназначена для того, чтобы поделиться шаблонным проектом, который я создал, использовал и поддерживал в течение последних двух лет. Мало того, я поделюсь своим подходом к кодированию, а также решениями общих проблем, которые могут возникнуть. С учетом сказанного, это часть 1: Изучение проекта.

Посмотреть на Гитхабе

Архитектура проекта

1. Особенности

Особенности, встроенные в этот проект:

  • Архитектура: чистая архитектура
  • Управление состоянием: flutter_bloc
  • Навигация: auto_route
  • DI: get_it , инъекционный
  • REST API: дио
  • GraphQL: артемида , graphql_flutter
  • База данных: объектбокс
  • Общие настройки: protected_shared_preferences
  • Класс данных: заморожен
  • Линт: dart_code_metrics , flutter_lints
  • CI/CD: действия Github, конвейеры Bitbucket
  • Модульный тест: mocktail , block_test
  • Пейджинг: бесконечный_scroll_pagination
  • Утилиты: rxdart , dartx , асинхронный
  • Генератор ассетов: flutter_gen_runner , flutter_launcher_icons , flutter_native_splash
  • Другие функции: автоматический повтор неудачных запросов API, токен обновления,…

Чтобы запустить этот проект по назначению, ваша версия Flutter должна быть (точно) 3.3.9, а версия Melos 2.8.0 должна быть установлена. Если вы хотите организовать Git Convention для своей команды, вы также можете установить lefthook . Я расскажу о Мелосе и Лефтхуке в более поздних частях серии. На данный момент, вот как запустить проект.

Примечание. Я использовал VSCode и macOS, поэтому не могу гарантировать, что он будет работать на Android Studio или Windows.

2.1. Установка Мелос

Чистая архитектура требует от нас разделения проекта на несколько модулей (пакетов), поэтому нам нужен инструмент для управления пакетами — Melos. Чтобы установить Мелос:

dart pub global activate melos 2.8.0

export PATH="$PATH:<path to flutter>/flutter/bin"
export PATH="$PATH:<path to flutter>/flutter/bin/cache/dart-sdk/bin"
export PATH="$PATH:~/.pub-cache/bin"

После установки Melos вам нужно будет запустить команду make gen_envили dart run tools/gen_env.dart. Это инструмент, который я создал сам, чтобы создать envпапку, чтобы мы могли объявлять секреты, такие как Google API_KEY, основные учетные данные аутентификации и т. д. Этот инструмент будет считывать значения в .envфайле, а затем помещать их в dart-defineфлаг, когда приложение Flutter запустить или построить.

Папка Env и файлы .env, соответствующие их средам

2.3. Создание файлов

Когда у нас есть envпапка, нам нужно только запустить команду make sync. Это сокращение для трех команд:

melos bootstrap
melos run l10n
melos run force_build_all

Далее команда melos run l10nпомогает сгенерировать класс Sиз .arbфайлов в resourcesмодулях.

Наконец, команда melos run force_build_allпомогает генерировать файлы .g.dart, .freezed.dartи т. д. для всех пакетов, содержащих библиотеку build_runner.

Теперь бегите, make run_devчтобы запустить приложение и наслаждайтесь!

3. Архитектура проекта

Всего у нас 6 модулей (пакетов)

  • Приложение
  • Домен
  • Данные
  • Ресурсы
  • Общий
  • Инициализатор

3.1. Модуль приложения

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

Модуль приложения

Обе папки app_iconи splashсодержат файлы, app-icon.yamlпоэтому splash.yamlмы можем настроить значок приложения и заставку в соответствии с инструкциями библиотек flutter_launcher_icons и flutter_native_splash .

Папка appсодержит AppBloc, который является блоком, представленным в MaterialApp, место, откуда все экраны в приложении могут получать данные, другими словами; это похоже на блок, доступный для всех экранов. Вы можете использовать AppBlocдля задач, требующих обновления пользовательского интерфейса всех экранов, таких как настройка языка, переключение темного/светлого режима.

В этой baseпапке мы будем кодировать базовые классы, такие как BasePageState, BaseBlocи BaseEvent.BaseState

В этой папке shared_viewя буду кодировать пользовательский интерфейс, общий для многих экранов, и затем его можно будет использовать для других проектов. Например: app_text_field, circle_image— это общие групповые представления, отображаемые на экранах входа и регистрации.

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

В этой папке configя буду вызывать функции конфигурации для модуля приложения, напримерFirebase.initializeApp()

Папка diпредназначена для настройки DI для модуля приложения.

Папка exception_handlerпозаботится обо всех исключениях в проекте.

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

В этой папке navigationмы будем объявлять экраны, а также диалоги и нижние листы, используемые в приложении.

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

В папке uiя буду кодировать пользовательский интерфейс и блок для каждого экрана.

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

3.2. Модуль домена

Модуль домена

Подобно модулю приложения, папки configи diслужат для аналогичных целей в модуле домена.

Папка entityсодержит объекты класса, такие как Пользователь и Бронирование.

В папке navigationесть AppNavigatorкласс, отвечающий за push, replace, и popэкраны.

В этой папке repositoryмы будем объявлять классы Abstract Repository в проекте.

В этой папке usecaseмы будем объявлять варианты использования в проекте.

3.3. Модуль данных

Модуль данных

Точно так же папки configи diслужат здесь той же цели.

Папка graphqlсодержит .graphqlи schema.graphqlесли ваш проект использует GraphQL. Файл build.yamlиспользуется для настройки сгенерированных файлов, связанных с GraphQL.

Папка repositoryсодержит классы реализации репозитория. Он также содержит несколько папок:

  • converter: для кодирования классов Converter
  • mapper: для кодирования классов Mapper, которые затем используются для сопоставления моделей данных с объектами.
  • model: для кодирования классов модели данных
  • source: для кодирования базовых классов, таких как RestApiClient, GraphQlApiClient, а также для предоставления источников данных в проекте, таких как AppApiService, AppDatabase, AppSharedPreferences, LocationDataSource.
Модуль ресурсов

Это простой модуль, он содержит только .arbфайлы, которые предоставляют строки для локализации. Этот модуль внедряется в два других модуля, appпоэтому domainего могут использовать классы UI, Bloc, Entity и Use Case.

3.5. Общий модуль

Общий модуль

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

3.6. Модуль инициализатора

Модуль инициализатора

Этот модуль содержит только класс AppInitializer. Этот класс отвечает за сбор всех конфигов из других модулей в init()функцию; Функция main()должна вызвать эту init()функцию только для получения всех конфигураций из всех модулей.

3.7. Другие папки и файлы

Папка .githubпредназначена для настройки CI/CD, если ваш проект использует Github Actions.

Папка .lefthookи файл lefthook.ymlпредназначены для настройки соглашения Git с использованием библиотеки lefthook.

Папка .vscodeпредназначена для объявления настроек VSCode, используемых для этого проекта.

Папка .envпредназначена для объявления секретов для каждой среды.

Папка toolsсодержит инструменты, которые я создал.

Файл analysis_options.yamlпредназначен для объявления правил линтера двух библиотек flutter_lints и dart_code_metrics .

Файл bitbucket-pipelines.ymlпредназначен для настройки CI, если ваш проект использует конвейеры Bitbucket.

Файл makefileотслеживает все команды, используемые в проекте. Например, команда make formatпомогает отформатировать весь код с .dartрасширением файла в проекте. Есть еще много команд, например make test, запускающих Unit Test на всех модулях.

Файл melos.yamlпредназначен для настройки Melos.

Вывод

Зачем нам нужно создавать еще один модуль только для конфигурации инициализации? Что такое классы сопоставления, сущностей и вариантов использования? Я расскажу об этом во второй части этой серии — «Чистая архитектура».