Tokens de diseño en Flutter

Tokens de diseño en Flutter

¿Alguna vez has pensado cómo hacer que tu aplicación Flutter sea más hermosa y a tu manera, pero también tener un patrón que se usará en ella? ¿Cuáles son los elementos principales que necesitamos aprender para poder hacer esta personalización? ¿Dónde viven, qué hacen, qué comen?

En este artículo, aprenderemos qué son los tokens de color, su tipografía (texto) y cómo funcionan en Flutter.

¿Qué es un token de diseño?


Los tokens son pequeñas partes de un diseño que conforman la personalidad de tu aplicación. Cada color y cada estilo de texto puede considerarse un token. Los tokens de diseño de tu aplicación te permitirán ajustar todos los componentes, por lo que es muy importante comprender todo su potencial y cómo trabajar con ellos.

Los tokens de diseño permiten flexibilidad y coherencia dentro de una aplicación, ya que hace posible que los equipos de diseño definan la función del color en una interfaz de usuario en lugar de un valor de color.

Además, actúan como puente entre la función de un elemento y el color elegido para esa función en particular. Por ejemplo, puedes tener tokens de color principal, secundario, de superficie, de fondo y de botón, y también tokens de título, cuerpo y texto con diferentes énfasis. El cielo es el límite por aquí.

Flutter ofrece algunas opciones con respecto a los tokens. Para los colores, permite crear un conjunto de colores o usarlos de materiales predeterminados. En cuanto a la tipografía, permite definir sus tamaños, colores, fuentes, etc.

Un ejemplo rápido de colores y textos con funciones:

//feedback colors
  static const success = Color(0xFF59C559);
  static const warning = Color(0xFFEFCC43);
  static const error = Color(0xFFE0493E);

// text styles
static final TextStyle captionText = TextStyle(
		fontFamily: 'open_sans',
    fontSize: 12,
    fontWeight: FontWeight.w400,
    height: 1.5,
    leadingDistribution: TextLeadingDistribution.even,
  );

  static final TextStyle captionTextBold = captionText.copyWith(
    fontWeight: FontWeight.w700,
  );

  static final TextStyle subtitleText = TextStyle(
		fontFamily: 'open_sans',
    fontSize: 16,
    fontWeight: FontWeight.w600,
    height: 1.5,
    leadingDistribution: TextLeadingDistribution.even,
  );

  static final TextStyle titleTextLarge = TextStyle(
		fontFamily: 'open_sans',
    fontSize: 32,
    fontWeight: FontWeight.w700,
    height: 1.375,
    leadingDistribution: TextLeadingDistribution.even,
  );

  static final TextStyle titleTextMedium = TextStyle(
		fontFamily: 'open_sans',
    fontSize: 22,
    fontWeight: FontWeight.w700,
    height: 1.363,
    leadingDistribution: TextLeadingDistribution.even,
  );

  static final TextStyle titleTextSmall = TextStyle(
		fontFamily: 'open_sans',
    fontSize: 14,
    fontWeight: FontWeight.w700,
    height: 1.571,
    leadingDistribution: TextLeadingDistribution.even,
  );

Tokens de color

Los colores en Flutter se pueden definir de diferentes maneras, según el tipo de colores que tú o tu equipo de diseño usarán al crear las webs de la aplicación.

Algunos ejemplos de tokens de color que Flutter entiende:

Hexadecimal:
	8 dígitos - 0xFF42A5F5
	6 dígitos - 0x42A5F5

ARGB (alpha ou transparência, vermelho, verde, azul):
	com Hex - (0xFF, 0x42, 0xA5, 0xF5)
	com notação RGB - (255, 66, 165, 245)

RGBO (vermelho, verde, azul, opacidade): (66, 165, 245, 1.0)
	// note que a opacidade, diferente do alpha do ARGB, varia de 0.0 a 1.0

La clase Color


Flutter tiene una clase dedicada a los colores llamada (¡mira eso!) Color. Su documentación está en este enlace: Flutter Color.

Esta clase es cómo, en el fondo, Flutter entiende todos los colores que puedes usar en tu aplicación. Está formado por un valor de color inmutable de 32 bits, en formato ARGB. Puedes construir colores con él de algunos constructores, dependiendo de los tokens que tengas:

Color c = const Color(0xFF42A5F5); - Hexadecimal
Color c = const Color.fromARGB(0xFF, 0x42, 0xA5, 0xF5); - ARGB Hex
Color c = const Color.fromARGB(255, 66, 165, 245); - ARGB
Color c = const Color.fromRGBO(66, 165, 245, 1.0); - RGBO

Un detalle importante es que, al utilizar valores en hexadecimal, no es posible elegir colocar, el inicio, el valor alfa o la transparencia.

Color c1 = const Color(0x42A5F5); // cor totalmente transparente (invisível)
Color c2 = const Color(0xFF42A5F5); // cor totalmente opaca (visível)

Bonus: ¿quieres crear un nuevo color basado en uno existente? Puedes hacerlo con los siguientes métodos:

Color baseColor = const Color.fromARGB(255, 66, 165, 245);

Color transparentColor =  baseColor.withAlpha(0);
Color redderColor = baseColor.withRed(160);
Color greenerColor = baseColor.withGreen(200);
Color lessBlueColor = baseColor.withBlue(50);
Color baseColor50 = baseColor.withOpacity(0.5);

El ejemplo anterior ilustra:

Si deseas que el código ejecute tus propias pruebas, aquí está:

// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Color demonstration'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const Color baseColor = Color.fromARGB(255, 66, 165, 245);

  Color transparentColor = baseColor.withAlpha(0);
  Color redderColor = baseColor.withRed(160);
  Color greenerColor = baseColor.withGreen(200);
  Color lessBlueColor = baseColor.withBlue(50);
  Color baseColor50 = baseColor.withOpacity(0.5);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                child: const Text('normal background'),
              ),
            ),
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                color: baseColor,
                child: Text('baseColor: $baseColor'),
              ),
            ),
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                color: baseColor50,
                child: Text('baseColor50: $baseColor50'),
              ),
            ),
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                color: transparentColor,
                child: Text('transparentColor: $transparentColor'),
              ),
            ),
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                color: redderColor,
                child: Text('redderColor: $redderColor'),
              ),
            ),
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                color: greenerColor,
                child: Text('greenerColor: $greenerColor'),
              ),
            ),
            Expanded(
              child: Container(
                width: double.maxFinite,
                alignment: Alignment.center,
                color: lessBlueColor,
                child: Text('lessBlueColor: $lessBlueColor'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

La clase Colors


Flutter tiene una clase más dedicada a los colores: Colors. Su documentación está en este enlace: Flutter Colors.

Colors ya es una implementación de Material, con colores predefinidos con diferentes tonalidades. Estos tonos ya están diseñados para cumplir una función específica, facilitando el uso de Material. Podemos definir la clase Colors como una paleta de colores a partir de un color principal.

Algunos detalles de implementación importantes para señalar con respecto a esta clase:

// Para selecionar uma cor específica dentro de uma paleta de cores
Color specificSwatch = Colors.amber[400]! //seleciona uma cor mais clara que a amber normal

// Cada paleta de cores tem uma cor constante e pode ser usada diretamente
Container(
	color: Colors.amber, // isso é a mesma coisa que utilizar Colors.amber[500] ou Colors.amber.shade500
);

La mayoría de los colores de Colors tienen un rango entre 100 y 900 dentro de la paleta. Cuanto menor sea el número, el color se vuelve más claro, y cuanto mayor sea el número, el color se vuelve más oscuro.

Los colores de acento (o acentuación) también se derivan del color principal, obedeciendo las mismas reglas de implementación.

// Para selecionar uma cor específica dentro de uma cor de acentuação
Color specificSwatch = Colors.amberAccent[400]! //seleciona uma cor mais escura que a amberAccent

// A cor de acentuação também tem uma cor padrão constante
Container(
	color: Colors.amberAccent,
);

La clase Colors también tiene variantes en blanco y negro.

Estos colores se identifican según su transparencia. Cuanto más bajo es el número, más transparente es el color y, por lo tanto, más difícil de ver. Se recomienda evitar los colores más transparentes a menos que tengan un uso específico y sutil.

Finalmente, también tenemos el “color” transparente, con el constructor:

Color transparent = Colors.transparent;

No podremos mostrarlo porque, como habrás adivinado, ¡es transparente! ;)


Tokens de tipografía


La tipografía es el arte y la técnica de organizar los textos para que el lenguaje escrito sea legible, comprensible y atractivo dondequiera que se muestre. Con el uso de la tipografía logramos algunos objetivos realmente geniales, como el reconocimiento de la marca por parte del usuario (Brand Recognition).

Cuando hablamos de tokens de tipografía, tenemos varias opciones posibles: la fuente, el tamaño de la fuente, el peso de la fuente (en los términos más simples, su "grosor"), el espacio entre letras y entre líneas, el color de el texto.

En Flutter, las fuentes predeterminadas son:

Podemos cambiar los parámetros de estas fuentes en Flutter usando una clase llamada TextStyle. ¿Vamos allá?

La clase TextStyle


La clase TextStyle es una clase de estilo inmutable que describe cómo Flutter debe formatear y pintar el texto.

const Text(
  'Este texto deveria estar em itálico',
  style: TextStyle(fontStyle: FontStyle.italic),
);
const Text(
  'Este texto deveria estar em negrito',
  style: TextStyle(fontWeight: FontWeight.bold),
);

Repasemos cada uno de los atributos más utilizados de esta clase para una mejor comprensión.

Color? backgroundColor


El atributo backGroundColor define el color de fondo del texto que tiene este estilo a partir de un color. Por ejemplo:

Text(
	'normal background com baseColor de background',
	style: TextStyle(
	  backgroundColor: baseColor,
	),
);


double? fontSize


El atributo fontSize hace referencia al tamaño del texto, en logical pixels. Si quieres saber más sobre los píxeles lógicos, aquí hay un enlace a la documentación: devicePixelRatio.

double? height


El atributo height se refiere al espacio vertical que ocupará la fuente, según el tamaño del texto (fontSize). En otras palabras, se compara con la altura de línea en CSS. Es importante tener en cuenta que este valor multiplicará el fontSize, por lo que si usamos height = 3.0 tendremos una altura de línea 3 veces mayor que el tamaño de la fuente.

Si no se especifica height, se utilizará la altura de línea predeterminada de la fuente. Directamente de la documentación de Flutter, veamos algunos ejemplos:

Fuente: Flutter API Doc


FontWeight? fontWeight


El atributo fontWeight se refiere al peso de la fuente, es decir, el grosor de la fuente cuando se representa. Se puede usar para poner en negrita una fuente, por ejemplo.

Un detalle importante aquí es recordar siempre asegurarse de tener la fuente con el peso de fuente especificado disponible en tu aplicación. Si la fuente no está disponible, el texto no se representará con el peso especificado, sino con la fuente predeterminada.

Este atributo se puede especificar desde FontWeight.w100 hasta FontWeight.w900.

## exemplos de import de fontes com espessuras diferentes:

flutter:	
	fonts:
    - family: Work Sans
      fonts:
        - asset: fonts/WorkSans-Black.ttf
        - asset: fonts/WorkSans-Bold.ttf
        - asset: fonts/WorkSans-Medium.ttf
        - asset: fonts/WorkSans-Regular.ttf
        - asset: fonts/WorkSans-SemiBold.ttf
		- family: Raleway
      fonts:
        - asset: fonts/Raleway-Regular.ttf
        - asset: fonts/Raleway-Medium.ttf
          weight: 500 ## peso especificado
        - asset: assets/fonts/Raleway-SemiBold.ttf
          weight: 600

FontStyle? fontStyle

El atributo de estilo de fuente definirá si tenemos una fuente con estilo normal o cursiva. Pudimos definir este atributo usando la clase FontStyle.

String? fontFamily


El atributo fontFamily define qué fuente se utilizará en este estilo de texto. Una aplicación puede ser de varias fuentes diferentes para diferentes contextos. Es importante asegurarse de que la fuente se importe correctamente al pubspec.yaml de su proyecto, como se muestra arriba.

List<String>? fontFamilyFallback


El atributo fontFamilyFallback se utilizará si la fontFamily especificada no admite un carácter específico. Por ejemplo: si su aplicación necesita textos tanto en inglés como en japonés, es más seguro que siempre tenga el fallback de una fuente que admita caracteres japoneses o kanjis.

También es interesante pensar en fuentes para emojis, si desea que los emojis se representen de la misma manera en todas las plataformas.

const TextStyle(
  fontFamily: 'Roboto',
  fontFamilyFallback: <String>[
    'Noto Sans CJK SC',
		'Noto Sans Japanese',
    'Noto Color Emoji',
  ],
)

double? letterSpacing


El atributo letterSpacing es el espacio entre caracteres, en logical pixels.

double? wordSpacing


El atributo wordSpacing es el espacio entre las palabras, en logical pixels.

TextDecoration? decoration


El atributo decoration se refiere a la decoración del texto, como subrayado, tachado, etc. Podemos establecer este atributo con la clase TextDecoration.

Container(
  width: double.maxFinite,
  padding: const EdgeInsets.all(16),
  alignment: Alignment.center,
  child: const Text(
    'aqui os exemplos de decoration',
    style: TextStyle(
			// TextDecoration.underline, TextDecoration.overline, 
			// TextDecoration.lineThrough ou uma combinação entre eles 
      decoration: TextDecoration.underline,
    ),
  ),
),

Color? decorationColor


El atributo decorationColor hace referencia al color de la decoration. Aquí puede pasar cualquier color como parámetro y el subrayado, el tachado o la línea superior tendrán ese color.

TextDecorationStyle? decorationStyle


El atributo decorationStyle permite una mayor personalización de la decoration. Puede ser sólido, discontinuo, punteado, ondulado o incluso doble.

double? decorationThickness


El atributo decorationThickness configura el grosor de la decoration en logical pixels.

TextOverflow? overflow


El atributo de overflow te permite personalizar cómo Flutter maneja el ajuste de texto cuando no cabe en la pantalla dentro del número permitido de líneas. Estas son las posibilidades disponibles:

Código fuente


No podría dejarte sin un código fuente genial para que puedas jugar y descubrir aún más cosas sobre estas implementaciones, ¿verdad? El enlace está aquí para ti: Flutter TextStyle Demo :).


¡Listo!


Vaya, quién diría que hablar de tokens de diseño sería demasiado, ¿verdad? Pero ahora puedes considerarte capacitado/a para personalizar tus tokens de diseño de una manera más profunda.

Espero haberte ayudado a incorporar algunos conceptos importantes de colores, textos y tokens en general. Dime lo que piensas y, si tienes alguna pregunta o sugerencia, búscame en LinkedIn o envíame un e-mail, ¿de acuerdo?

¡Gracias!

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.