(译)Flutter+Redux- 如何做一个购物清单应用

greenmeercat 发布于10月前
0 条问题

原文链接

嗨,大家好!在本文中,我想向你展示如何使用Redux创建Flutter应用程序。如果你不知道Flutter是什么,我推荐你阅读我的文章 Flutter - 你可能喜欢它的5个理由 。但是,如果你知道Flutter是什么,并且你想要创建一个设计良好,易于测试且具有非常可预测的行为的应用程序 - 那么请继续看下去!

什么是Redux?

首先,让我们首先解释一下Redux是什么。Redux是一种应用程序架构,最初是为JavaScript构建的,现在用于使用反应式框架构建的应用程序(例如React Native或Flutter)。Redux是由Facebook制作的Flux架构的简化版本。但Redux有什么用呢?基本上,你需要知道三件事:

action

听起来很酷,但该解决方案的优点是什么?

  • 我们控制了状态 - 这意味着我们确切地知道导致状态变化的原因,我们没有重复状态,我们可以轻松地跟踪数据流
  • 纯reducer方法很容易对其进行测试 -我们可以传入状态和 action ,然后看结果是否正确
  • 应用程序结构清晰 - 我们为 action , model ,业务逻辑等提供了不同的层 - 因此你要加新功能的时候,你会知道要放在哪里。
  • 对于更复杂的应用程序来说,这是一个很棒的架构 - 您不需要将整个视图树中的状态从父级传递到子级
  • 还有一个……

Redux时间旅行

Redux中有一个很酷的功能 - :tada:时间旅行!使用Redux和适当的工具,您可以随时跟踪应用程序状态,检查实际状态并随时重新创建。看看这个功能到底是什么样的:

(译)Flutter+Redux- 如何做一个购物清单应用

Redux相关Widget在一个简单例子上的应用

所有上述规则使数据在Redux是单向流动的。但是这是什么意思?事实上,这都是由多个 action ,多个 reducer , store 以及多个 state 完成的。让我们想象显示按钮计数器的应用程序:

(译)Flutter+Redux- 如何做一个购物清单应用

action
reducer

正如你能看到的,通常都是与状态相关的。你有单个应用程序状态,状态对于视图是只读的,并且要创建新的状态的时候你需要发送一个 action 。发送 action 会触发 reducer 创建并发送一个新的应用状态。循环往复。

(译)Flutter+Redux- 如何做一个购物清单应用 Redux数据流

使用Redux的购物清单应用示例

让我展示一下Redux在更为复杂例子中的实践。我们将创建一个简单的购物车应用程序。在这个应用程序中,将具有以下功能:

  • 添加条目
  • 将条目标记为已选中
  • 就那么多

该应用程序的样子是这样的:

(译)Flutter+Redux- 如何做一个购物清单应用 你可以在Github上看到 该程序的完整代码

让我们从编码开始吧! :point_down:

先决条件

在本文中,我不会展示应用程序UI创建的部分。您可以查看这个 实现Redux之前的Shopping List应用程序的代码 。我们将从这个基础上,把Redux添加到此应用程序中。

如果你之前从未使用过Flutter,我建议您尝试使用Google推出的 Flutter Codelabs

设置

要在Flutter上使用Redux运行,您需要向pubspec.yaml文件添加依赖项:

flutter_redux: ^0.5.2

您可以在 flutter_redux 包页面上查看最新版本。

模型(Model)

我们的应用程序需要管理添加和修改条目,因此我们将使用简单的CartItem模型来存储单个条目的状态。我们的整个应用程序状态将只是多个CartItem组成的列表。如你所见,CartItem只是一个普通的Dart对象

class CartItem {
  String name;
  bool checked;

  CartItem(this.name, this.checked);
}

注意:这里是这个文件的完整源代码

Actions

首先,我们需要声明 action 。 Action基本上是可以被调用以更改应用程序状态的任何意图。在我们的应用程序中,我们将有两个 action ,用于添加和修改条目:

class AddItemAction {
  final CartItem item;

  AddItemAction(this.item);
}

class ToggleItemStateAction {
  final CartItem item;

  ToggleItemStateAction(this.item);
}

注意:这里是这个文件的完整源代码

Reducers

然后,我们需要告诉我们的应用程序应该如何处理这些操作。这就是Reducer的用途 - 它们只是采用当前的 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()方法都返回新列表-这个列表就是我们的新应用程序状态。如你所见,您不应修改当前列表,而是每次都创建新的列表。

StoreProvider

现在,当我们有了 action 和 reducer 时,我们需要提供存储应用程序状态的位置。它在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()是主要的应用程序widget。

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 可以获取当前应用程序状态(List )并将其与转换器方法映射到任何内容。出于这种情况的目的,它将是相同的状态(List ),因为我们需要这里的整个列表。

接下来,在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 ,就像前面的例子一样,但这一次,我们将把它映射到OnItemAddedCallback,而不是将CartItems列表映射到同一个列表中。这样我们就可以将回调传递给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));
            }),
      );
    });
  }
}

typedef OnStateChanged = Function(CartItem item);

注意: 这里是这个文件的完整源代码

与前面的示例一样,我们使用 StoreConnector 将List 映射到OnStateChanged回调。现在每次更改复选框(在onChanged方法中),回调都会触发ToggleItemStateAction事件。

摘要

以上就是全部内容!在本文中,我们使用Redux架构创建了一个简单的购物清单应用程序。在我们的应用程序中,我们可以添加一些条目并更改其状态。向此应用程序添加新功能就像添加新action和reducer一样简单。

在这里 ,您可以查看此应用程序的完整源代码,包括时间旅行widge:

希望你喜欢这篇文章并敬请期待更多! :raised_hands:

查看原文: (译)Flutter+Redux- 如何做一个购物清单应用

  • organictiger
  • goldenrabbit
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。