- PVSM.RU - https://www.pvsm.ru -
Привет всем! В этой статье я хотел бы показать вам, как создать Flutter приложение, используя Redux. Если вы не знаете, что такое Flutter [1], то это — SDK с открытым исходным кодом для создания мобильных приложений от компании Google. Он используется для разработки приложений под Android и iOS, а также это пока единственный способ разработки приложений под Google Fuchsia.
Если вы знакомы с Flutter и хотите создать приложение, которое хорошо спроектировано, легко тестируется и имеет очень предсказуемое поведение, — продолжайте читать данную статью и вы скоро это узнаете!
Но перед тем как мы приступим к написанию самого приложения. Давайте немного познакомимся с теорией, давайте начнем с объяснения, что такое Redux.
Redux [2] — это архитектура, изначально созданная для языка JavaScript и используемая в приложениях, которые созданы с использованием reactive frameworks (таких как React Native или Flutter). Redux — это упрощенная версия архитектуры Flux, созданная Facebook. По сути, вам нужно знать три вещи:
Примечание: Побочный эффект функции — возможность в процессе выполнения своих вычислений: читать и модифицировать значения глобальных переменных, осуществлять операции ввода-вывода, реагировать на исключительные ситуации, вызывать их обработчики. Если вызвать функцию с побочным эффектом дважды с одним и тем же набором значений входных аргументов, может случиться так, что в качестве результата будут возвращены разные значения. Такие функции называются недетерминированными функциями с побочными эффектами.
Звучит круто, но каковы преимущества данного решения?
В Redux возможна одна интересная возможность — Путешествие во времени! С Redux и соответствующими инструментами вы можете отслеживать state вашего приложения с течением времени, проверять фактический state и воссоздавать его в любое время. Смотрите эту возможность в действии:
Все вышеперечисленные правила делают поток данных в Redux однонаправленным. Но что это значит? На практике все это делается с помощью actions, reducers, store и states. Давайте представим приложение, которое отображает счетчик:
Итак, как вы можете видеть, как правило, это все о state. У вас есть один state всего приложения, state только read-only, и для создания нового state вам нужно отправить action. Отправка actions запускает reducer, который создает и вернет нам новый state. И история повторяется.
Давайте все же создадим небольшое приложение и более ближе познакомимся с реализацией подхода Redux в действий, приложение будет называться “Список покупок/Shopping List”
Мы посмотрим, как Redux работает на практике. Мы создадим простое приложение ShoppingCart. В приложении будут функциональные возможности, такие как:
Приложение будет выглядеть так:
Давайте начнем написания кода!
В этой статье я не буду показывать создание пользовательского интерфейса для данного приложения. Вы можете ознакомится с кодом который я подготовил для Вас, прежде чем продолжить погружение в Redux [3]. После чего мы продолжим написание кода и добавление Redux в текущее приложение.
Примечание: Если вы никогда раньше не использовали Flutter, я советую вам попробовать Flutter Codelabs от Google [4].
Чтобы начать использовать Redux для Flutter, нам необходимо добавить зависимости в файл pubspec.yaml:
flutter_redux: ^0.5.2
Вы также можете проверить текущую версию данной зависимости, перейдя на страничку flutter_redux [5].
На момент написания статьи версия была, flutter_redux 0.6.0
Наше приложение должно уметь управлять добавлением и изменением элементов, поэтому мы будем использовать простую модель CartItem для хранения состояния одного элемента. Все наше состояние приложения будет просто списком CartItems. Как видите, CartItem — это просто объект.
class CartItem {
String name;
bool checked;
CartItem(this.name, this.checked);
}
Во-первых, нам нужно объявить actions. Action — это, по сути, любое намерение, которое может быть вызвано для изменения состояния приложения. По сути нас будет два actions для добавления и изменения элемента:
class AddItemAction {
final CartItem item;
AddItemAction(this.item);
}
class ToggleItemStateAction {
final CartItem item;
ToggleItemStateAction(this.item);
}
Затем нам нужно сообщить нашему приложению, что делать с этими actions. Вот почему используются reducers — они просто принимают текущее состояние приложения и действие (application state и action), затем создают и возвращают новый state. У нас будет два reducer метода:
List<CartItem> appReducers(List<CartItem> items, dynamic action) {
if (action is AddItemAction) {
return addItem(items, action);
} else if (action is ToggleItemStateAction) {
return toggleItemState(items, action);
}
return items;
}
List<CartItem> addItem(List<CartItem> items, AddItemAction action) {
return List.from(items)..add(action.item);
}
List<CartItem> toggleItemState(List<CartItem> items, ToggleItemStateAction action) {
return items.map((item) => item.name == action.item.name ?
action.item : item).toList();
}
Метод appReducers() делегирует action соответствующим методам. Оба метода addItem() и toggleItemState() возвращают новые списки — это наш новый state/ состояние. Как видите, вы не должны изменять текущий список. Вместо этого мы каждый раз создаем новый список.
Теперь, когда у нас есть actions и reducers, нам нужно предоставить место для хранения состояния приложения. В Redux он называется store и является единственным источником правды для приложения.
void main() {
final store = new Store<List<CartItem>>(
appReducers,
initialState: new List());
runApp(new FlutterReduxApp(store));
}
Чтобы создать store, нам нужно передать метод reducers и начальный state. Если мы создали store, мы должны передать его в StoreProvider, чтобы сообщить нашему приложению, что store может быть использован любым, кто захочет запросить текущий state приложения.
class FlutterReduxApp extends StatelessWidget {
final Store<List<CartItem>> store;
FlutterReduxApp(this.store);
@override
Widget build(BuildContext context) {
return new StoreProvider<List<CartItem>>(
store: store,
child: new ShoppingCartApp(),
);
}
}
В приведенном выше примере, ShoppingCartApp() является основным виджетом нашего приложения.
В настоящее время у нас есть все, кроме… фактического добавления и изменения элементов для покупки. Как это сделать? Чтобы сделать это возможным, нам нужно использовать StoreConnector. Это способ получить store и отправить ему какие-то action или просто получить текущий state.
Во-первых, мы хотим получить текущие данные и отобразить их в виде списка на экране:
class ShoppingList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new StoreConnector<List<CartItem>, List<CartItem>>(
converter: (store) => store.state,
builder: (context, list) {
return new ListView.builder(
itemCount: list.length,
itemBuilder: (context, position) =>
new ShoppingListItem(list[position]));
},
);
}
}
Код выше оборачивает ListView.builder с StoreConnector. StoreConnector может принять текущий state (которое является списком элементов ) и с помощью функций map мы можем конвертировать его в что угодно. Но в нашем случае это будет один и тоже state (List ), потому что здесь нам нужен список покупок.
Далее, в функции builder мы получаем список — который в основном представляет собой список CartItems из store, который мы можем использовать для создания ListView.
Хорошо, круто — у нас есть данные. Теперь, как установить некоторые данные?
Для этого мы также будем использовать StoreConnector, но немного по-другому.
class AddItemDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new StoreConnector<List<CartItem>, OnItemAddedCallback>(
converter: (store) {
return (itemName) =>
store.dispatch(AddItemAction(CartItem(itemName, false)));
}, builder: (context, callback) {
return new AddItemDialogWidget(callback);
});
}
}typedef OnItemAddedCallback = Function(String itemName);
Давайте посмотрим на код. Мы использовали в StoreConnector, как и в предыдущем примере, но на этот раз вместо сопоставления списка CartItems с тем же списком, мы сделаем преобразование с помощью map в OnItemAddedCallback. Таким образом, мы можем передать функцию обратного вызова [6] в AddItemDialogWidget и вызвать ее, когда пользователь будет добавлять новый элемент:
class AddItemDialogWidgetState extends State<AddItemDialogWidget> {
String itemName;
final OnItemAddedCallback callback;
AddItemDialogWidgetState(this.callback);
@override
Widget build(BuildContext context) {
return new AlertDialog(
...
actions: <Widget>[
...
new FlatButton(
child: const Text('ADD'),
onPressed: () {
...
callback(itemName);
})
],
);
}
}
Теперь каждый раз, когда пользователь нажимает кнопку «ADD», функция обратного вызова отправляет action AddItemAction().
Теперь мы можем сделать очень похожую реализацию для изменения состояния элемента.
class ShoppingListItem extends StatelessWidget {
final CartItem item;
ShoppingListItem(this.item);
@override
Widget build(BuildContext context) {
return new StoreConnector<List<CartItem>, OnStateChanged>(
converter: (store) {
return (item) => store.dispatch(ToggleItemStateAction(item));
}, builder: (context, callback) {
return new ListTile(
title: new Text(item.name),
leading: new Checkbox(
value: item.checked,
onChanged: (bool newValue) {
callback(CartItem(item.name, newValue));
}),
);
});
}
}
Как и в предыдущем примере, мы используем StoreConnector для отображения List для функции обратного вызова OnStateChanged. Теперь каждый раз, когда флажок изменяется (в методе onChanged), функция обратного вызова запускает событие ToggleItemStateAction.
Это все! В этой статье мы создали простое приложение которое отображает список покупок и немного погрузились в использование архитектуры Redux. В нашем приложении мы можем добавить элементы и изменить их состояние. Добавление новых функций в это приложение так же просто, как добавление новых actions и reducers.
Здесь [7] вы можете ознакомится с исходный кодом этого приложения, включая виджет Time Travel:
Надеюсь, вам понравился этот пост!
Автор: yarmolchuk
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/341002
Ссылки в тексте:
[1] Flutter: https://flutter.dev
[2] Redux: https://redux.js.org
[3] Вы можете ознакомится с кодом который я подготовил для Вас, прежде чем продолжить погружение в Redux: https://github.com/pszklarska/flutter_shopping_cart
[4] Flutter Codelabs от Google: https://codelabs.developers.google.com/codelabs/flutter/
[5] flutter_redux: https://pub.dartlang.org/packages/flutter_redux
[6] функцию обратного вызова: https://developer.mozilla.org/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F_%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2%D0%B0
[7] Здесь: https://github.com/pszklarska/FlutterShoppingCart
[8] Источник: https://habr.com/ru/post/481624/?utm_campaign=481624&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.