从Flutter到Flight 4:功能和重构

你们当中有多少人精通或至少熟悉函数式编程? 举手,不要害羞。 太好了,所有人现在都可以离开,因为在这篇文章中我可能会说些愚蠢的话。

我不会“获取”函数式编程。 我尝试过Clojure和R,并发现它们通常难以理解。 这并不是说它们是不好的语言(嗯……也许是R),只是我个人无法有效地使用它们。 但是我认为这可能是外来语法混淆了基本概念的情况。 许多聪明的人使用函数式编程产生了巨大的效果,而我一直ing不安地感到自己缺少一些深刻的东西,或者至少对于我的工具箱来说这是一个漂亮的工具。

最近,我碰到了这篇文章,有点“哈哈”的时刻。 启示是函数返回函数的想法,除了有点元之外,我真的从未想过要这样做。 本文的其余部分开始引起我的注意,但是我认为,如果至少可以实现返回函数的函数,那么我将逐步提高自己的能力。 因此,我开始在Checklist应用中寻找机会进行此操作,并立即找到了一个机会。

清单应用程序的顶级对象是一本包含许多清单的书。 每个清单指向其后的清单,以便可以按顺序正确完成。 清单编辑器页面需要一种选择下一个清单的方法。 我在Flutter中使用showDialog进行了此操作,该chooseList嵌入了一个称为chooseList的函数中。 该函数返回一个Selection对象,该对象只是所选列表的包装器。 如果返回值为null,则该用户被取消,并且我不进行任何更新。 否则,我将使用Selection对象中的值更新清单的指针。 我这样做是因为提供空指针是有效的操作,但是我需要区分空指针(具有null ChecklistSelection对象)和取消指针(null Selection )。 一些代码可以帮助您可视化:

理想情况下,我希望对话框的顶部具有选项卡控件,以允许我在常规列表和紧急列表之间切换。 在过去,我会复制buildBody代码并将该功能拼凑在一起,对复制不满意,但不确定如何做不同的事情。 今天不行! 今天,我学到了一个新的技巧。 我可以构建一个函数来返回一个函数! 我不必复制和粘贴! 下面的代码是我的处理方式。 我花了大约5分钟的时间凝视着屏幕然后走向“呵呵?” 在我的大脑进行必要的体操训练以开始输入之前。

现在,我已经完成了这一工作,我发现这种模式正在变得越来越容易。 我不再需要呆呆地盯着屏幕看10分钟,然后才知道要输入什么。 进步,我猜呢?

同时,我毫不畏惧地投入了函数式编程学前班,还为我的应用构建了各种编辑器页面。 在创建使用大量相同代码的约4–5页之后,我终于想到,我可能应该将该代码重构为它自己的抽象类,可以在每个具体页面上进行扩展。 我给人的印象是,大多数开发人员在编写第一个副本时都会立即重构。 可悲的是,这种意识在我心中还不强。

我所有的编辑器页面都需要执行几个常见操作:

  • 将书籍文件从磁盘加载到内存中。
  • 将主题更改为红色和绿色(总有一天我会详细讨论这个主题以及为什么我不确定自己做对了吗)。
  • 进行更改后,将书保留在磁盘上。
  • 当用户更改列表中项目的顺序时,几个页面需要能够处理onMove回调。

当我在抽象类中实现它们时,让我们一一介绍。 让我们从类声明和文件加载过程开始:

这里有一些,所以让我们分解一下。 EditorPage只是一个StatefulWidget ,它带有表示导航路径的字符串参数。 很简单。

EditorPageStateState ,是实际执行所有工作的位置。 它是使用isLoading标志和BookIo类的实例创建的。 BookIo是我创建的用于处理向磁盘读写书籍对象的类。

initEditorState()用于在EditorPageState的具体实现的initState()期间initState() 。 目的是解析导航路径并保存对已解析Book的引用。 它接受一个回调,该回调允许具体实现对其他已解析的对象执行其所需的任何操作。 通过initState()可以轻松使用它是同步的,但是ParsePath.parse()是异步的,因为它是从磁盘读取的。 因此,需要.then()调用。

buildPage()用于在具体实现的build()方法期间调用,并提供页面内容的“框架”。 最初,它仅构建CupertinoActivityIndicator 。 旨在提供页面正在加载Book文件的视觉反馈。 加载文件后,页面将重建并显示在bodyBuilder参数中传递的内容。

将书文件加载到内存中的第一个常见操作就是这样。 这个过程之所以使我开始进行重构,是因为在每个页面上重复该过程变得非常烦人。

第二个操作是更改主题,不需要太多其他代码。 首先,我调整了EditorPage类,以接受用于主题更改过程的特殊回调。

然后,我创建了一个小的方法来在主题更改时调用setState并将其传递给themeAppBar 。 当按下应用程序栏中的相应按钮时, themeAppBar调用此方法。

下一个常见的操作是将书保留到磁盘上,但有一个警告。 如果持久性以某种方式失败,则需要撤消该操作。 我在所有业务逻辑对象中都使用了经过修改的Command模式,因此在这里实现它很简单。 我需要的是一个独立函数,每个具体实现都可以在要保存Book时调用该函数。

最后但并非最不重要的一点是能够处理列表中的项目重新排序。 为此,我的应用程序当前使用我创建的拖放式ListView 。 移动项目时,它将调用onMove回调。 如果您想检查一下,请看这里。 有空的时候我会继续努力,但我暗地里希望Flutter团队能提出自己更强大(正确的?)的解决方案,以便在发布前用我的解决方案代替。

因为我的DraggableListView为我处理了移动,所以我只需要一个小方法就可以在源列表中进行实际更改并将Book持久保存到磁盘:

将它们放在一起看起来像这样:

创建具体的实现时,只需扩展这些类并在initStatebuild方法中添加必要的钩子。 然后,我可以集中精力构建特定页面的详细信息。

展望未来,我将继续在编辑器页面上工作。 有很多元素可以编辑,所以我要创建很多页面。 我还将寻找其他机会来重构我的工作并使用返回函数的函数。 我也很高兴尝试新的Dart 2预览版; Dart 2带来了一些非常酷的变化。