¡Hola! Hoy quiero presentarles el fruto de mis meses de trabajo por las noches y los fines de semana, diseñado para mejorar la experiencia de administración de contenido y brindar funciones adicionales al mundo del desarrollo de aplicaciones Flutter: un nuevo tipo de CMS.
Estamos hablando de Nanc, que no es un CMS normal . Por qué "no es normal" y qué puede hacer con él, lo descubrirá después de leer este artículo.
El artículo contendrá no solo teoría sino también "práctica": podrá jugar en el entorno limitado de Nanc. Para mostrar al público las capacidades de Nanc, se crearon dos aplicaciones de demostración: una aplicación de cliente que imita cualquier aplicación Flutter y otra que da una idea de lo que puede hacer una aplicación Nanc basada en Flutter: Nanc CMS precompilado. Y la aplicación Nanc CMS, que es un CMS preconfigurado con una capa adicional de lógica implementada para sincronizar la aplicación cliente con el CMS.
Al final de la mayoría de los bloques lógicos del texto, encontrará un video de YouTube con un ejemplo de cómo usar/demostrar algún aspecto de Nanc.
Introducción
Acerca de CMS
tipos de modelos
Editor
Campos
Aplicaciones dinámicas de aleteo
Aplicaciones de demostración de Nanc
Lo que sigue / Epílogos
Entonces. Nanc es un CMS independiente del backend que no extrae su propio backend. ¿Como funciona? Nanc ofrece implementar interfaces de servicio de red, que en este momento tienen 6 métodos pero tendrán alrededor de 10 para el momento del lanzamiento. ¿Esto es mucho o poco? Por ejemplo, se necesitaron 170 líneas de código para implementar la API para la aplicación de demostración. Estos métodos son responsables de todo el trabajo de Nanc con su backend existente. La implementación debe estar escrita en Dart (el lenguaje de desarrollo para Flutter también). En el futuro, Nanc vendrá con implementaciones listas de estas interfaces para ciertas opciones de back-end: Firebase, Supabase, SQLite local/de red e implementaciones genéricas de REST y GraphQL (¿quizás algo más?) y no tendrá que pensar en esta implementación en todo o tendrá que hacerlo, pero sólo un poco.
Un modelo en Nanc es una descripción estructural de una entidad que desea controlar con Nanc. Un modelo contiene un nombre de entidad, varios parámetros visuales y una descripción de los campos.
Una colección es una entidad que puede tener muchas instancias. Una lista de usuarios, libros y reseñas son buenos ejemplos de modelos de tipo Colección en Nanc.
Si está familiarizado con las bases de datos relacionales, un ejemplo de una Colección en Nanc sería casi cualquier tabla de su base de datos.
Un solo (modelo) es una entidad que existe en una sola instancia. Por ejemplo: Cambios de función o una configuración de algo, o... "La pantalla principal de una aplicación móvil". En términos generales, los modelos Solo están diseñados para aumentar la usabilidad, siendo solo una proyección de su base de datos. Y un modelo Solo podría ser fácilmente cualquier tabla en su base de datos con un solo registro. Pero por el momento, la implementación de esta clase de modelos requiere que el registro de este modelo (fila en la base de datos) tenga un id
igual al id
del propio modelo.
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 se puede configurar de varias maneras: por código, a través de la propia interfaz de Nanc y una combinación de estas opciones.
Cuando digo "configuración", me refiero, en primer lugar, a describir la estructura de sus modelos. Tomemos un ejemplo simple, el modelo de características, que es una entidad que describe las características de un producto. Esta entidad contiene los siguientes campos:
Y la implementación como una configuración de código primero será la siguiente:
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), ], ], );
Al describir dicho modelo y usarlo en Nanc CMS, todas las operaciones CRUD de ese modelo estarán disponibles para usted.
Podríamos crear exactamente el mismo modelo de características (llamémoslo Variante de características) a través de la interfaz de Nanc. Y (teniendo en cuenta que todo el trabajo preparatorio para usar Nanc ya está hecho), cuando crea un modelo en Nanc, inmediatamente creará una tabla en la base de datos, y todo el CRUD también estará disponible para usted de inmediato.
Además, puede tomar la forma más segura de no crear nada en la base de datos cuando crea un modelo a través de la interfaz de Nanc. Y cree de forma independiente una tabla en su base de datos y luego, debajo de ella, cree un modelo en Nanc. Por cierto, esto es lo que debe hacer si describe la configuración en el código: las nuevas tablas no aparecerán en su base de datos.
Esta opción está disponible para usted cuando tiene una configuración Code-first y decide cambiarla a través de la interfaz de Nanc. En este caso, todos los cambios posteriores a este modelo solo serán posibles a través de la interfaz y los cambios realizados en el modelo de código original se ignorarán. La única forma de volver a Code-first es "restablecer" el modelo, en cuyo caso se borrarán todos los cambios en la estructura del modelo realizados a través de la interfaz y se volverá a utilizar la configuración de Code-first. Ningún dato se ve afectado por este restablecimiento. Sólo afecta a la estructura del modelo.
Ahora veamos qué tipos de campos admite actualmente Nanc.
BoolField le permite controlar el tipo de datos bool
y personalizar el valor predeterminado.
ColorField le proporciona un práctico selector de color que le permite elegir un color inmediatamente en Nanc. También le brinda la posibilidad de realizar cambios manualmente, editando el código AHEX. AHEX es un HEX-Color clásico (por ejemplo, #10A0CF
), pero con un valor de transparencia adicional especificado primero. En este caso, este color sería similar al color #FF10A0CF
( FF
es 100% de opacidad - el color es completamente opaco). Y así es como se vería el mismo color con un 50 % de opacidad: #7F10A0CF
.
DateField es responsable de controlar la fecha y la hora (ambos valores a la vez, se implementarán los separados para la fecha y la hora más adelante). DateField contiene dos parámetros booleanos que le permiten modificar el comportamiento de este campo convirtiéndolo en una marca de tiempo de creación de entidad y una marca de tiempo de cambio.
DynamicField, por un lado, es un campo muy simple, pero por otro lado, incluye toda la complejidad de los otros campos. Inicialmente, puede configurar solo la apariencia de este campo (cómo se verá en la interfaz de Nanc: el color y el ícono que lo acompaña). Después de eso, este campo puede contener cualquier valor disponible en Nanc, incluido él mismo. ¿Qué quiere decir esto? Esencialmente, DynamicField es un JSON escrito con la capacidad de colocar campos en orden dentro de sí mismo.
EnumField es un campo para seleccionar un valor de múltiples valores. La regla a seguir al seleccionar un EnumField es que si tiene una lista final de valores para seleccionar que no está almacenada en una tabla de base de datos separada, elija Enum. De lo contrario, elija SelectorField, que se analiza con más detalle a continuación. TODO: Por el momento este campo solo se puede configurar a través de CodeConfig, la configuración a través de la interfaz no se resuelve.
HeaderField no es realmente un campo, sino un potenciador visual para su modelo en Nanc. Puede usar este no campo para establecer un encabezado común para un grupo de campos relacionados, o para distinguir los campos del modelo entre sí usando HeaderField como delimitador.
IconField le brinda la posibilidad de seleccionar un ícono (clase IconData
) de un conjunto predefinido de íconos. Actualmente hay alrededor de 25,000 de ellos, y este conjunto incluye los siguientes íconos:
Si es necesario, se pueden agregar otros íconos al conjunto básico de entrega y, en un futuro no muy lejano, también habrá una opción para usar sus propios íconos.
Uno podría preguntarse: "Los íconos están ahí, los colores están ahí, ¿pero las fuentes?" Si lo hizo, puede encontrar la respuesta en la documentación del widget de texto . La respuesta corta es que todas las fuentes de Google Fonts están disponibles para usted.
IdField es un campo tan simple pero tan importante. Todo modelo gestionado por Nanc debe tener al menos un campo de tipo Id. Actualmente, solo se admite el tipo de ID de cadena (puede ser cualquier cadena única dentro de una entidad). También hay planes para agregar soporte para el tipo numérico, que, sin embargo, se puede implementar incluso ahora simplemente convirtiendo los datos de campo en el tipo numérico en la implementación de la API.
MultiSelectorField es un campo bastante complejo que es responsable de implementar una relación relacional de muchos a muchos o de muchos a uno. Hay tres modos de usar este campo. Repasemos cada uno de ellos con más detalle. TODO: Por el momento este campo solo se puede configurar a través de CodeConfig, no se trabaja la configuración a través de la interfaz.
Este modo le brinda la capacidad de almacenar la id
de entidades relacionadas en la entidad principal directamente. Por ejemplo, tenemos dos modelos: Reader y Book. En este modo, los libros tomados por el lector se contabilizarán como identificadores almacenados en el campo Modelo del lector. Por ejemplo como este:
/// user table { id: 'UUID', name: 'String', books: [ 'UUID', 'UUID' // ... ] }
Arriba hay una estructura de tabla de ejemplo expresada usando la sintaxis JSON5.
La desventaja de este modo es la integridad limitada de los datos. Si no implementa la eliminación automática de ID de libros obsoletos (eliminados) del campo Lector books
, obtendrá errores.
El modo clásico de proporcionar relaciones del mundo SQL. Al usar este modo, almacena las relaciones entre entidades en una tabla separada y garantiza la integridad de los datos al 100 %. La siguiente estructura es un ejemplo de este modo:
[ /// user table { id: 'UUID', name: 'String' }, /// book table { id: 'UUID', title: 'String' }, /// user_books_relations table { user_id: 'UUID', book_id: 'UUID' } ]
En el séptimo segundo, puede ver una ligera contracción y, si observa detenidamente, puede notar que la URL de la página ha cambiado; así es como traté de ocultar el error: en el modo de tercera tabla, los datos se guardan en la página principal solo si ya se ha guardado 🤷🏼
Generalmente similar a Array of ids, excepto que el registro principal no almacena identificadores, sino el objeto completo (como una estructura plana, sin posibles entidades asociadas del registro anidado). Tiene la misma desventaja que Array of ids, pero tiene una adicional: mayor uso del almacenamiento. Sin embargo, hay (al menos por el momento) un área de aplicación de este modo, y es muy importante. Pero hablaremos de esto un poco más tarde.
Me estoy adelantando en el video, mostrando ScreenField, volveremos a esto
En general, existe la idea de hacer que los modos "no canónicos" sean virtuales, para que de alguna manera funcionen a través de la Tercera tabla, y los datos necesarios se carguen al editar la página (si es necesario).
NumberField almacena números, así de simple.
SelectorField es similar a MultiSelectorField (como puede adivinar por sus nombres), pero un poco más simple: la relación aquí es de uno a uno o de uno a muchos, y hay dos modos. TODO: Por el momento este campo solo se puede configurar a través de CodeConfig, la configuración a través de la interfaz no se resuelve.
Una forma común de provisión de enlace SQL, donde el campo de registro principal almacena la ID del registro vinculado. Tomemos el Reader como ejemplo. ¿Quién es? En primer lugar, es una persona, y ¿qué tiene una persona? ¡Así es! La ciudad de nacimiento (que nuestra Biblioteca, por alguna razón, también quería saber).
/// user table { id: 'UUID', name: 'String', birth_place_id: 'UUID' }
Muy similar al Array de objetos de MultiSelectorField, pero almacenaremos un único valor asociado en el campo del registro padre. Las desventajas son las mismas, las ventajas también se describirán un poco más abajo.
StringField almacena cadenas. Este campo tiene una configuración personal responsable de la conveniencia de editar en Nanc: el parámetro que limita la altura máxima del campo editable. Si su texto será grande, tiene sentido no especificarlo en absoluto, entonces el campo se ajustará a la altura del texto. Si se limita a grande, puede especificar una altura de campo explícita (en líneas) y siempre será así. Y finalmente, para cadenas cortas, puede configurarlo en una línea, y luego el campo no se expandirá sin importar cuánto escriba después.
StructureField le permite almacenar una matriz de estructuras tipeadas en registros modelo. Usted especifica el tipo de datos que se almacenarán y puede administrarlos de manera fácil y sencilla. Los tipos disponibles para los campos de estructura son absolutamente todo. Por lo tanto, puede crear fácilmente una "Repetición de campo de estructura dinámica". TAREAS: Solo se pueden agregar campos "planos" a StructureField dentro de la demostración.
ScreenField es un campo que te permite escribir una aplicación completa en Flutter, ¡directamente en Nanc! Con ScreenField puede describir la interfaz de una sola... pantalla, actualizarla como desee y hacerlo en cualquier momento en minutos, sin la tediosa y estresante espera de las revisiones de Apple y Google.
Analicemos este tipo de campo (y en realidad una rama funcional completamente separada de Nanc) con un poco más de detalle.
Con ScreenField, realmente puede crear una interfaz de casi cualquier complejidad directamente en su navegador (y su IDE) y luego, sin hacer una publicación en stock, actualizar la pantalla correspondiente en su aplicación. Si necesita verificar hipótesis rápidamente, esta es una gran característica. Esta funcionalidad es excelente para páginas relativamente simples (en términos de lógica) en su aplicación, que, sin embargo, deben cambiar con bastante frecuencia. En el futuro, esta funcionalidad se expandirá a un estado en el que realmente puede crear lo que quiera sin restricciones.
Ahora veamos todos los aspectos de la creación de pantallas dinámicas con Nanc.
Hay muchos widgets en Flutter. Muchos de ellos. ¿Qué es un widget? Es un bloque de funcionalidad a partir del cual ensambla su aplicación. Puede ser solo visual o puede ser lógico, con algún comportamiento interno. Nanc proporciona una extensa lista de widgets implementados que puede usar para crear su interfaz de usuario. Pero cuantas más posibilidades, más difícil es aprender sobre ellas... Así que el editor de pantalla en Nanc le da acceso a una documentación interactiva donde puede averiguar qué widgets están implementados actualmente, qué parámetros y propiedades configurables tienen, y, directamente en la documentación, vea cómo afectan la apariencia de la interfaz que crea.
Nanc le permite crear una interfaz en tiempo real, pero lo más importante: le permite hacerlo de manera muy fácil y rápida (tomó un poco más de 2 horas para crear una interfaz con una aplicación de demostración). Pero surge la pregunta: ¿cómo crear la interfaz de usuario en sí? No existe una sintaxis exótica para describir la interfaz de usuario en Nanc, ni soluciones "demasiado" simples, como JSON largo, que te harán odiar la creación de interfaces en Nanc.
El resultado de encontrar la mejor solución es una sintaxis XML simple y directa. Todos los widgets estándar de Flutter tienen exactamente los mismos nombres, pero en formato XML. Por ejemplo, el widget SizedBox
en Nanc sería <sizedBox>...</sizedBox>
, y esta regla se aplica a todos los widgets sin excepción. Si el widget tiene alguna propiedad compleja, tendrá el mismo nombre (o más simple) que XML, con el prefijo prop
. Por ejemplo, el widget Container
tiene una propiedad compleja boxDecoration
, que tiene sus propias propiedades internas. Entonces, esta propiedad en Nanc tendrá el siguiente aspecto: <prop:decoration>...</prop:decoration>
. Esta regla se aplica a todas las propiedades complejas. Y el último aspecto es que los argumentos que son relativamente simples son parámetros de etiquetas XML. Tomemos el mismo SizedBox
como ejemplo:
<sizedBox width="50" height="50"> ... </sizedBox>
Para algunos widgets, se implementan argumentos adicionales para simplificar la escritura de código, y para SizedBox
es el argumento ize
el que establece tanto width
como height
.
Todo lo escrito aquí está en la documentación en línea, por lo que si olvida algo o quiere saber algo, consúltelo y encuentre respuestas a todas sus preguntas allí.
Implemente el soporte para el nuevo widget: una cuestión de 10 minutos a unas pocas horas. En este punto, casi todos los widgets básicos están implementados, de los cuales puede crear una interfaz compleja con lógica. Con el tiempo, todos los widgets disponibles en Flutter se implementarán en Nanc y realmente puedes hacer todo. Pero eso no es todo. Puede implementar fácil y simplemente sus propios widgets y usarlos en Nanc con una o dos líneas de código XML. Por ejemplo, no hay ningún widget en la biblioteca estándar de Flutter que le permita mostrar fácilmente el Control deslizante de carrusel con imágenes. Tendrá que escribir una implementación usted mismo o usar alguna solución de código abierto como esa . Y, habiendo implementado lo que necesita, puede integrar fácilmente su widget en Nanc y usarlo.
Nanc proporciona más que solo la capacidad de convertir código XML en una interfaz en Flutter. Nanc proporciona capacidades de creación de plantillas y escritura lógica. Representación de elementos condicionales, dibujo de bucles, manejo de toques: esto ya está en la versión actual 0.0.1
de Nanc.
Hasta ahora, la parte de la lógica es bastante sencilla: admite la interacción a través de toques y el manejo de eventos en su código '.dart' escrito con anticipación, pero eventualmente esta parte de Nanc se expandirá considerablemente, permitiéndole escribir la lógica en Dart directamente en el navegador. y haz que funcione en tu aplicación también.
El enfoque para manejar los clics de los usuarios es el siguiente: puede definir una lista de "acciones" que el usuario puede realizar en su aplicación. Por ejemplo: abra la pantalla de la aplicación interna, haga clic en el enlace externo, muestre SnackBar, abra una ventana modal y muchas otras cosas, y cree un controlador para tales acciones por adelantado. Y luego usa esa acción de la forma que quieras en Nanc. Para obtener más información sobre el manejo de eventos, consulte la documentación del widget InkWell
en Nanc.
Nanc tiene un editor XML incorporado, pero no es muy útil. No se puede buscar (todavía), no es muy rápido con mucho código y no tiene autocompletado. ¿Cómo vivir con eso? Por ejemplo, deje que el usuario use su IDE favorito y vea los cambios en Nanc en tiempo real. Déjame enseñarte como.
Y esta es la Web (que es con lo que hay que jugar):
En el futuro se agregará soporte de autocompletar, quizás en un futuro lejano... Intenté profundizar en XML Schema, pasé varios días, pero hasta ahora no pude 🤷🏼
Por separado, me gustaría mencionar el rendimiento (interfaz de dibujo de XML en dispositivos móviles). En resumen, es idéntico al rendimiento de Flutter, sin sobrecarga. Por el momento, la "pantalla" es una lista de widgets renderizada perezosamente (SliverList), creada de forma asíncrona. Más tarde, esta implementación se refinará para comenzar a representar los widgets de forma asíncrona, pero a su vez, el tiempo necesario para mostrar el contenido será igual al tiempo que se tarda en representar el primer widget descrito en el XML.
Para demostrar las capacidades, se ha creado un conjunto público de aplicaciones de demostración para mostrar lo que se puede lograr con Nanc en este momento. Esta es una aplicación de cliente en Android y la Web (esta última también desempeña temporalmente el papel de una aplicación de iOS). Así como la aplicación Nanc CMS. Lea más sobre ellos a continuación.
Client es una aplicación de demostración de cliente que utiliza una sola biblioteca del ecosistema nanc. Esta biblioteca le permite convertir XML en una interfaz de aplicación en Flutter. Esta aplicación tiene una sola pantalla, creada íntegramente en Nanc, y puede actualizarse cuando se desee y en cualquier momento sin necesidad de almacenes. En la parte inferior derecha hay un botón con un ícono de conexión: es responsable de conectarse a demo-CMS . Más sobre lo que será esta "conexión" a continuación.
Admin es una aplicación de demostración de Nanc-CMS, con una capa de lógica implementada adicionalmente, que brinda la capacidad de sincronizar con los clientes (más información sobre la conexión a continuación). En la aplicación de demostración de Nanc-CMS, el propio navegador del usuario y su almacenamiento local actúan como "backend". Todo lo que agrega o cambia se almacena solo en su navegador. En Nanc-CMS puedes modificar/crear/eliminar datos relacionados con los modelos existentes (los verás), y - puedes crear tus propios modelos a través de la interfaz y hacer lo mismo con ellos.
Como representación SQL de los modelos de datos utilizados en la creación de esta demostración, puede guiarse por la siguiente captura de pantalla:
Esta sección se relaciona únicamente con la lógica de la "demostración" en las aplicaciones cliente y CMS. Y se implementó para simular la experiencia de interactuar con Nanc y el proceso de actualización del cliente. Pero primero lo primero.
En un proyecto de producción real, podría usar Nanc de la siguiente manera: implemente una aplicación CMS Nanc estática en algún lugar, con los servicios API implementados. Se comunicaría con su backend y usaría Nanc a su gusto. Su aplicación contiene una biblioteca del ecosistema nanc que le permite renderizar la interfaz. Hizo una actualización: la aplicación cargó un código nuevo desde su backend, actualizó: todos están contentos y satisfechos.
Para mostrar este modelo en acción, se implementa lo mismo, pero de forma simplificada:
Nanc CMS existe como estático, se encuentra en las páginas de github y puede usarlo como "en la vida real", pero su navegador actúa como backend. Es decir, las API se implementaron de tal manera que "van a la red", en el almacenamiento local del navegador. Con esta parte hemos terminado, pero todavía queda una aplicación móvil, que de alguna manera debe mostrarte el proceso de "actualización".
Bueno, ahí es donde entra en juego la "conexión". En resumen, puede establecer una conexión directa entre cualquier aplicación de demostración de cliente de Nanc y cualquier aplicación de demostración de Nanc CMS. Para hacer esto, debe hacer clic en el botón inferior derecho con el icono del código QR en el CMS. En la ventana modal que aparece, verá el código QR. A continuación, tiene dos sillas: puede escanear este código con la aplicación de cliente móvil (o navegador) presionando el botón similar en la parte inferior derecha, luego la conexión se establecerá automáticamente. O puede hacer clic en el código QR y los datos necesarios para la conexión se copiarán en el portapapeles. Luego tendrás que pegar estos datos en el campo de entrada de la aplicación móvil y conectarte presionando el botón. Cuando se establezca la conexión, te comprenderás a ti mismo. Después de eso, puede hacer lo que quiera con la página de destino existente y ver los cambios en tiempo real (después de guardar), en la aplicación móvil.
Pero no está limitado a la página de destino . Puede crear cualquier modelo nuevo directamente en el navegador, llenarlo con contenido y, si sus modelos tendrán un campo para la descripción de la interfaz (escriba Pantalla), cuando guarde dichas entidades, también verá el resultado en la aplicación: el La pantalla del nuevo modelo reemplazará la pantalla de la aplicación existente. El único punto es que, dado que la aplicación cliente no sabe qué tipo de campo es su registro recién creado, tiene prescritos posibles identificadores, que se espera que sean ScreenFields.
Entonces, si desea crear una pantalla completamente desde cero y mostrarla en la aplicación, use algo de la siguiente lista como valor de IdField:
Si rompe algo, simplemente reinicie los datos de admin.nanc.io (Chrome: F12 -> Aplicación -> Aplicación -> Almacenamiento -> Borrar datos del sitio), bueno, cuando vuelva a abrir la aplicación cliente, siempre cargará el código de pantalla real creado para esta demostración. (El código de su navegador se cargará solo si se conecta)
Y finalmente, un pequeño ejemplo de cómo crear una nueva página de un nuevo modelo que contiene un ScreenField y representarlo en la aplicación:
La demostración pública está lista. Se escribe el artículo introductorio. Planes futuros para Nanc: completar la integridad funcional del enfoque de interfaz para la creación de modelos, haciendo posible configurar todos los campos: Enum, Selector y MultiSelector. Solucione errores conocidos, como cambiar la posición de los elementos en StructureField. Luego "bla, bla, bla", y luego "fulano de tal". La acumulación será suficiente para el próximo medio año al menos, pero el modelo adicional de expansión de la funcionalidad se basará en las necesidades del cliente, por lo que si tiene ideas/críticas/errores encontrados (y hay muchos de ellos) /cualquier otra cosa: complete el formulario, cuyo enlace está disponible en la aplicación de demostración del cliente.
Si está interesado en las características de Nanc y está interesado en la cooperación, complete el formulario también y definitivamente hablaremos.