Oi! Hoje quero apresentar a vocês o fruto dos meus meses de trabalho noturno e aos finais de semana, pensado para melhorar a experiência de gerenciamento de conteúdo e trazer funcionalidades adicionais ao mundo do desenvolvimento de aplicativos Flutter - um novo tipo de CMS.
Estamos falando de Nanc - N ot A N ormal C MS. Por que "não é normal" e o que você pode fazer com isso, você descobrirá depois de ler este artigo.
O artigo conterá não apenas teoria, mas também "prática" - você poderá jogar na caixa de areia Nanc. Para mostrar ao público as capacidades do Nanc, foram feitos dois aplicativos de demonstração: um aplicativo cliente que imita qualquer aplicativo Flutter e outro que dá uma ideia do que um aplicativo Nanc baseado em Flutter pode fazer - Nanc CMS pré-construído. E o aplicativo Nanc CMS, que é um CMS pré-configurado com uma camada adicional de lógica implementada para sincronizar o aplicativo cliente com o CMS.
Ao final da maioria dos blocos lógicos do texto, você encontrará um vídeo do youtube com um exemplo de como usar/demonstrar algum aspecto do Nanc.
Introdução
Sobre CMS
tipos de modelos
editor
Campos
Aplicativos dinâmicos do Flutter
Aplicativos de demonstração Nanc
O que vem a seguir / Posfácios
Então. O Nanc é um CMS independente de back-end que não puxa seu próprio back-end. Como funciona? A Nanc se oferece para implementar interfaces de serviço de rede, que agora têm 6 métodos, mas terão cerca de 10 no momento do lançamento. Isso é muito ou pouco? Por exemplo, foram necessárias 170 linhas de código para implementar a API do aplicativo de demonstração. Esses métodos são responsáveis por todo o trabalho do Nanc com seu back-end existente. A implementação deve ser escrita em Dart (a linguagem de desenvolvimento do Flutter também). No futuro, o Nanc virá com implementações prontas dessas interfaces para determinadas opções de back-end - Firebase, Supabase, local/rede SQLite e implementações genéricas de REST e GraphQL (talvez outra coisa?) tudo ou terá que, mas só um pouco.
Um modelo no Nanc é uma descrição estrutural de uma entidade que você deseja controlar com o Nanc. Um modelo contém um nome de entidade, vários parâmetros visuais e uma descrição dos campos.
Uma coleção é uma entidade que pode ter muitas instâncias. Uma lista de usuários, livros e resenhas são bons exemplos de modelos do tipo Coleção em Nanc.
Se você estiver familiarizado com bancos de dados relacionais, um exemplo de Coleção em Nanc seria quase qualquer tabela em seu banco de dados.
Um solo (modelo) é uma entidade que existe em uma única instância. Por exemplo - Alternância de recursos ou uma configuração de algo, ou... "A tela principal de um aplicativo móvel". De um modo geral, os Solo-models são projetados para aumentar a usabilidade, sendo apenas uma projeção do seu banco de dados. E um modelo Solo pode facilmente ser qualquer tabela em seu banco de dados com apenas um registro. Mas, no momento, a implementação dessa classe de modelos exige que o registro desse modelo (linha no banco de dados) tenha um id
igual ao id
do próprio 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: [ ...
O Nanc pode ser configurado de várias formas: por código, pela própria interface do Nanc e uma combinação dessas opções.
Quando digo "configuração", quero dizer, antes de tudo, descrever a estrutura de seus modelos. Vamos dar um exemplo simples, o modelo Feature, que é uma entidade que descreve as funcionalidades de um produto. Esta entidade contém os seguintes campos:
E a implementação como uma configuração code-first será a seguinte:
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), ], ], );
Ao descrever esse modelo e usá-lo no Nanc CMS, todas as operações CRUD desse modelo ficam disponíveis para você.
Poderíamos criar exatamente o mesmo modelo de recurso (vamos chamá-lo de variante de recurso) por meio da interface Nanc. E (considerando que todo o trabalho preparatório para o uso do Nanc está feito) - ao criar um modelo no Nanc, você criará imediatamente uma tabela no banco de dados, e todo o CRUD também estará disponível para você imediatamente.
Além disso, você pode seguir o caminho mais seguro de não criar nada no banco de dados ao criar um modelo por meio da interface Nanc. E crie uma tabela de forma independente em seu banco de dados e, abaixo dela, crie um modelo no Nanc. A propósito, isso é o que você deve fazer se descrever a configuração em código - novas tabelas não aparecerão em seu banco de dados a partir dele.
Esta opção fica disponível para você quando você tem uma configuração Code-first e decide alterá-la através da interface Nanc. Nesse caso, todas as alterações posteriores nesse modelo só serão possíveis por meio da interface, e as alterações feitas no modelo de código original serão ignoradas. A única maneira de retornar ao Code-first é "redefinir" o modelo, caso em que todas as alterações na estrutura do modelo feitas por meio da interface serão apagadas e a configuração do Code-first será usada novamente. Nenhum dado é afetado por esta redefinição. Afeta apenas a estrutura do modelo.
Agora vamos ver quais tipos de campos o Nanc suporta atualmente.
BoolField permite controlar o tipo de dados bool
e personalizar o valor padrão.
O ColorField fornece um seletor de cores útil que permite escolher uma cor imediatamente no Nanc. Ele também oferece a capacidade de fazer alterações manualmente, editando o código AHEX. AHEX é uma cor HEX clássica (por exemplo, #10A0CF
), mas com um valor de transparência adicional especificado primeiro. Neste caso, esta cor seria semelhante à cor #FF10A0CF
( FF
é 100% opacidade - a cor é completamente opaca). E é assim que a mesma cor ficaria com 50% de opacidade: #7F10A0CF
.
DateField é responsável por controlar data e hora (os dois valores ao mesmo tempo, valores separados para data e hora serão implementados posteriormente). DateField contém dois parâmetros booleanos que permitem modificar o comportamento desse campo, tornando-o um carimbo de hora de criação de entidade e um carimbo de hora de mudança.
DynamicField, por um lado, é um campo muito simples, mas, por outro lado, inclui toda a complexidade dos outros campos. Inicialmente, você pode configurar apenas a aparência deste campo (como ficará na interface do Nanc - a cor e o ícone que o acompanha). Depois disso, este campo pode conter quaisquer valores disponíveis no Nanc, inclusive ele próprio. O que isto significa? Essencialmente, DynamicField é um JSON digitado com a capacidade de posicionar os campos em ordem dentro de si.
EnumField é um campo para selecionar um valor de vários valores. A regra a seguir ao selecionar um EnumField é que, se você tiver uma lista final de valores para selecionar que não esteja armazenada em uma tabela de banco de dados separada, escolha Enum. Caso contrário, escolha SelectorField, que é discutido com mais detalhes abaixo. TODO: No momento este campo só pode ser configurado via CodeConfig, configuração via interface não está funcionando.
HeaderField não é realmente um campo, mas um aprimorador visual para seu modelo no Nanc. Você pode usar este campo não para definir um cabeçalho comum para um grupo de campos relacionados ou para distinguir campos de modelo uns dos outros usando HeaderField como um delimitador.
IconField permite selecionar um ícone (classe IconData
) de um conjunto predefinido de ícones. Existem atualmente cerca de 25.000 deles, e este conjunto inclui os seguintes ícones:
Se necessário, outros ícones podem ser adicionados ao conjunto básico de entrega e, em um futuro não muito distante, haverá a opção de usar seus próprios ícones também.
Alguém pode se perguntar: "Os ícones estão lá, as cores estão lá, mas as fontes?" Se sim, você pode encontrar a resposta na documentação do widget Texto . A resposta curta é que todas as fontes do Google Fonts estão disponíveis para você.
IdField é um campo tão simples, mas tão importante. Todo modelo gerenciado pelo Nanc deve possuir pelo menos um campo do tipo Id. Atualmente, apenas o tipo de ID de string é suportado (pode ser qualquer string exclusiva dentro de uma entidade). Existem planos para adicionar suporte para tipo numérico também, que, no entanto, pode ser implementado mesmo agora simplesmente convertendo os dados do campo para o tipo numérico na implementação da API.
MultiSelectorField é um campo bastante complexo que é responsável por implementar um relacionamento relacional muitos-para-muitos ou muitos-para-um. Existem três modos de usar este campo. Vamos percorrer cada um deles com mais detalhes. TODO: No momento este campo só pode ser configurado através do CodeConfig, a configuração através da interface não está funcionando.
Este modo oferece a capacidade de armazenar o id
de entidades relacionadas diretamente na entidade pai. Por exemplo - temos dois modelos - Leitor e Livro. Neste modo, os livros levados pelo leitor serão contabilizados como ids armazenados no campo Modelo do leitor. Por exemplo assim:
/// user table { id: 'UUID', name: 'String', books: [ 'UUID', 'UUID' // ... ] }
Acima está um exemplo de estrutura de tabela expressa usando a sintaxe JSON5.
A desvantagem desse modo é a integridade limitada dos dados. Se você não implementar a remoção automática de IDs de livros obsoletos (excluídos) do campo Leitor de books
, você receberá erros.
O modo clássico de fornecer relacionamentos do mundo SQL. Ao usar este modo, você armazena os relacionamentos entre entidades em uma tabela separada e garante 100% de integridade dos dados. A estrutura a seguir é um exemplo desse modo:
[ /// user table { id: 'UUID', name: 'String' }, /// book table { id: 'UUID', title: 'String' }, /// user_books_relations table { user_id: 'UUID', book_id: 'UUID' } ]
No 7º segundo você pode ver uma leve contração e se você olhar de perto você pode notar que o url da página mudou - foi assim que tentei esconder o bug: no terceiro modo de tabela os dados são salvos na página pai somente se já foi salvo 🤷🏼
Geralmente semelhante ao Array de ids, exceto que o registro pai não armazena identificadores, mas o objeto inteiro (como uma estrutura plana, sem possíveis entidades associadas ao registro aninhado). Tem a mesma desvantagem do Array of ids, mas tem uma desvantagem adicional - maior uso de armazenamento. No entanto existe (pelo menos por enquanto) uma área de aplicação deste modo, e é muito importante. Mas vamos falar sobre isso um pouco mais tarde.
Estou me adiantando no vídeo, mostrando o ScreenField, já voltaremos a isso
Em geral, existe a ideia de tornar os modos "não canônicos" virtuais - para que funcionem de alguma forma na Terceira tabela e os dados necessários sejam carregados ao editar a página (se necessário).
NumberField armazena números - simples assim.
SelectorField é semelhante a MultiSelectorField (como você pode imaginar pelos nomes), mas um pouco mais simples - a relação aqui é um-para-um ou um-para-muitos, e há dois modos. TODO: No momento este campo só pode ser configurado via CodeConfig, configuração via interface não está funcionando.
Uma forma comum de fornecimento de link SQL, em que o campo de registro pai armazena a ID do registro vinculado. Tomemos o Reader como exemplo. Quem é esse? Em primeiro lugar, é uma pessoa, e o que uma pessoa tem? Isso mesmo! A cidade de nascimento (que nossa Biblioteca, por algum motivo, também queria saber).
/// user table { id: 'UUID', name: 'String', birth_place_id: 'UUID' }
Muito semelhante ao Array de objetos do MultiSelectorField, mas armazenaremos um único valor associado no campo do registro pai. As desvantagens são as mesmas, as vantagens também serão descritas um pouco abaixo.
StringField armazena strings. Este campo possui uma configuração pessoal responsável pela comodidade de edição no Nanc - o parâmetro que limita a altura máxima do campo editável. Se o seu texto for grande - faz sentido não especificá-lo, o campo se ajustará à altura do texto. Se limitado a grande - você pode especificar a altura do campo explícito (em linhas) e sempre será assim. E, finalmente, para strings curtas, você pode configurá-lo para uma linha e, em seguida, o campo não se expandirá, não importa o quanto você escreva nele depois.
StructureField permite que você armazene uma matriz de estruturas digitadas em registros de modelo. Você especifica o tipo de dados a serem armazenados e pode gerenciá-los de maneira fácil e simples. Os tipos disponíveis para campos de estrutura são absolutamente tudo. Assim, você pode criar facilmente uma "Repetição de campo de estrutura dinâmica". TODO: Somente campos "planos" podem ser adicionados ao StructureField dentro da demonstração.
ScreenField é um campo que permite escrever uma aplicação inteira no Flutter, direto no Nanc! Com o ScreenField, você pode descrever a interface de uma única tela, atualizá-la como quiser e fazê-lo a qualquer momento em minutos - sem a espera tediosa e angustiante por análises da Apple e do Google.
Vamos detalhar um pouco mais esse tipo de campo (e, na verdade, todo um desdobramento funcional separado do Nanc).
Com ScreenField, você pode realmente criar uma interface de quase qualquer complexidade diretamente em seu navegador (e seu IDE) e, em seguida, sem fazer uma publicação de estoque, atualizar a tela correspondente em seu aplicativo. Se você precisa verificar hipóteses rapidamente, esse é um ótimo recurso. Essa funcionalidade é ótima para páginas relativamente simples (em termos de lógica) em seu aplicativo, que, no entanto, precisam ser alteradas com bastante frequência. No futuro, essa funcionalidade será expandida para um estado em que você pode realmente criar o que quiser, sem nenhuma restrição.
Agora vamos percorrer todos os aspectos da criação de telas dinâmicas com o Nanc.
Existem muitos widgets no Flutter. Muitos deles. O que é um widget? É um tijolo de funcionalidade a partir do qual você monta seu aplicativo. Pode ser apenas visual ou pode ser lógico - com algum comportamento interno. O Nanc fornece uma extensa lista de widgets implementados que você pode usar para construir sua IU. Mas quanto mais possibilidades - mais difícil é aprender sobre eles... Portanto, o editor de tela do Nanc oferece acesso a uma documentação interativa onde você pode descobrir quais widgets estão implementados atualmente, quais parâmetros e propriedades configuráveis eles possuem e, diretamente na documentação, veja como eles afetam a aparência da interface que você cria.
O Nanc permite que você crie uma interface em tempo real, mas o mais importante - permite que você faça isso com muita facilidade e rapidez (levou um pouco mais de 2 horas para fazer a interface de um aplicativo de demonstração). Mas surge a pergunta - como criar a própria interface do usuário? Não existe uma sintaxe exótica para descrever a interface do usuário no Nanc, nem soluções "muito" simples, como JSON longo, que farão você odiar a criação de interfaces no Nanc.
O resultado de encontrar a melhor solução é uma sintaxe XML simples e direta. Todos os widgets Flutter padrão têm exatamente os mesmos nomes, mas em formato XML. Por exemplo, o widget SizedBox
no Nanc seria <sizedBox>...</sizedBox>
, e esta regra se aplica a todos os widgets sem exceção. Se o widget tiver alguma propriedade complexa, ele terá o mesmo nome (ou mais simples) do XML, com o prefixo prop
. Por exemplo - o widget Container
possui uma propriedade complexa boxDecoration
, que possui suas próprias propriedades internas. Assim, esta propriedade no Nanc terá o seguinte aspecto: <prop:decoration>...</prop:decoration>
. Esta regra se aplica a todas as propriedades complexas. E o último aspecto é que argumentos relativamente simples são parâmetros de tags XML. Vamos pegar o mesmo SizedBox
como exemplo:
<sizedBox width="50" height="50"> ... </sizedBox>
Para alguns widgets, argumentos adicionais são implementados para simplificar a escrita do código e, para SizedBox
, é o argumento ize
que define width
e height
.
Tudo o que está escrito aqui está na documentação online, então se você esquecer alguma coisa ou quiser saber alguma coisa, consulte-a e encontre lá as respostas para todas as suas dúvidas.
Implemente o suporte para o novo widget - uma questão de 10 minutos a algumas horas. Neste ponto, quase todos os widgets básicos são implementados, dos quais você pode criar uma interface complexa com lógica. Com o tempo, todos os widgets disponíveis no Flutter serão implementados no Nanc, e você pode realmente fazer tudo. Mas isso não é tudo. Você pode facilmente e simplesmente implementar seus próprios widgets e usá-los no Nanc com uma ou duas linhas de código XML. Por exemplo - não há widget na biblioteca padrão do Flutter que permita exibir facilmente o controle deslizante de carrossel com imagens. Você mesmo terá que escrever uma implementação ou usar alguma solução de código aberto como tal . E, tendo implementado o que você precisa - você pode facilmente integrar seu widget ao Nanc e usá-lo.
O Nanc fornece mais do que apenas a capacidade de converter código XML em uma interface no Flutter. O Nanc fornece recursos de criação de modelos e lógica. Renderização de elemento condicional, desenho de loop, manipulação de toque - isso já está na versão 0.0.1
atual do Nanc.
Até agora, a parte lógica é bastante direta - ela oferece suporte à interação por meio de toques e manipulação de eventos em seu código `.dart' escrito com antecedência - mas, eventualmente, essa parte do Nanc se expandirá consideravelmente, permitindo que você escreva lógica em Dart diretamente no navegador e fazê-lo funcionar em seu aplicativo também.
A abordagem para lidar com os cliques do usuário é a seguinte - você pode definir uma lista de "ações" que o usuário pode realizar em seu aplicativo. Por exemplo - abra a tela interna do aplicativo, clique no link externo, exiba o SnackBar, abra a janela modal e muitas outras coisas e crie um manipulador para tais ações com antecedência. E então use essa ação da maneira que quiser no Nanc. Para obter mais informações sobre manipulação de eventos, consulte a documentação do widget InkWell
no Nanc.
O Nanc possui um editor de XML embutido, mas não é muito prático. Não é pesquisável (ainda), não é muito rápido com muito código e não possui preenchimento automático. Como viver com isso? Por exemplo - deixe o usuário usar seu IDE favorito e observe as alterações no Nanc em tempo real. Deixa-me mostrar-te como.
E esta é a Web (que é o que você tem para brincar):
Futuramente será adicionado suporte ao autocomplete, talvez num futuro distante...Tentei me aprofundar no XML Schema, passei vários dias, mas até agora não consegui 🤷🏼♀️
Separadamente, gostaria de mencionar o desempenho (desenho de interface de XML em dispositivos móveis). Resumindo, é idêntico ao desempenho do próprio Flutter, sem nenhum overhead. No momento, a "tela" é uma lista de widgets renderizada lentamente (SliverList), criada de forma assíncrona. Posteriormente, essa implementação será refinada para começar a renderizar os widgets de forma assíncrona, mas por sua vez, para que o tempo necessário para exibir o conteúdo seja igual ao tempo que leva para renderizar o primeiro widget descrito no XML.
Para demonstrar os recursos, um conjunto público de aplicativos de demonstração foi criado para mostrar o que pode ser alcançado com o Nanc no momento. Este é um aplicativo cliente no Android e na Web (este último também desempenha temporariamente o papel de um aplicativo iOS). Bem como o aplicativo Nanc CMS. Leia mais sobre eles abaixo.
Client é um aplicativo de demonstração do cliente que usa uma única biblioteca do ecossistema nanc. Esta biblioteca permite converter XML em uma interface de aplicativo no Flutter. Esta aplicação possui apenas uma tela, criada inteiramente em Nanc, podendo ser atualizada conforme desejado e a qualquer momento sem necessidade de almoxarifados. No canto inferior direito existe um botão com um ícone de conexão - é responsável por conectar-se ao demo-CMS . Mais sobre o que será essa "conexão" abaixo.
Admin é um aplicativo de demonstração Nanc-CMS, com uma camada de lógica implementada adicionalmente, que fornece a capacidade de sincronizar com clientes (mais sobre a conexão abaixo). No aplicativo de demonstração Nanc-CMS, o próprio navegador do usuário e seu localStorage atuam como o "back-end". Tudo o que você adiciona ou altera é armazenado apenas no seu navegador. No Nanc-CMS você pode modificar/criar/excluir dados relacionados aos modelos existentes (você os verá), e - você pode criar seus próprios modelos através da interface e fazer o mesmo com eles.
Como uma representação SQL dos modelos de dados usados na criação desta demonstração, você pode ser guiado pela seguinte captura de tela:
Esta seção refere-se exclusivamente à lógica da "demonstração" nos aplicativos cliente e CMS. E foi implementado para simular a experiência de interação com o Nanc e o processo de atualização do cliente. Mas as primeiras coisas primeiro.
Em um projeto de produção real, você poderia usar o Nanc da seguinte maneira: implantar um aplicativo Nanc CMS estático em algum lugar, com serviços de API implementados. Ele se comunicaria com seu back-end e você usaria o Nanc ao seu gosto. Seu aplicativo contém uma biblioteca do ecossistema nanc que permite renderizar a interface. Você fez uma atualização - o aplicativo carregou um novo código do seu back-end, atualizado - todos estão felizes e satisfeitos.
Para mostrar esse modelo em ação, implementamos o mesmo, mas de forma simplificada:
O Nanc CMS existe como estático, nas páginas do github e você pode usá-lo como "na vida real", mas seu navegador atua como back-end. Ou seja, as APIs foram implementadas de forma que "vão para a rede" - no navegador localStorage. Com esta parte terminamos, mas ainda existe um aplicativo móvel, que deve de alguma forma mostrar o processo de "atualização".
Bem, é aí que entra a "conexão". Resumindo - você pode estabelecer uma conexão direta entre qualquer aplicativo de demonstração do cliente Nanc e qualquer aplicativo de demonstração do Nanc CMS. Para fazer isso, você precisa clicar no botão inferior direito com o ícone do código QR no CMS. Na janela modal que aparece, você verá o código QR. Em seguida, você tem duas cadeiras - você pode escanear este código com o aplicativo cliente móvel (ou navegador) pressionando o botão semelhante no canto inferior direito, então a conexão será estabelecida automaticamente. Ou você pode clicar no código QR e os dados necessários para a conexão serão copiados para a área de transferência. Então você terá que colar esses dados no campo de entrada do aplicativo móvel e conectar pressionando o botão. Quando a conexão for estabelecida - você se entenderá. Depois disso, você pode fazer o que quiser com a Landing Page existente e ver as alterações em tempo real (após salvar) - no aplicativo móvel.
Mas você não está limitado à Landing Page . Você pode criar novos modelos diretamente no navegador, preenchê-los com conteúdo e, se seus modelos tiverem um campo para descrição da interface (tipo Tela) - então, ao salvar essas entidades, você também verá o resultado no aplicativo - o tela do novo modelo substituirá a tela do aplicativo existente. O único ponto é que, como a aplicação cliente não sabe que tipo de campo é o seu registro recém-criado, ela possui possíveis identificadores prescritos, que se espera que sejam ScreenFields.
Portanto, se você deseja criar uma tela inteiramente do zero e exibi-la no aplicativo, use algo da lista a seguir como o valor de IdField:
Se você quebrar alguma coisa - basta redefinir os dados admin.nanc.io (Chrome: F12 -> Aplicativo -> Aplicativo -> Armazenamento -> Limpar dados do site), bem, quando você reabrir o aplicativo cliente, ele sempre carregará o código de tela real criado para esta demonstração. (O código do seu navegador será carregado apenas se você se conectar)
E por fim, um pequeno exemplo de criação de uma nova página de um novo modelo contendo um ScreenField e renderizando-o na aplicação:
A demonstração pública está pronta. O artigo introdutório está escrito. Planos futuros para Nanc - para completar a integridade funcional da abordagem de interface para criação de modelo, tornando possível configurar todos os campos - Enum, Selector e MultiSelector. Corrija bugs conhecidos, como alterar a posição dos elementos no StructureField. Depois "blah blah blah" e depois "fulano de tal". A lista de pendências será suficiente para o próximo semestre, pelo menos, mas o modelo adicional de expansão da funcionalidade será baseado nas necessidades do cliente, portanto, se você tiver ideias/críticas/bugs encontrados (e há muitos deles) /qualquer outra coisa - preencha o formulário, cujo link está disponível no aplicativo de demonstração do cliente.
Se você está interessado nos recursos do Nanc e está interessado em cooperação - preencha o formulário também e com certeza conversaremos.