1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Z2.md 33 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Выбор фреймворков для инженерилизации проекта Flutter — куда движется управление состоянием?

Эта статья является эксклюзивной публикацией сообщества Tuishe Juejin, запрещено перепечатывать в течение 14 дней, после 14 дней перепечатка возможна только с разрешения автора, иначе будут приняты меры за нарушение авторских прав!

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

На самом деле, это самый нелюбимый мной раздел.

Управление состоянием — это вечная тема в Flutter. Основная идея управления состоянием в Flutter заключается в передаче состояния и упаковке метода setState, а фреймворк управления состоянием решает вопрос более элегантного совместного использования состояний и вызова метода setState.

*Почему же мне совсем не хочется писать сравнение различных систем управления состоянием?*С одной стороны, это очень сложная тема. С момента выпуска Flutter до настоящего времени были представлены различные фреймворки управления состоянием, такие как scoped_model, BLoC, Provider, flutter_redux, MobX, fish_redux, Riverpod, GetX. Хотя это хорошо для сообщества, для обычных разработчиков это может привести к проблеме переизбытка выбора, особенно среди начинающих, многие из которых были запутаны различными фреймворками.

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

image-20221109095950845

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

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

Конечно, если вы действительно заботитесь о влиянии размера после интеграции, можно сгенерировать файл analysis.json при сборке с помощью ключа --analyze-size:```shell flutter build apk --target-platform android-arm64 --analyze-size


После выполнения этой команды будет создан файл `apk-code-size-analysis_01.json`, который находится в директории `/Users/ваш_пользователь/.flutter-devtools/`. Для анализа вам достаточно открыть инструмент App Size Tooling в Flutter DevTools.

| ![](http://img.cdn.guoshuyu.cn/20221109_Z2/image7.png) | ![](http://img.cdn.guoshuyu.cn/20221109_Z2/image8.png) |
| ------------------------------------------------------- | ------------------------------------------------------- |

Например, здесь показано сравнение JSON-файлов, полученных после интеграции Riverpod и GetX в одном проекте. Разница составляет всего 78,5 КБ, что меньше, чем влияние одной PNG-картинки.

![](http://img.cdn.guoshuyu.cn/20221109_Z2/image9.png)

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

> В данной статье мы лишь представляем особенности этих библиотек и предлагаем способы их использования, но подробное объяснение реализации доступно в предыдущих публикациях:
>
> > - [Полное исследование Flutter Riverpod](https://juejin.cn/post/7063111063427874847)
> >
> > - [Полное понимание State и Provider](https://juejin.cn/post/6844903866706706439)
> >
> > - [Полное понимание дизайна управления состоянием](https://juejin.cn/post/6844904035439345671)


# ProviderНа конференции Google I/O 2019 года [Provider](https://github.com/rrousselGit/provider) был представлен как один из новых рекомендованных способов управления состоянием в Flutter. Основные его характеристики заключаются в том, что он **не сложен, легко понятен и позволяет удобно комбинировать и контролировать гранулярность обновлений**, когда объём кода невелик. Начальный вариант этого решения назывался [flutter-provide](https://github.com/google/flutter-provide), однако он был прекращён, и [Provider](https://github.com/rrousselGit/provider) стал его заменой.> ⚠️ Обратите внимание, что название `provider` отличается от `flutter-provide` наличием буквы `r`, поэтому не следует путать эти два названия.

**Кратко говоря, Provider представляет собой инструмент для упрощённого использования InheritedWidget**. Он делает использование InheritedWidget более простым, позволяя передавать состояние вниз и использовать ChangeNotifier, Stream, Future вместе с Consumer* для создания различных моделей обновления. Использование библиотеки Provider имеет ряд преимуществ, одним из которых является простота. Вы можете контролировать гранулярность обновлений с помощью таких компонентов как `Consumer*`. Это также относится к уровню детализации `BuildContext`, который определяется при вызове метода `of(context)`.

> В контексте регистрации в `InheritedWidget` используется `BuildContext`, который управляет тем, какой `ComponentElement` будет перестроен при обновлении. Подробнее можно прочитать в статье «Полное понимание State и Provider».

*Конечно, хотя мы постоянно говорим о простоте использования Provider, есть некоторые более "сложные" аспекты, такие как `select`.*

Внутри библиотеки Provider, `select` представляет собой вторичную регистрацию `BuildContext`. Когда вы используете `context.watch()`, это просто регистрирует виджет в элементе. При наличии обновлений происходит уведомление.

![image10](http://img.cdn.guoshuyu.cn/20221109_Z2/image10.png)Однако `select` выполняет двойную проверку через использование `dependOnInheritedElement`, что позволяет выполнять гранулярное сравнение. Обновление происходит только если значения различаются. Поэтому `select` требует специфического типа `BuildContext`.

| ![image11](http://img.cdn.guoshuyu.cn/20221109_Z2/image11.png) | ![image12](http://img.cdn.guoshuyu.cn/20221109_Z2/image12.png) |
| ----------------------------------------------------------- | ----------------------------------------------------------- |

Таким образом, `select` можно назвать "маленьким магическим" элементом внутри Provider. В целом, **Provider — это отличная система управления состоянием, которая хорошо работает с поведением Flutter, но может не подходить для фронтенд-разработки или работы с нативными языками программирования**.

**Преимущества:**
- Простота и удобство обслуживания
- `read`, `watch`, `select` предоставляют более гранулярное управление
- Официально рекомендовано

**Недостатки:**
- Относительно зависим от Flutter и виджетов
- Требуется использовать `BuildContext`

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

| ![image13](http://img.cdn.guoshuyu.cn/20221109_Z2/image13.png) | ![image14](http://img.cdn.guoshuyu.cn/20221109_Z2/image14.png) |
| ----------------------------------------------------------- | ----------------------------------------------------------- |


# Riverpod[Riverpod](https://github.com/rrousselGit/riverpod) был создан тем же автором, что и Provider. **Из-за некоторых ограничений в Provider, автор создал Riverpod путём изменения порядка букв в слове Provider**.![](http://img.cdn.guoshuyu.cn/20221109_Z2/image15.png)

Если говорить о том, что Provider является упаковкой InheritedWidget, то Riverpod представляет собой переосмысление Provider с целью создания более гибкого набора возможностей. Самым очевидным отличием является то, что **Provider в Riverpod можно легко объявить как глобальный и использовать его без зависимости от BuildContext для реализации бизнес-логики**.

> Внимание: Provider в Riverpod никак не связан с Provider, который был рассмотрен ранее [(Provider)](https://github.com/rrousselGit/provider).

![](http://img.cdn.guoshuyu.cn/20221109_Z2/image16.png)

В Riverpod каждый "Provider" имеет свой уникальный "Element", а затем через `WidgetRef` создаётся хук, заменяющий `BuildContext`. Это одна из причин, почему Riverpod не зависит от контекста.

> ⚠️**Этот "Element" не относится к понятию "Element" в трёх деревьях Flutter; это "Element" внутри Riverpod, являющийся подклассом объекта Ref. Ref предоставляет интерфейсы для взаимодействия между "Provider" в Riverpod и абстрактные методы жизненного цикла, поэтому он является уникальной единицей в Riverpod**.

Кроме того, Riverpod не требует зависимости от Flutter, следовательно, он также не зависит от `Widget`, то есть от `BuildContext`, что позволяет ему поддерживать глобальные определения "Provider".**Преимущества:**

- Более гибкая реализация на основе Provider,
- Отсутствие зависимости от `BuildContext`, что освобождает бизнес-логику от необходимости внедрять `BuildContext`,
- Riverpod стремится решать проблемы потенциальных ошибок во время выполнения за счет компиляционной безопасности,
- Поддержка глобального определения,
- `ProviderReference` лучше решает проблему вложенной логики.

**Недостатки:**

- Реализация становится сложнее,
- Увеличивается стоимость обучения.

**С моей точки зрения, Riverpod является лучшим выбором для управления состоянием при текущих условиях — он гибкий и сосредоточенный, а опыт использования полностью соответствует привычкам разработки в Flutter**.

> Внимание: многие начинают использовать только `Riverpod`, но потом обнаруживают, что некоторые упакованные объекты отсутствуют, поскольку `Riverpod` не зависит от Flutter. Поэтому при использовании в Flutter не забудьте добавить зависимость `flutter_riverpod`.

# BLoC

[BLoC](https://github.com/felangel/bloc) считается одним из самых известных фреймворков управления состоянием в ранних версиях Flutter. Он также использует зависимости `bloc` и `flutter_bloc`, **реализуя управление состоянием на основе событий**.


![](http://img.cdn.guoshuyu.cn/20221109_Z2/image17.png)**Основой событийной модели в `flutter_bloc` являются `Stream` и Provider**, да, `flutter_bloc` зависит от Provider и использует на его основе механизм реакции на события через `Stream`.Поэтому строго говоря, BLoC — это просто совокупность Provider и `Stream`. Если вы привыкли работать с моделями событийных потоков, то вам будет удобнее всего использовать BLoC. **Однако, по моему личному опыту, BLoC не всегда ускоряет процесс разработки, а иногда даже усложняет его**, хотя преимущества тоже очевидны: использование `Stream` позволяет более легко прослушивать и преобразовывать состояние.

```dart
BlocSelector<BlocA, BlocAState, SelectedState>(
  selector: (state) {
    // Возвращаем выбранное состояние на основе предоставленного состояния.
  },
  builder: (context, state) {
    // Здесь возвращаем виджет на основе выбранного состояния.
  },
)

MultiBlocListener(
  listeners: [
    BlocListener<BlocA, BlocAState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocB, BlocBState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocC, BlocCState>(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)

Преимущества:

  • Код становится более декомпозирован, что является характеристикой событийной модели.
  • Объединение обновлений состояний и событий позволяет гибко реализовать перехват состояний, повторные попытки и откаты.

Недостатки:

  • Нужно писать больше кода, что может замедлить темп разработки.
  • Для новых участников проекта, особенно если нет эффективной документации, сложность может вызвать путаницу между событиями и бизнес-логикой.
  • На поздних этапах развития проекта события могут стать запутанными и переплетёнными.> Аналогичные библиотеки включают rx_bloc, который также основан на Stream и Provider, но использует Stream rxdart для своей реализации.

flutter_redux

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

Возможно, начинающие фронтенд-разработчики будут знакомы с Redux, так как многие из нас использовали его в React Native. Поэтому flutter_redux был естественным выбором для меня при переходе на Flutter.

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

В flutter_redux, каждый действие разработчика представляет собой просто Action. Логика, которая срабатывает при выполнении этого действия, полностью определяется middleware и reducer. Такой подход в некоторой степени разделяет бизнес-логику и UI, а также унифицирует управление состоянием.

Конечно, недостатки тоже очевидны — вам придётся писать множество кода, что может не очень хорошо сочетаться с привычками разработки в Flutter.Преимущества:

  • Разделение ответственности
  • Дружественный к разработке с использованием Redux
  • Подходит для командной работы над средними и крупными проектами

Недостатки:

  • Уменьшает скорость разработки за счёт необходимости писать много шаблонного кода
  • Может не совпадать с общими подходами к разработке в Flutter

Разговор о Redux неминуемо ведёт к fish_redux. Если говорить о Redux как о конструкторе из Lego, то fish_redux, открытый Alibaba Group, можно сравнить с конструктором из Lego. Fish_redux предлагает концепцию компонентов, которая позволяет ему "вторгаться" во все аспекты вашего кода, начиная от Context до Widgets, тем самым создавая "ультра-версию" Redux.

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

GetX

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

Строго говоря, сегодня GetX уже не является простым фреймворком управления состоянием; он представляет собой единую коллекцию инструментов для разработки Flutter. Внутри GetX вы найдёте:- Управление состоянием

  • Управление маршрутами
  • Поддержка нескольких языков
  • Управление страницами
  • HTTP запросы через GetConnect
  • Реактивные потоки данных через GetStream
  • Различные расширения

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

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

image image

Конечно, наиболее очевидной особенностью использования GetX является отсутствие необходимости использовать BuildContext. Даже при переходах между маршрутами вам не нужно беспокоиться о контексте, что делает ваш код чистым и позволяет сосредоточиться на "разработке с использованием GetX".

Кроме того, GetX отличается от других библиотек, таких как Provider:- Операции, такие как Get.put, Get.find, Get.to, не требуют вмешательства виджетов

  • Встроенные расширения, такие как *.obs, позволяют легко создавать наблюдаемые переменные через GetStream, например var count = 0.obs; и Obx(() => Text("${controller.name}"));Как же GetX освобождается от зависимости от BuildContext? Это довольно просто:

  • Внутри GetMaterialApp используется глобальный ключ GlobalKey, который конфигурируется для navigatorKey в MaterialApp. Этот ключ позволяет получить доступ к состоянию навигатора через navigatorKey, что позволяет вызывать методы, такие как push.

  • Get.put и Get.find используют внутренний глобальный статический Map для управления экземплярами контроллеров, что позволяет избежать использования InheritedWidget. Объединение этого подхода с Obx обеспечивает добавление слушателя событий к значениям контроллера, что позволяет реализовать привязку и обновление данных через поток.

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

Это также порождает проблему: новички часто начинают работать сразу с GetX, не понимая Flutter полностью, особенно после полной деконнектации от BuildContext. Такие вопросы, как "как использовать Google Maps в GetX", становятся распространёнными.

> Если вы используете GetX, но не пытаетесь понять его реализацию, вы можете легко запутаться на пути изучения Flutter, столкнувшись со множеством базовых вопросов. Это также является одним из самых больших недостатков GetX: GetX делает слишком много, он вторгается во многие области и имеет множество "магических" решений, которые заставляют Flutter-разработчиков отклоняться от нормального пути развития.image25.png

Конечно, вы можете сказать, что мне просто нужно выполнить требования, и если это работает, то почему бы не использовать эти "магические" решения? Из этой точки зрения GetX действительно является отличным выбором, если он будет продолжать поддерживаться и его "магия" будет совместима.

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

Преимущества:

  • Швейцарский нож для разработчиков
  • Дружелюбность для новичков
  • Уменьшение количества необходимого кода

Недостатки:

  • Наличие всего "набора", что может быть смертельным недостатком для некоторых разработчиков, увеличивает количество ошибок, которые нужно исправить
  • Большое использование "магии", которое отклоняет развитие от первоначальной цели Flutter
  • Высокий уровень вторжения

В целом, GetX очень хорош, он помогает вам автоматически выполнять много вещей, экономя время разработчикам на соображении о том, как лучше объединить различные компоненты. Хотя лично я не одобряю этот подход, он всё же позволяет повысить эффективность разработки.Еще один популярный менеджер состояний — Mobx, который использует аналогичный стиль работы, как и GetX. Хотя известность и внимание к Mobx не так велики, как к GetX, он также использует скрытую зависимость, поэтому в некотором смысле Mobx можно рассматривать как версию GetX, которая занимается только управлением состояния.image26.png

В заключение

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

И последнее, если у вас есть вопросы или вы ещё не уверены в своём выборе Flutter-проекта, пожалуйста, оставьте свои комментарии.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master