flutter redux
嗨,大家好! 在本文中,我想向您展示如何使用Redux创建Flutter应用程序。 如果您不知道Flutter是什么,我鼓励您阅读我的文章Flutter-您可能会喜欢它的5个原因 。 但是,如果您知道Flutter是什么,并且想要创建一个设计良好,易于测试且行为可预测的应用程序,那么请继续阅读!
什么是Redux?
首先,让我们开始解释什么是Redux 。 Redux是一种应用程序架构,最初是为Javascript制作的,现在已在使用React式框架 (例如React Native或Flutter)构建的应用程序中使用。 Redux是Facebook制造的Flux架构的简化版本。 但是Redux到底有什么用呢? 基本上,您需要了解三件事:
- 有一个真实的来源 —您的整个应用程序状态仅保存在一个地方(称为存储)
- 状态为只读 -要更改应用程序状态,您需要分派操作,然后创建新状态
- 使用纯函数进行更改-纯函数(为简化起见,它是没有副作用的函数)采用先前的状态和操作,然后返回新状态
听起来很酷,但是该解决方案的优点是什么?
- 我们可以控制状态 -这意味着我们完全知道导致状态更改的原因,没有重复的状态,我们可以轻松地跟踪数据流
- 纯reducer函数易于测试 -我们可以通过状态,操作和测试结果是否正确
- 应用程序的结构清晰 -我们在操作,模型,业务逻辑等方面具有不同的层次-因此您完全知道在哪里放置另一个新功能
- 对于更复杂的应用程序而言,它是一个很好的体系结构 -您无需将状态从父级到子级传递给整个视图树
- 还有一个…
Redux时间旅行
Redux中可能有一项很酷的功能-🎉 时间旅行 ! 使用Redux和适当的工具,您可以随时跟踪应用程序状态 ,检查实际状态并随时重新创建它 。 实际使用此功能:
Redux Widgets一个简单的例子
以上所有规则使数据在Redux中单向流动。 但是这是什么意思? 实际上,这都是通过动作,reduce,store和states完成的 。 让我们想象一下显示按钮计数器的应用程序:
- 您的应用程序开始时具有某种状态 (点击数,即0)
- 基于该状态视图被呈现。
- 如果用户点击按钮, 则会发送操作 (例如,IncrementCounter)
- 由reducer接收动作, reduce知道先前的状态(计数器0),接收动作(IncrementCounter)并可以返回新状态(计数器1)。
- 您的应用程序具有新状态 (计数器1)
- 基于新状态,再次渲染视图
如您所见,一般来说, 一切都与国家有关 。 您只有一个应用状态 ,该状态对于查看是只读的,要创建新状态,您需要发送操作。 发送动作会触发reducer来创建并发出新的应用程序状态 。 历史在重演。
带Redux的购物清单应用程序示例
让我在更多高级示例中展示Redux如何在实践中工作。 我们将创建一个简单的ShoppingCart应用程序。 在此应用程序中,将具有以下功能:
该应用程序将如下所示:
您可以在GitHub上查看整个应用程序代码:
pszklarska / FlutterShoppingCart
FlutterShoppingCart-使用Redux架构 github.com 的购物应用程序的Flutter示例
让我们从编码开始! 👇
先决条件
在本文中,我不会显示为此应用程序创建UI。 您可以在此处实现Redux之前检查此购物清单应用程序的代码。 从这一点开始,我们将进行编码,并将Redux添加到该应用程序中。
如果您以前从未使用过Flutter,建议您尝试使用Google的Flutter Codelabs 。
建立
要在Flutter上与Redux一起运行,您需要将依赖项添加到pubspec.yaml
文件中:
您可以在flutter_redux软件包页面上查看最新版本。
模型
我们的应用程序需要管理添加和更改项目,因此我们将使用简单的CartItem
模型来存储单个项目状态。 我们整个应用程序的状态将只是CartItems的列表 。 如您所见,CartItem只是一个普通的Dart对象。
class CartItem {
String name;
bool checked;
CartItem (this.name, this.checked);
}
注意:这是此文件的完整源代码。
动作
首先,我们需要声明action 。 动作基本上是可以用来更改应用程序状态的任何意图 。 在我们的应用程序中,我们将执行两个操作,用于添加和更改项目:
class AddItemAction {
final CartItem item;
AddItemAction (this.item);
}
class ToggleItemStateAction {
final CartItem item;
ToggleItemStateAction (this.item);
}
注意:这是此文件的完整源代码
减速器
然后,我们需要告诉我们的应用程序这些动作应该做什么。 这就是为什么reducer适用于它们-它们只获取当前应用程序状态和操作,然后创建并返回新的应用程序状态 。 我们将有两种reducer方法:
List appReducers (List items, dynamic action) {
if (action is AddItemAction) {
return addItem(items, action) ;
} else if (action is ToggleItemStateAction) {
return toggleItemState(items, action) ;
}
return items;
}
List addItem (List items, AddItemAction action) {
return List.from(items)..add(action.item);
}
List toggleItemState (List items, ToggleItemStateAction action) {
return items.map((item) => item.name == action.item.name ?
action.item : item).toList();
}
注意:这是此文件的完整源代码。
方法appReducers()
将操作委派给适当的方法。 方法addItem()
和toggleItemState()
返回新列表-这是我们的新应用程序状态。 如您所见, 您不应该修改当前列表 。 取而代之的是,我们每次都创建新列表。
商店提供者
现在,当我们有动作和简化器时,我们需要提供存储应用程序状态的地方。 它在Redux中称为store ,它是我们应用程序的唯一事实来源 。
void main() {
final store = new Store>(
appReducers,
initialState: new List());
runApp(new FlutterReduxApp(store));
}
注意:这是此文件的完整源代码。
要创建商店,我们需要传递reducers方法和初始应用程序状态 。 如果创建了商店,则必须将其传递给StoreProvider,以告知我们的应用程序任何想要请求应用程序状态的人都可以使用它:
class FlutterReduxApp extends StatelessWidget {
final Store> store;
FlutterReduxApp(this.store);
@override
Widget build(BuildContext context) {
return new StoreProvider>(
store: store,
child: new ShoppingCartApp(),
);
}
}
注意:这是此文件的完整源代码。
在上面的示例中, ShoppingCartApp()
是主应用程序小部件。
StoreConnector
目前,我们拥有除……实际添加和更改项之外的所有内容。 怎么做? 为了使其成为可能,我们需要使用StoreConnector 。 这是一种获取商店并对其执行某些操作或读取其状态的方法 。
首先,我们想读取当前数据并将其显示在列表中 :
class ShoppingList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new StoreConnector, List>
(
converter: (store) => store.state,
builder: (context, list) {
return new ListView.builder(
itemCount: list.length,
itemBuilder: (context, position) =>
new ShoppingListItem(list[position]));
},
);
}
}
注意:这是此文件的完整源代码。
上面的代码用StoreConnector
包装默认的ListView.builder
。 StoreConnector可以采用当前应用程序状态(即List
),并将其与converter
功能映射到任何内容。 对于这种情况,它将是相同的状态( List
),因为我们需要这里的整个列表。
接下来,在builder
的功能,我们得到list
-这基本上是从CartItems名单store
,我们可以用它构建的ListView。
好的,很酷-我们在这里读取数据。 现在如何设置一些数据?
为此,我们还将使用StoreConnector ,但方式略有不同。
class AddItemDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new StoreConnector, 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列表映射到同一列表中,而是将其映射到OnItemAddedCallback
。 这样,我们可以将回调传递给AddItemDialogWidget
,并在用户添加一些新项时调用它:
class AddItemDialogWidgetState extends State {
String itemName;
final OnItemAddedCallback callback;
AddItemDialogWidgetState(this.callback);
@override
Widget build(BuildContext context) {
return new AlertDialog(
...
actions: [
...
new FlatButton(
child: const Text('ADD'),
onPressed: () {
...
callback(itemName);
})
],
);
}
}
注意:这是此文件的完整源代码。
现在,每当用户按下“ ADD”按钮时,回调将调度AddItemAction()
事件。
现在,我们可以为切换项目状态做非常相似的事情:
class ShoppingListItem extends StatelessWidget {
final CartItem item;
ShoppingListItem(this.item);
@override
Widget build(BuildContext context) {
return new StoreConnector, 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));
}),
);
});
}
}
typedef OnStateChanged = Function(CartItem item);
注意:这是此文件的完整源代码。
与前面的示例一样,我们使用StoreConnector将List
映射到OnStateChanged
回调中。 现在,每次更改复选框(在onChanged
方法中)时,回调都会触发ToggleItemStateAction
事件。
摘要
就这样! 在本文中,我们使用Redux架构创建了一个简单的Shopping List应用程序。 在我们的应用程序中,我们可以添加一些项目并更改其状态。 向此应用程序添加新功能就像添加新动作和简化程序一样简单。
在这里,您可以检查此应用程序的完整源代码,包括Time Travel小部件:
pszklarska / FlutterShoppingCart
FlutterShoppingCart-使用Redux架构 github.com 的购物应用程序的Flutter示例
希望您喜欢这篇文章,并继续关注! 🙌
翻译自: https://hackernoon.com/flutter-redux-how-to-make-shopping-list-app-1cd315e79b65
flutter redux