大家好! 在本文中,我想向您展示如何使用Redux创建Flutter应用程序。 如果您不知道
Flutter是什么,那么这是一个用于从Google创建移动应用程序的开源SDK。 它用于开发适用于Android和iOS的应用程序,也是开发适用于Google紫红色的应用程序的唯一方法。
如果您熟悉Flutter,并希望创建一个设计良好,易于测试且行为可预测的应用程序,请继续阅读本文,您将很快发现!
但是在我们开始编写应用程序本身之前。 让我们对理论有所了解,让我们从解释什么是Redux开始。
什么是Redux?
Redux是一种最初为JavaScript语言创建的体系结构,用于使用
反应式框架 (例如React Native或Flutter)创建的应用程序中。 Redux是Facebook创建的Flux架构的简化版本。 本质上,您需要了解三件事:
- 唯一的真理来源-应用程序的整个状态仅存储在一个地方(称为store )。
- 状态为只读/状态为只读-要更改应用程序的状态,您需要发送操作(操作),然后将创建新状态
- 使用纯函数/纯函数进行更改-纯函数(为简单起见,它是没有副作用的函数)采用当前状态的应用程序和操作并返回应用程序的新状态
注意: 函数的副作用是在执行其计算过程中的能力:读取和修改全局变量的值,执行I / O操作,对异常情况作出响应并调用其处理程序。 如果使用一组相同的输入参数值调用具有副作用的函数两次,则可能会返回不同的值作为结果。 这种功能被称为具有副作用的非确定性功能。听起来很酷,但是此解决方案的好处是什么?
- 我们可以控制状态/状态 -这意味着我们确切知道导致状态更改的原因,没有重复的状态,并且我们可以轻松地监视数据流
- Reducer是易于测试的纯函数-我们可以将状态,操作传递给输入并检查结果是否为真
- 该应用程序结构清晰-我们在操作,模型,业务逻辑等方面具有不同的层-因此您确切地知道在哪里添加另一个新功能
- 对于更复杂的应用程序来说,这是一个很好的架构-您不必在整个视图树中从父级到子级传递状态
- 还有一个...
Redux时间旅行
Redux有一个有趣的机会-时空旅行! 使用Redux和相关工具,您可以随时跟踪应用程序的状态,检查实际状态并随时重新创建它。 实际使用此功能:
一个简单示例的Redux小部件
以上所有规则使数据在Redux中单向流动。 但这是什么意思? 实际上,所有这些都是通过使用
action ,
reduces ,
store和
state来完成的 。 让我们想象一个显示计数器的应用程序:
- 您的应用程序在启动时处于某种状态 (点击数为0)
- 基于此状态, 将呈现视图 。
- 如果用户单击按钮,则会发送操作(例如,IncrementCounter)
- 之后,该动作将接收一个知道先前状态的简化器 (计数器0),然后接收动作 (IncrementCounter)并可以返回新状态 (计数器现在为1)。
- 我们的应用程序具有新状态 (计数器为1)
- 基于新状态 ,将重绘视图 ,该视图显示当前状态
因此,如您所见,通常都是关于
state的 。 您拥有整个应用程序的一个状态,
状态为 只读 ,并且要创建新
状态,您需要发送
action 。 提交
动作会启动化简
器 ,它会创建一个新
状态并将其返回给我们。 故事重演。
让我们创建一个小应用程序并了解实际使用的Redux方法的实现,该应用程序将被称为“
购物清单 ”
我们将看到Redux在实践中如何工作。 我们将创建一个简单的ShoppingCart应用。 该应用程序将具有以下功能:
该应用程序将如下所示:
让我们开始编写代码!
先决条件
在本文中,我不会显示为该应用程序创建用户界面。
在继续进行Redux潜水之前,您可以熟悉我为您准备的代码 。 之后,我们将继续编写代码并将
Redux添加到当前应用程序。
注意: 如果您以前从未使用过Flutter,建议您尝试使用Google的Flutter Codelabs 。初步准备
要开始使用
Redux for Flutter,我们需要将依赖项添加到
pubspec.yaml文件中:
flutter_redux: ^0.5.2
您还可以通过转到
flutter_redux页面来检查此依赖项的当前版本。
在撰写本文时,版本为flutter_redux 0.6.0型号
我们的应用程序应该能够控制元素的添加和修改,因此我们将使用一个简单的
CartItem模型来存储一个元素的状态。 我们所有的应用程序状态都只是CartItems的列表。 如您所见,CartItem只是一个对象。
class CartItem { String name; bool checked; CartItem(this.name, this.checked); }
首先,我们需要声明动作。 实际上,操作是可以调用以更改应用程序状态的任何意图。 本质上,将有两个添加和更改元素的操作:
class AddItemAction { final CartItem item; AddItemAction(this.item); } class ToggleItemStateAction { final CartItem item; ToggleItemStateAction(this.item); }
然后,我们需要告诉我们的应用程序如何处理这些
动作 。 这就是为什么使用
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()方法将操作委派给适当的方法。
addItem()和
toggleItemState()方法都返回新列表-这是我们的新状态。 如您所见,您
不应更改当前列表 。 相反,我们每次都创建一个新列表。
商店提供者
现在我们有了
动作和
简化器 ,我们需要提供一个地方来存储
应用程序的
状态 。 在Redux中,它称为
存储 ,是应用程序的唯一事实来源。
void main() { final store = new Store<List<CartItem>>( appReducers, initialState: new List()); runApp(new FlutterReduxApp(store)); }
要创建
商店 ,我们需要传递
reducers方法和初始
状态 。 如果创建了
商店 ,则必须将其传递给
StoreProvider来告诉我们的应用程序,任何想要请求应用程序当前
状态的人都可以使用该
商店 。
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
目前,我们拥有除...实际上添加和更改要购买的物品以外的所有东西。 怎么做? 为了做到这一点,我们需要使用
StoreConnector 。 这是获取
存储并向其发送一些
操作或仅获取当前
状态的一种方法 。
首先,我们要获取当前数据并将其显示为屏幕上的列表:
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])); }, ); } }
上面的代码用
StoreConnector包装
ListView.builder 。
StoreConnector可以接受当前
状态 (项目列表
),借助map函数,我们可以将其转换为任何内容。 但是在我们的例子中,它将是相同的状态 (列表),因为在这里我们需要一个购物清单。
接下来,在builder函数中,我们获得一个列表-基本上是商店中CartItems的列表,我们可以使用它来创建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列表与相同列表匹配,而是将映射 转换为OnItemAddedCallback 。 因此,我们可以将回调函数传递给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按钮时,回调函数都会发送一个动作 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显示OnStateChanged回调函数的列表。 现在,每次标志更改时(在onChanged方法中),回调函数都会触发ToggleItemStateAction事件。
总结
仅此而已! 在本文中,我们创建了一个简单的应用程序,该应用程序显示购物清单,并沉迷于Redux架构。 在我们的应用程序中,我们可以添加元素并更改其状态。 向此应用程序添加新功能就像添加新动作和简化程序一样容易。
在这里,您可以找到此应用程序的源代码,包括“ Time Travel”小部件:
希望您喜欢这篇文章!