Uma síntese do Atomic Design e exemplos em Flutter e Swift

Venho estudando a metodologia de Atomic Desing e como ela é universal de ser aplicada consigo usar frameworks de UI de maneira extremamente eficiente

Atomic

O que é ?

Eu imagino que você já tenha ouvido falar sobre essa metodologia que nós faz programadores de front end (mobile e web) quase que com super poderes. Atomic Design de Brad Frost é realmente uma arquitetura e modelo de criação muito bom para criar sistemas complexos e tirar o boilarplate que vemos muitas vezes, ele vem trazer simplicidade pro front.

O objetivo é nos fazer mais eficientes para podermos focar no que é importante. Devemos padronizar o que deve ser padronizado.

Exemplos

Eu trabalho em um ambiente com essa métodologia e recomendo muito. Componentizar além de ser útil, quando levado a sério pode ser prazeroso e altamente eficiente, ter alguém que cuida ativamente dos componentes de um projeto pode transformar a produtividade. Nesse blog post, não vou só evangelizar como mostrarei exemplos dessa metodologia. Para saber mais sempre veja a fonte primaria, Brad Frost fez o livro em 2016 e é extremamente moderno. Vamos ao código, vou começar falando de um código em swift extremamente simples mas ao mesmo tempo com o núcleo da metodologia.

Esse código a seguir é uma inspiração de um tuíte que eu vi onde achei a Ui criada muito bonita com tão pouco código. An awesome iOS developer named @tgrapperon made this O tuíte original é esse:

func glassText(text: String) -> some View {
    Text(text)
            .foregroundStyle(.secondary)
            .padding()
            .background(
                    .regularMaterial,
                    in: RoundedRectangle(
                            cornerRadius: 11,
                            style: .continuous
                    )
            )
            .shadow(radius: 11)
            .padding()
}

// Função que faz tela generica
struct ContentView: View {
    var body: some View {
        ZStack {
            HStack {
                glassText(text: "Manda") // uso do texto
                glassText(text: "Bala") // uso do texto
            }
                    .font(.largeTitle.weight(.black))
        }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(
                        LinearGradient(colors: [.pink, .yellow],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                        ),
                        ignoresSafeAreaEdges: .all
                )
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Exemplo simples de atomic design

Resultado em forma de UI

UI

Ao invés de toda vez configurar como deve ser um texto em minha code base, eu deveria ter ela por padrão já como componente e usar ele da maneira mais simples o póssivel, como se fosse uma função. O único trabalho que deve se ter é de configurar e criar a primeira vez.

Complexidade ?

Tudo pode ser componentizado até mesmo modúlos inteiros, tem um fluxo que se repete pelo sistema ? faça dele um componente/classe/função e apenas chame e deixe ele fazer o seu trabalho. Seu dever é criar maquinas que trabalham por si só ou com o mínimo de esforço seja computacional ou humano.

class _WelcomePageState extends State<WelcomePage> {
  List<Page> pageContent = [
    Page("welcome-one.png", "Viaje", "Para as Montanhas",
        "Conhecer a natureza além de te dar a sensação de liberdade te fará muito mais feliz como pessoa."),
    Page("welcome-two.png", "Viva", "Os seus sonhos",
        "Essa experiencia mudará sua vida de formas que nunca antes você sentiu."),
    Page("welcome-three.png", "Aprenda", "Sobre as culturas",
        "Isso é único e apenas conhecendo outros lugares te darão isso."),
  ];

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
          scrollDirection: Axis.vertical,
          itemCount: pageContent.length,
          itemBuilder: (_, index) {
            return AppCarrossel(
              bg: pageContent[index].bg,
              title: pageContent[index].title,
              subtitle: pageContent[index].subtitle,
              text: pageContent[index].text,
              currentIndex: index,
              pagesNumber: pageContent.length,
            );
          }),
    );
  }
}

Código dá página de onboarding de um projeto ficticio

class AppCarrossel extends StatelessWidget {
  String bg;
  String title;
  String subtitle;
  String text;
  int pagesNumber;
  int currentIndex;

  AppCarrossel(
      {Key? key,
      required this.bg,
      required this.title,
      required this.subtitle,
      required this.text,
      required this.pagesNumber,
      required this.currentIndex})
      : super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      width: double.maxFinite,
      height: double.maxFinite,
      decoration: BoxDecoration(
          image: DecorationImage(
              fit: BoxFit.cover, image: AssetImage("img/" + bg))),
      child: Container(
        margin: const EdgeInsets.only(top: 120, left: 20, right: 20),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                AppLargeText(text: title),
                AppText(text: subtitle, size: 30),
                Container(
                  margin: EdgeInsets.only(top: 20, bottom: 40),
                  width: 250,
                  child: AppText(
                    text: text,
                    color: AppColors.textColor2,
                    size: 14,
                  ),
                ),
                ResponsiveButton(
                  width: 120,
                )
              ],
            ),
            Column(
              children: List.generate(pagesNumber, (indexDots) {
                return Container(
                  margin: EdgeInsets.only(bottom: 2),
                  width: 8,
                  height: currentIndex == indexDots ? 25 : 8,
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(8),
                      color: currentIndex == indexDots
                          ? AppColors.mainColor
                          : AppColors.mainColor.withOpacity(0.8)),
                );
              }),
            )
          ],
        ),
      ),
    );
  }
}

Componente de página de carrosel

Resultado

Observe como o componente por mais que grande ele só recebe os dados de um componente 'smart' e dai ele constroi a tela. O resultado pode ser visto a seguir:

Quem é o foco disso ?

Isso é para nós ajudar como programadores, é o foco dessa metodologia buscamos deixar o dia dia de nossos colegas de trabalho mais fácil e eficiente independente da task ele terá algo quase pronto pra utilizar, no maximo terá de ajustar algo que já existe e funciona, Um exemplo legal disso é esse código em flutter onde eu componentizo as páginas que serão usadas em um carrosel. Quando um colega programador for utilizar esse componente ele só precisa seguir o modelo já definido que tudo ocorerrá bem.

Conclusão

Essa metodologia pode ser o padrão sem mesmo ter sido acordado, usar componentes prontos como foco é uma ótima prática no front end. Elevar isso ao nível profissional, com disciplina e dedidcação transforma projetos em obras de arte, com a castata de estilo afetando todos os lugares como deveria e caso for preciso a aplicação de estilo ou funcionalidade adicional, todos os lugares desse componente compartilham esse poder.

Contato

Se quiser discutir sobre qualquer assunto ou viu algum erro, não hesite em me marcar ou chamar no Twitter: @que_cara_legal
Estou sempre tentando trazer o que tenho estudado, as vezes traduzindo algums tópicos divertidos que me chamam atenção.