Botón gradiente reutilizable en Flutter

Clase 25 de 29Curso de Dart

Resumen

Crear componentes reutilizables es una de las prácticas más valiosas al trabajar con Flutter. En lugar de repetir código cada vez que necesitas un botón con estilos específicos, puedes construir tu propia clase que herede de Stateless Widget y aprovechar los constructores con parámetros nombrados para lograr un componente flexible y dinámico.

¿Cómo crear una clase personalizada que herede de Stateless Widget?

El punto de partida es declarar una clase llamada CustomGradientButton que extienda de StatelessWidget [0:18]. Al heredar de esta clase, Flutter exige implementar el método build, que es obligatorio en toda clase que herede de Stateless o Stateful. Este método es el encargado de describir la interfaz visual del componente.

Dentro de la clase se declaran las variables finales que representan las propiedades configurables del botón:

  • text: un widget de tipo Text para mostrar el contenido.
  • width y height: valores double para definir dimensiones.
  • gradientColors: una lista de tipo Color para los colores del gradiente.
  • initialPosition y finalPosition: de tipo Alignment, controlan la dirección del gradiente.
  • function: una función que se ejecutará al tocar el botón.
  • leadingIcon y finalIcon: widgets de tipo Icon opcionales.

Estas variables se marcan como final, lo que significa que deben inicializarse una sola vez, tal como se estudia en el concepto de final y const en Dart [1:12].

¿Por qué usar required en el constructor?

El IDE IntelliJ facilita la creación del constructor generando automáticamente los parámetros a partir de los campos final [2:05]. Aquí entra en juego la palabra clave required: se aplica a los parámetros que son indispensables para que el componente funcione correctamente, como width, height, text y gradientColors. Si no se envían, el compilador genera un error antes de ejecutar [2:20].

Los constructores con llaves {} producen parámetros nombrados, lo que resulta fundamental cuando tienes seis, siete u ocho propiedades. En lugar de recordar el orden exacto, cada argumento se identifica por su nombre al momento de usarlo [5:55].

¿Cómo construir un container con gradiente y sombra?

Dentro del método build, se retorna un Container con las propiedades width y height recibidas. La magia visual ocurre en el BoxDecoration, donde se define un LinearGradient [3:10]:

dart decoration: BoxDecoration( gradient: LinearGradient( colors: gradientColors, begin: initialPosition, end: finalPosition, ), boxShadow: [ BoxShadow( color: Colors.black, offset: Offset(2, -2), blurRadius: width * 0.1, spreadRadius: 1, ), ], borderRadius: BorderRadius.only( topRight: Radius.circular(height / 2), ), ),

El offset (2, -2) desplaza la sombra hacia arriba y a la derecha, rompiendo el patrón habitual de sombras inferiores [3:55]. El blurRadius se calcula como una décima parte del ancho, haciéndolo proporcional al tamaño del botón.

¿Cómo manejar widgets opcionales con container y offstage?

Para los íconos opcionales se aplica una técnica práctica: si el valor llega nulo, se reemplaza por un Container() vacío o un widget Offstage [4:48]. Ambos ocupan cero espacio en pantalla y no generan errores. El contenido se organiza con un Row que recibe la lista de widgets y utiliza MainAxisAlignment.spaceEvenly para distribuir el espacio uniformemente [7:20].

Para agregar la funcionalidad de toque, se envuelve todo en un widget InkWell que expone la propiedad onTap, donde se asigna la función recibida como parámetro [5:25].

¿Cómo se implementa el botón personalizado?

Al usarlo, los parámetros nombrados aparecen de forma clara [6:15]:

dart CustomGradientButton( text: Text("México"), width: 150, height: 40, gradientColors: [Colors.green, Colors.white, Colors.red], initialPosition: Alignment.centerLeft, finalPosition: Alignment.centerRight, function: () => print("Hola, desde Colombia"), leadingIcon: Icon(Icons.person), finalIcon: Icon(Icons.chat), )

Si necesitas un botón de un solo color, basta con enviar el mismo color dos veces en la lista, ya que LinearGradient requiere al menos dos valores [8:30]. Para redondear esquinas se utiliza BorderRadius.only con un valor dinámico basado en height / 2, evitando distorsiones si cambian las dimensiones [9:15].

Comparte en los comentarios qué variaciones lograste crear con este componente y qué otras propiedades le agregarías.