- PVSM.RU - https://www.pvsm.ru -
Привет! Представляю перевод статьи, Let me help you to understand and choose a state management solution for your app [1], которая попалась мне и заинтересовала в процессе изучения азов управления состоянием во Flutter. Буду рад услышать любую критику касательно данного перевода. В обратных кавычках (``) будут написаны мои личные мысли и пояснения.
Управление состоянием во Flutter — горячая тема. Возможных вариантов решения задачи много и запутаться в них, выбирая наиболее подходящий под ваши потребности — крайне просто. Я сам путался, но нашел подходящее решение. Позвольте поделиться им с вами.
Чтобы найти решение, подходящее к вашим потребностям, необходимо определить сами потребности. В моем случае это:
Учитывая эти требования, подходящими вариантами остаются:
setState()
и Stateful-виджетов
Перед тем, как погрузиться в анализ отобранных решений, необходимо понять разницу между локальным и глобальным состоянием. Для этого подойдет практический пример:
Представим форму авторизации, где пользователю предлагается ввести логин и пароль и получить объект «личности пользователя» после отправки формы. В этом примере любая проверка данных, вводимых в поля формы — будет являться частью локального состояния `виджета формы авторизации`, и остальная часть приложения не должна знать об этом. А возвращаемый `сервером авторизации` объект «личности» — частью глобального состояния. Так, как от этого объекта зависят другие компоненты, меняющие поведение в зависимости от того, авторизован ли пользователь.
Моя рекомендация — использовать BLoC для управления локальным состоянием и Redux для глобального состояния, особенно, если вы создаете сложное приложение, которое будет расти со временем.
Использование setState()
внутри ваших виджетов отлично подходит для быстрого создания прототипов, и получения обратной связи на эти вносимые изменения, но данный путь не помогает нам достичь поставленных целей, потому логика отображения смешивается с бизнес логикой, чем нарушается принцип чистоты и качества кода. Сопровождение подобного кода будет сложным в будущем, поэтому кроме как для создания прототипов, данный подход не рекомендуется.
ScopedModel [2] — библиотека стороннего разработчика Brian Egan [3]. Она дает возможность создавать специальные объекты Models
, а также использовать метод notifyListeners()
тогда, когда это необходимо. Например, для отслеживания на любого изменения свойства объекта модели:
class CounterModel extends Model {
int _counter = 0;
int get counter = _counter;
void increment() {
_counter++;
notifyListeners();
}
}
В наших виджетах мы сможем реагировать на изменения в модели с помощью `предоставляемого данной библиотекой` виджета ScopedModelDescendant
:
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new ScopedModel<CounterModel>(
model: new CounterModel(),
child: new Column(children: [
new ScopedModelDescendant<CounterModel>(
builder: (context, child, model) => new Text('${model.counter}'),
),
new Text("Другой виджет, который не зависит от CounterModel")
])
);
}
}
В противовес использованию подхода setState()
, данное решение позволяет отделить логику отображения от бизнес логики. Однако, оно накладывает определенные ограничения:
Model
становится сложной, то сложным становится и определение того, когда нужно использовать метод notifyListeners()
, а когда нет — чтобы избежать лишнего обновления интерфейсаModel
, в общем, не точно описывает асинхронную природу интерфейса приложенийУчитывая все это, если состояние вашего приложения не легкое для управления — я не рекомендую использовать данных подход. Я просто не верю, что он способен продуктивно обеспечить рост и сложность приложений.
Данный паттерн был придуман в Google и там же его используют. Он поможет нам достичь следующих целей:
Идея данного подхода очень проста:
Sink<T>
Api для описания асинхронно поступающих в наши компоненты данных
Stream<T>
Api для описания асинхронно возвращаемых нашими компонентами данных
StreamBuilder
для управления потоками данных, без приложения усилий с нашей стороны к подпискам на обновления данных и перерисовку виджетовУ Google есть хорошие примеры использования данного паттерна управления состоянием, потому что он широко используется и очень рекомендуется компанией.
Я и сам очень рекомендую использовать данный подход для управления локальным состоянием, однако он подойдет даже и для управления глобальным состоянием. Однако в последнем случае вы столкнетесь с проблемой — где и как правильно внедрять BLoC, чтобы к нему имели доступ разные компоненты, и тут на сцену выходит Redux.
Одной из целей, которые я описывал в начале статьи был поиск чего-то, широко используемого и предсказуемого и это Redux — паттерн и набор инструментов, которые вместе помогают нам управлять глобальным состоянием. Он имеет три базовых принципа в основе:
Единственный источник истины — все состояние `state
` вашего приложения хранится в древовидном объекте в единственном хранилище `store
`
reducer
`, которая не должна вызывать никаких побочных эффектов `Ссылка на пример кода` [4]
Ссылка на оригинальный пост, откуда взято изображение [5]
Данный подход к управлению состоянием широко принят web-разработчиками, и его появление на мобильных устройствах поможет получить преимущества web и mobile-application разработчикам.
Brian Egan разрабатывает как оригинальный Redux [4], так и flutter_redux [6], а также он сделал потрясающее Todo приложение [7], в котором он применил множество архитектурных паттернов, включая Redux.
Учитывая все качества Redux, я очень сильно советую использовать его для управления глобальным состоянием, но вы должны быть уверены, что не используете его для управления локальным состоянием, если хотите масштабировать свое приложение.
В данной статье нет полностью правильного или неправильного решения. Чтобы определиться с тем, какой подход применять в вашем проекте, вам необходимо определиться с вашими потребностями. Для меня и моих целей комбинация Redux и BLoC позволяет моим проектам быстро и безопасно расти, а также облегчает вход сторонних разработчиков в эти проекты, благодаря доступным и понятным инструментам. Однако не у всех одинаковые потребности и с течением времени можно находить как проблемы `в текущих инструментах`, так и еще лучшие решения. Очень важно всегда оставаться любопытным, учиться и думать, подходит ли вам тот или иной инструмент.
Автор: alphamikle
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/dart/304960
Ссылки в тексте:
[1] Let me help you to understand and choose a state management solution for your app: https://medium.com/flutter-community/let-me-help-you-to-understand-and-choose-a-state-management-solution-for-your-app-9ffeac834ee3
[2] ScopedModel: https://pub.dartlang.org/packages/scoped_model
[3] Brian Egan: https://github.com/brianegan
[4] `Ссылка на пример кода`: https://pub.dartlang.org/packages/redux
[5] Ссылка на оригинальный пост, откуда взято изображение: https://blog.novoda.com/introduction-to-redux-in-flutter/
[6] flutter_redux: https://pub.dartlang.org/packages/flutter_redux
[7] Todo приложение: https://github.com/brianegan/flutter_architecture_samples
[8] Источник: https://habr.com/post/435780/?utm_campaign=435780
Нажмите здесь для печати.