你好!今天,我想向您介绍我几个月来在晚上和周末工作的成果,旨在改善内容管理体验并为 Flutter 应用程序开发世界带来额外的功能——一种新型的 CMS。
我们谈论的是 Nanc - N ot A N ormal C MS。为什么它“不正常”以及您可以用它做什么,您会在阅读本文后找到答案。
这篇文章不仅包含理论,还包含“实践”——您将能够在 Nanc 沙盒中进行游戏。为了向公众展示 Nanc 的功能,制作了两个演示应用程序:一个模仿任何 Flutter 应用程序的客户端应用程序,另一个让您了解基于 Flutter 的 Nanc 应用程序可以做什么——预构建的 Nanc CMS。以及 Nanc CMS 应用程序,它是一个预配置的 CMS,具有附加的逻辑层,用于将客户端应用程序与 CMS 同步。
在文本中大部分逻辑块的末尾,您会找到一个 youtube 视频,其中包含一个示例,说明如何使用/演示 Nanc 的某些方面。
介绍
关于内容管理系统
模型类型
编辑
领域
动态 Flutter 应用程序
Nanc 演示应用程序
接下来是什么/后记
所以。 Nanc 是一个与后端无关的 CMS,它不会拉出自己的后端。它是如何工作的? Nanc 提供实现网络服务接口,目前有 6 个方法,但到发布时将有大约 10 个。这是很多还是很少?例如,为演示应用程序实现 API 需要 170 行代码。这些方法负责 Nanc 与您现有后端的所有工作。实现必须用 Dart(也是 Flutter 的开发语言)编写。未来,Nanc 将为某些后端选项提供这些接口的现成实现——Firebase、Supabase、本地/网络 SQLite 以及 REST 和 GraphQL 的通用实现(也许是其他东西?)全部或将不得不,但只是一点点。
Nanc 中的模型是对要使用 Nanc 控制的实体的结构描述。模型包含实体名称、各种视觉参数和字段描述。
集合是一个可以有很多实例的实体。用户、书籍和评论列表是 Nanc 中集合类型模型的很好示例。
如果您熟悉关系数据库,Nanc 中的集合示例几乎可以是数据库中的任何表。
solo(模型)是存在于单个实例中的实体。例如 - 功能切换或某物的配置,或......“移动应用程序的主屏幕”。一般来说,Solo 模型旨在提高可用性,只是数据库的投影。并且 Solo-model 可以很容易地成为数据库中只有一条记录的任何表。但目前,此类模型的实现要求该模型的记录(数据库中的行)的id
等于模型本身的id
。
final Model landingPage = Model( name: 'Landing page', /// ? id in format [toSnakeCase(name)] will be set automatically, if omitted // id: 'landing_page', isCollection: false, icon: IconPackNames.flu_document_page_break_regular, fields: [ ...
可以通过多种方式配置 Nanc:通过代码、通过 Nanc 界面本身以及这些选项的组合。
当我说“配置”时,我的意思是,首先,描述模型的结构。我们举一个简单的例子,Feature模型,它是一个描述产品特性的实体。该实体包含以下字段:
作为代码优先配置的实现如下:
import 'package:fields/fields.dart'; import 'package:icons/icons.dart'; import 'package:model/model.dart'; final Model feature = Model( name: 'Feature', icon: IconPackNames.flu_ribbon_star_filled, fields: [ [ IdField(width: 200), StringField(name: 'Title', maxLines: 1, isRequired: true, width: 400), ], [ IconField(name: 'Image', isRequired: true), ], [ StringField(name: 'Description', isRequired: true, showInList: true), ], ], );
通过描述这样一个模型并在 Nanc CMS 中使用它,您可以使用该模型的所有 CRUD 操作。
我们可以通过 Nanc 接口创建完全相同的特征模型(我们称之为特征变体)。并且(考虑到使用 Nanc 的所有准备工作都已完成)——当您在 Nanc 中创建模型时,您将立即在数据库中创建一个表,并且整个 CRUD 也将立即可供您使用。
此外,当您通过 Nanc 界面创建模型时,您可以采取不在数据库中创建任何内容的更安全的方式。并在你的数据库中独立创建一个表,然后在其下,在 Nanc 中创建一个模型。顺便说一句,如果您在代码中描述配置,这就是您必须做的——新表不会出现在您的数据库中。
当您拥有代码优先配置并决定通过 Nanc 界面更改它时,您可以使用此选项。在这种情况下,对该模型的所有进一步更改将只能通过接口进行,而对原始代码模型所做的更改将被忽略。返回代码优先的唯一方法是“重置”模型,在这种情况下,通过界面对模型结构所做的所有更改都将被清除,代码优先配置将再次使用。此重置不会影响任何数据。它只影响模型结构。
下面我们看看Nanc目前支持哪些类型的字段。
BoolField 允许您控制bool
数据类型并自定义默认值。
ColorField 为您提供了一个方便的颜色选择器,让您可以立即在 Nanc 中选择一种颜色。它还使您能够通过编辑 AHEX 代码手动进行更改。 AHEX 是一种经典的 HEX-Color(例如, #10A0CF
),但首先指定了一个额外的透明度值。在这种情况下,这种颜色类似于颜色#FF10A0CF
( FF
是 100% 不透明 - 颜色是完全不透明的)。这是相同颜色在 50% 不透明度下的样子: #7F10A0CF
。
DateField 负责控制日期和时间(同时控制两个值,日期和时间的单独值将在以后实现)。 DateField 包含两个布尔参数,允许您通过使其成为实体创建时间时间戳和更改时间时间戳来修改此字段的行为。
一方面,DynamicField 是一个非常简单的字段,但另一方面,它包含了其他字段的全部复杂性。最初,您只能配置此字段的外观(它在 Nanc 界面中的外观 - 颜色和随附的图标)。之后,该字段可以包含 Nanc 中可用的任何值,包括它本身。这是什么意思?本质上,DynamicField 是一种类型化的 JSON,能够在其内部按顺序定位字段。
EnumField 是用于从多个值中选择一个值的字段。选择 EnumField 时要遵循的规则是,如果要选择的最终值列表未存储在单独的数据库表中,请选择 Enum。否则,请选择 SelectorField,这将在下面进行更详细的讨论。 TODO:目前这个字段只能通过CodeConfig配置,通过接口配置还没有实现。
HeaderField 并不是真正的字段,而是 Nanc 中模型的视觉增强器。您可以使用此非字段为一组相关字段设置一个公共标题,或者通过使用 HeaderField 作为分隔符来区分模型字段。
IconField 使您能够从一组预定义的图标中选择一个图标( IconData
类)。目前大约有 25,000 个,这个集合包括以下图标:
如有必要,可以将其他图标添加到基本交付集中,并且在不久的将来也可以选择使用您自己的图标。
有人可能会想,“图标在那里,颜色在那里,但是字体呢?”如果这样做,您可以在文本小部件的文档中找到答案。简短的回答是您可以使用Google Fonts中的所有字体。
IdField 是一个如此简单但又如此重要的字段。 Nanc 管理的每个模型都必须至少有一个 Id 类型的字段。目前仅支持字符串 ID 类型(它可以是一个实体中的任何唯一字符串)。还计划添加对数字类型的支持,但是,即使现在也可以通过在 API 实现中将字段数据转换为数字类型来实现。
MultiSelectorField 是一个相当复杂的字段,负责实现关系型多对多或多对一关系。有三种使用该字段的模式。让我们更详细地了解它们中的每一个。 TODO:目前这个字段只能通过CodeConfig配置,通过接口配置还没有实现。
此模式使您能够直接将相关实体的id
存储在父实体中。例如 - 我们有两个模型 - Reader 和 Book。在这种模式下,读者所拿的书将被记为存储在 Reader 模型字段中的 id。例如像这样:
/// user table { id: 'UUID', name: 'String', books: [ 'UUID', 'UUID' // ... ] }
以上是使用 JSON5 语法表示的示例表结构。
这种模式的缺点是数据完整性有限。如果您不实现从books
读者字段中自动删除过时(已删除)图书 ID,您将收到错误。
SQL 世界中提供关系的经典模式。使用此模式时,您将实体关系存储在单独的表中并确保 100% 的数据完整性。下面的结构是这种模式的一个例子:
[ /// user table { id: 'UUID', name: 'String' }, /// book table { id: 'UUID', title: 'String' }, /// user_books_relations table { user_id: 'UUID', book_id: 'UUID' } ]
在第 7 秒,您可以看到轻微的抽动,如果您仔细观察,您会注意到页面 url 已更改 - 这就是我试图隐藏错误的方式:在第三表模式下,数据仅在父页面中保存已经保存了🤷🏼
通常类似于 ID 数组,除了父记录不存储标识符,而是整个对象(作为平面结构,嵌套记录没有可能关联的实体)。它具有与 ID 数组相同的缺点,但有一个额外的缺点 - 增加了存储的使用。然而(至少目前)这种模式有一个应用领域,而且非常重要。但我们稍后会讨论这个。
我在视频中超前了,展示了 ScreenField,我们会回到这个
通常,有一种想法可以使“非规范”模式虚拟化——这样它们就可以通过第三表以某种方式工作,并且在编辑页面时加载必要的数据(如果需要)。
NumberField 存储数字——就这么简单。
SelectorField 类似于 MultiSelectorField(你可能从他们的名字就猜到了),但更简单一些——这里的关系是一对一或一对多,有两种模式。 TODO:目前这个字段只能通过CodeConfig配置,通过接口配置还没有实现。
SQL 链接提供的一种常见形式,其中父记录字段存储链接记录的 ID。我们以 Reader 为例。是谁?首先是人,人有什么?这是正确的!出生城市(出于某种原因,我们的图书馆也想知道)。
/// user table { id: 'UUID', name: 'String', birth_place_id: 'UUID' }
与 MultiSelectorField 中的对象数组非常相似,但我们将在父记录的字段中存储单个关联值。缺点是一样的,优点也将在下面稍微描述一下。
StringField 存储字符串。这个字段有一个个人设置负责在 Nanc 中编辑的方便性 - 限制可编辑字段的最大高度的参数。如果您的文本很大 - 根本不指定它是有意义的,那么该字段将调整为文本的高度。如果限制为大 - 您可以指定明确的字段高度(以行为单位),然后它将始终如此。最后,对于短字符串,你可以设置为一行,之后无论你往里面写多少,这个字段都不会扩大。
StructureField 允许您在模型记录中存储类型化结构数组。您指定要存储的数据类型,并可以轻松简单地对其进行管理。结构字段的可用类型绝对是一切。因此您可以轻松创建“动态结构字段重复”。 TODO:只能将“平面”字段添加到演示中的 StructureField。
ScreenField 是一个允许您在 Flutter 中编写整个应用程序的字段,就在 Nanc 中!使用 ScreenField,您可以描述单个...屏幕的界面,根据需要对其进行更新,并在几分钟内随时进行 - 无需等待 Apple 和 Google 的审核,这既乏味又伤脑筋。
让我们更详细地分解这种类型的字段(实际上是 Nanc 的一个完整的独立功能分支)。
使用 ScreenField,您可以真正地在您的浏览器(和您的 IDE)中创建几乎任何复杂性的界面,然后 - 无需制作库存出版物 - 更新应用程序中的相应屏幕。如果您需要快速检查假设,这是一个很棒的功能。此功能非常适合应用程序中相对简单(就逻辑而言)的页面,但是,这些页面需要经常更改。未来,这个功能将被扩展到真正可以不受任何限制地创造任何你想要的东西的状态。
现在让我们来看看使用 Nanc 创建动态屏幕的所有方面。
Flutter 中有很多小部件。很多。什么是小部件?它是您组装应用程序的功能块。它可以只是视觉的,也可以是合乎逻辑的——内部有一些行为。 Nanc 提供了一个广泛的已实现小部件列表,您可以使用它们来构建您的 UI。但是可能性越多——了解它们就越难……所以 Nanc 中的屏幕编辑器让您可以访问交互式文档,您可以在其中找到当前实现的小部件、它们具有的参数和可配置属性,以及,在文档中,查看它们如何影响您创建的界面的外观。
Nanc 允许您实时创建界面,但最重要的是 - 它允许您非常轻松快速地完成(连接演示应用程序需要 2 个多小时)。但问题出现了——如何创建 UI 本身?在 Nanc 中没有用于描述 UI 的奇异语法,也没有“太”简单的解决方案,如长 JSON,这会让您讨厌在 Nanc 中创建界面。
找到最佳解决方案的结果是简单明了的 XML 语法。所有标准的 Flutter 小部件都具有完全相同的名称,但采用 XML 格式。例如,Nanc 中的SizedBox
小部件将是<sizedBox>...</sizedBox>
,并且此规则毫无例外地适用于所有小部件。如果小部件具有一些复杂的属性,它将具有与 XML 相同(或更简单)的名称,并带有前缀prop
。例如 - 小部件Container
有一个复杂的属性boxDecoration
,它有自己的内部属性。因此,Nanc 中的此属性将具有以下外观: <prop:decoration>...</prop:decoration>
。此规则适用于所有复杂属性。最后一个方面是相对简单的参数是 XML 标记参数。我们以同一个SizedBox
为例:
<sizedBox width="50" height="50"> ... </sizedBox>
对于某些小部件,实现了额外的参数以简化代码编写,而对于SizedBox
它是设置width
和height
的ize
参数。
这里写的所有内容都在在线文档中,所以如果您忘记了什么或想知道什么,请参考它并在那里找到所有问题的答案。
实施对新小部件的支持 - 只需 10 分钟到几小时。至此,几乎所有的基本小部件都已实现,您可以从中创建具有逻辑的复杂界面。随着时间的推移,Flutter 中可用的所有小部件都将在 Nanc 中实现,你真的可以做任何事情。但这还不是全部。您可以轻松简单地实现自己的小部件,并通过一两行 XML 代码在 Nanc 中使用它们。例如——标准的 Flutter 库中没有 widget 可以让你轻松地显示带有图片的 Carousel Slider。您将不得不自己编写一个实现或使用一些类似这样的开源解决方案。而且,在实现了您需要的功能后,您可以非常轻松地将您的小部件集成到 Nanc 中并使用它。
Nanc 提供的不仅仅是将 XML 代码转换为 Flutter 中的接口的能力。 Nanc 提供模板和逻辑编写功能。条件元素渲染、循环绘制、点击处理——这已经在当前的0.0.1
版本的 Nanc 中。
到目前为止,逻辑部分非常简单——它支持通过点击和预先编写的“.dart”代码中的事件处理进行交互——但最终 Nanc 的这一部分将大大扩展,允许你在浏览器中直接在 Dart 中编写逻辑并让它在您的应用程序中也能正常工作。
处理用户点击的方法如下 - 您可以定义用户可以在您的应用程序中执行的“操作”列表。例如 - 打开内部应用程序屏幕,点击外部链接,显示 SnackBar,弹出模态窗口等等,并提前为这些操作创建处理程序。然后在 Nanc 中以您想要的任何方式使用该操作。有关事件处理的更多信息,请参阅 Nanc 中InkWell
小部件的文档。
Nanc 有一个内置的 XML 编辑器,但不是很方便。它(目前)不可搜索,代码很多时速度不是很快,并且没有自动完成功能。如何忍受它?例如——让用户使用他最喜欢的 IDE,实时观看 Nanc 的变化。让我告诉你怎么做。
这是网络(这是你必须玩的):
将来会添加自动完成支持,也许在遥远的将来......我试图深入研究XML Schema,花了好几天,但到目前为止还不能🤷🏼
另外,我想提一下性能(在移动设备上从 XML 绘制界面)。简而言之,它与 Flutter 本身的性能相同,没有任何开销。目前,“屏幕”是一个延迟渲染的小部件列表 (SliverList),它是异步创建的。稍后,将改进此实现以开始异步呈现小部件,但反过来,显示内容所需的时间将等于呈现 XML 中描述的第一个小部件所需的时间。
为了展示这些功能,已经创建了一组公共演示应用程序来展示 Nanc 现在可以实现的目标。这是 Android 和 Web 上的客户端应用程序(后者暂时也扮演 iOS 应用程序的角色)。以及 Nanc CMS 应用程序。在下面阅读有关它们的更多信息。
Client 是一个客户端演示应用程序,它使用来自 nanc 生态系统的单个库。该库允许您将 XML 转换为 Flutter 中的应用程序界面。此应用程序只有一个屏幕,完全在 Nanc 中创建,并且可以根据需要随时更新,无需储藏室。在右下角有一个带有连接图标的按钮 - 它负责连接到demo-CMS 。有关此“连接”的更多信息,请参见下文。
Admin 是一个 Nanc-CMS 演示应用程序,具有额外实现的逻辑层,它提供与客户端同步的能力(更多关于下面的连接)。在 Nanc-CMS 演示应用程序中,用户的浏览器本身及其 localStorage 充当“后端”。您添加或更改的所有内容仅存储在您的浏览器中。在 Nanc-CMS 中,您可以修改/创建/删除与现有模型相关的数据(您将看到它们),并且 - 您可以通过界面创建自己的模型并对它们执行相同的操作。
作为创建此演示时使用的数据模型的 SQL 表示,您可以按照以下屏幕截图进行指导:
本节仅涉及客户端和 CMS 应用程序中“演示”的逻辑。并实现了模拟与 Nanc 交互的体验和更新客户端的过程。但首先是第一件事。
在实际生产项目中,您可以通过以下方式使用 Nanc:在某处部署静态 Nanc CMS 应用程序,并实现 API 服务。它会与您的后端通信,您可以根据自己的喜好使用 Nanc。您的应用程序包含一个来自 nanc 生态系统的库,它允许您呈现界面。您进行了更新 - 应用程序从您的后端加载新代码,更新 - 每个人都很高兴和满意。
为了展示这个模型的实际效果,同样的事情被实现了,但以一种简化的方式:
Nanc CMS 以静态形式存在,位于 github 页面上,您可以像“在现实生活中”一样使用它,但您的浏览器充当后端。也就是说,API 以“进入网络”的方式实现 - 在浏览器 localStorage 中。这部分我们已经完成了,但仍然有一个移动应用程序,它必须以某种方式向您展示“更新”的过程。
嗯,这就是“连接”的来源。简而言之 - 您可以在任何 Nanc 客户端演示应用程序和任何 Nanc CMS 演示应用程序之间建立直接连接。为此,您需要在 CMS 中单击右下角带有二维码图标的按钮。在出现的模态窗口中,您将看到二维码。接下来你有两把椅子 - 你可以通过按右下角的类似按钮使用移动(或浏览器)客户端应用程序扫描此代码,然后将自动建立连接。或者您可以点击二维码,连接所需的数据将被复制到剪贴板。然后,您必须将此数据粘贴到移动应用程序的输入字段中,然后按下按钮进行连接。建立连接后-您将了解自己。之后,您可以对现有的登陆页面做任何您想做的事情,并在移动应用程序中实时查看更改(保存后)。
但您不仅限于登陆页面。您可以直接在浏览器中创建任何新模型,用内容填充它们,如果您的模型将有一个用于接口描述的字段(类型 Screen)——那么当您保存此类实体时,您还将在应用程序中看到结果——新模型的屏幕将替换现有的应用程序屏幕。唯一的一点是,由于客户端应用程序不知道你新创建的记录是什么类型的字段,它有可能规定的标识符,预计是 ScreenFields。
因此,如果您想完全从头开始创建一个屏幕并将其显示在应用程序中,请使用以下列表中的内容作为 IdField 值:
如果你破坏了某些东西 - 只需重置admin.nanc.io数据(Chrome:F12 -> 应用程序 -> 应用程序 -> 存储 -> 清除站点数据),那么,当你重新打开客户端应用程序时,它总是会加载创建的实际屏幕代码对于这个演示。 (仅当您连接时,才会加载浏览器中的代码)
最后,一个创建包含 ScreenField 的新模型的新页面并在应用程序中呈现它的小示例:
公共演示已准备就绪。介绍文章写好了。 Nanc 的未来计划 - 完成模型创建接口方法的功能完整性,从而可以配置所有字段 - Enum、Selector 和 MultiSelector。修复已知错误,例如更改 StructureField 中元素的位置。然后是“blah blah blah”,然后是“某某”。至少下半年的积压将足够,但扩展功能的进一步模型将基于客户需求,因此如果您有想法/批评/发现错误(并且有很多) /其他 -请填写表格,客户演示应用程序中提供了指向该表格的链接。
如果您对 Nanc 的功能感兴趣并且有合作意向 - 也请填写表格,我们一定会谈。