与Redux一起在Flutter上编写应用程序

图片


大家好! 在本文中,我想向您展示如何使用Redux创建Flutter应用程序。 如果您不知道Flutter是什么,那么这是一个用于从Google创建移动应用程序的开源SDK。 它用于开发适用于Android和iOS的应用程序,也是开发适用于Google紫红色的应用程序的唯一方法。

如果您熟悉Flutter,并希望创建一个设计良好,易于测试且行为可预测的应用程序,请继续阅读本文,您将很快发现!

但是在我们开始编写应用程序本身之前。 让我们对理论有所了解,让我们从解释什么是Redux开始。

什么是Redux?


Redux是一种最初为JavaScript语言创建的体系结构,用于使用反应式框架 (例如React Native或Flutter)创建的应用程序中。 Redux是Facebook创建的Flux架构的简化版本。 本质上,您需要了解三件事:

  1. 唯一的真理来源-应用程序的整个状态仅存储在一个地方(称为store )。
  2. 状态为只读/状态为只读-要更改应用程序的状态,您需要发送操作(操作),然后将创建新状态
  3. 使用纯函数/纯函数进行更改-纯函数(为简单起见,它是没有副作用的函数)采用当前状态的应用程序和操作并返回应用程序的新状态

注意: 函数的副作用是在执行其计算过程中的能力:读取和修改全局变量的值,执行I / O操作,对异常情况作出响应并调用其处理程序。 如果使用一组相同的输入参数值调用具有副作用的函数两次,则可能会返回不同的值作为结果。 这种功能被称为具有副作用的非确定性功能。

听起来很酷,但是此解决方案的好处是什么?

  • 我们可以控制状态/状态 -这意味着我们确切知道导致状态更改的原因,没有重复的状态,并且我们可以轻松地监视数据流
  • Reducer是易于测试的纯函数-我们可以将状态,操作传递给输入并检查结果是否为真
  • 该应用程序结构清晰-我们在操作,模型,业务逻辑等方面具有不同的层-因此您确切地知道在哪里添加另一个新功能
  • 对于更复杂的应用程序来说,这是一个很好的架构-您不必在整个视图树中从父级到子级传递状态
  • 还有一个...

Redux时间旅行


Redux有一个有趣的机会-时空旅行! 使用Redux和相关工具,您可以随时跟踪应用程序的状态,检查实际状态并随时重新创建它。 实际使用此功能:

图片

一个简单示例的Redux小部件


以上所有规则使数据在Redux中单向流动。 但这是什么意思? 实际上,所有这些都是通过使用actionreducesstorestate来完成的 。 让我们想象一个显示计数器的应用程序:

图片

  1. 您的应用程序在启动时处于某种状态 (点击数为0)
  2. 基于此状态, 呈现视图
  3. 如果用户单击按钮,则会发送操作(例如,IncrementCounter)
  4. 之后,该动作将接收一个知道先前状态简化器 (计数器0),然后接收动作 (IncrementCounter)并可以返回新状态 (计数器现在为1)。
  5. 我们的应用程序具有新状态 (计数器为1)
  6. 基于新状态 ,将重绘视图 ,该视图显示当前状态

因此,如您所见,通常都是关于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.builderStoreConnector可以接受当前状态 (项目列表),借助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”小部件:

希望您喜欢这篇文章!

Source: https://habr.com/ru/post/zh-CN481624/


All Articles