Flutter: widgets personalizados

Creamos widgets personalizados cuando queremos una apariencia personalizada para nuestra aplicación, y sabemos que habrá una repetición de un widget en particular. Podemos crear el widget personalizado en un nuevo archivo dart con todos los códigos y definiendo los parámetros que necesitamos en el constructor.

Para obtener más información sobre cómo dividir widgets, puede visitar el artículo sobre Flutter: dividir la aplicación en widgets

Aquí discutiremos un ejemplo de cómo crear una aplicación simple aplicando propiedades personalizadas a los widgets y separándolos de sus propias propiedades. Haremos una aplicación de calculadora de IMC que tome la altura y el peso para calcular el IMC de una persona. Para mostrar cómo usar widgets personalizados, también hemos definido algunas cosas más en la pantalla.

Comencemos limpiando el archivo main.dart como:

Dart

import 'package:custom_widget_demo/home.dart';
import 'package:flutter/material.dart';
  
void main() {
  runApp(MyApp());
}
  
class MyApp extends StatelessWidget {
    
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'GFG Custom Widget Demo',
      theme: ThemeData.dark(),
      home: Home(),
    );
  }
}

Como puede ver, hemos definido la pantalla de inicio para mostrar todos los componentes en la pantalla. Ahora que hemos limpiado el archivo principal, crearemos un directorio de widgets y agregaremos tres archivos, a saber, custom_button.dart, custom_column.dart y custom_container.dart a este directorio. Escribiremos códigos para nuestros widgets personalizados en estos archivos.

Comenzando con el archivo custom_container.dart , escribiremos el siguiente código:

Dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
  
class CustomContainer extends StatelessWidget {
  CustomContainer(
      {@required this.child, this.height, this.width, this.onTap, this.color});
  final Function onTap;
  final Widget child;
  final double height;
  final double width;
  final Color color;
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: height,
        width: width,
        padding: EdgeInsets.all(12),
        decoration: BoxDecoration(
            color: color, borderRadius: BorderRadius.all(Radius.circular(8))),
        child: child,
      ),
    );
  }
}

En este widget personalizado, hemos creado un widget Stateless CustomContainer que es básicamente un contenedor con una esquina redondeada y una propiedad onTap que hemos implementado usando el widget GestureDetector . Hemos definido las propiedades de los widgets como el onTap que acepta una función, una propiedad secundaria que acepta un Widget, una propiedad de alto, ancho y, finalmente, un color. Veremos el uso de esto en el Home Widget que definiremos más adelante.

Pasando a nuestro próximo widget personalizado que definimos en el archivo custom_button.dart que es simplemente un widget CustomButton que definimos de la siguiente manera:

Dart

import 'package:flutter/material.dart';
  
class CustomButton extends StatelessWidget {
  CustomButton({this.onTap, this.color = Colors.white30, this.icon});
  final Function onTap;
  final Color color;
  final IconData icon;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: 50,
        width: 50,
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(30), color: color),
        child: Icon(icon),
      ),
    );
  }
}

Hemos definido simplemente un widget sin estado que actúa como un botón simple que usa un GestureDetector para detectar las funciones y también acepta un icono como elemento secundario que se mostrará dentro del botón. Es un botón redondeado con una altura y anchura fijas. Podemos alterar el color si lo necesitamos, pero ya tiene un color personalizado de blanco translúcido. 

Llegando a nuestro último widget que es simplemente una CustomColumn que definimos en el archivo custom_column.dart como:

Dart

import 'package:flutter/material.dart';
  
class CustomColumn extends StatelessWidget {
  CustomColumn({this.text, this.child});
  final String text;
  final Widget child;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          text,
          style: TextStyle(fontSize: 18),
        ),
        child
      ],
    );
  }
}

Este widget acepta un texto y un elemento secundario como sus propiedades que se muestran en la columna.

Ahora que todos los componentes están listos, vamos a escribir el código que mostrará todo nuestro contenido en la pantalla. Todo el código a partir de ahora es parte del archivo home.dart que definimos en la biblioteca .

Dart

import 'package:custom_widget_demo/widgets/custom_button.dart';
import 'package:custom_widget_demo/widgets/custom_column.dart';
import 'package:custom_widget_demo/widgets/custom_container.dart';
import 'package:flutter/material.dart';
import 'dart:math';
  
enum g { m, f }

Importamos todos nuestros widgets personalizados junto con material y archivos math.dart . Además de estos, también creamos una enumeración para el género g.

Dart

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}
  
class _HomeState extends State<Home> {
  final activeColor = Colors.white30;
  final inactiveColor = Colors.white12;
  g isSelected;
  int height = 160;
  int weight = 60;
  int age = 25;
  String bmi = '';

El widget Inicio es un widget con estado, y tenemos todas las propiedades como activeColor , inactiveColor , isSelected para la selección de género, altura, peso e hijo para el cálculo del IMC.

Dart

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('GFG Custom Widget'),
    ),
    body: SafeArea(
      child: Container(
        padding: EdgeInsets.all(12),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: CustomContainer(
                    color: isSelected == g.m ? activeColor : inactiveColor,
                    onTap: () {
                      setState(() {
                        isSelected = g.m;
                      });
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 20.0),
                      child: Text(
                        'FEMALE',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 18),
                      ),
                    ),
                  ),
                ),
                SizedBox(
                  width: 10,
                ),
                Expanded(
                  child: CustomContainer(
                    color: isSelected == g.f ? activeColor : inactiveColor,
                    onTap: () {
                      setState(() {
                        isSelected = g.f;
                      });
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 20.0),
                      child: Text(
                        'MALE',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 18),
                      ),
                    ),
                  ),
                ),
              ],
            ),

Para la primera parte, hemos definido un Scaffold bajo el cual hemos definido una AppBar y luego, como niño, hemos definido SafeArea . Ahora, llegando a los componentes, hemos definido una Columna que contiene todos los componentes de la pantalla. El primer elemento secundario de la columna es un widget de fila que contiene dos widgets CustomContainer con una función onTap para seleccionar el género y cambiar el color del contenedor mientras lo hacemos. 

Dart

SizedBox(
        height: 10,
      ),
      CustomContainer(
        color: inactiveColor,
        height: 100,
        child: CustomColumn(
          text: 'HEIGHT $height cm',
          child: SliderTheme(
            data: SliderTheme.of(context).copyWith(
              activeTrackColor: Colors.white,
              thumbColor: Colors.green,
              overlayColor: Color(0x2900ff00),
              thumbShape:
                  RoundSliderThumbShape(enabledThumbRadius: 15.0),
              overlayShape:
                  RoundSliderOverlayShape(overlayRadius: 25.0),
            ),
            child: Slider(
              value: height.toDouble(),
              min: 120.0,
              max: 220.0,
              onChanged: (double newValue) {
                setState(() {
                  height = newValue.floor();
                });
              },
            ),
          ),
        ),
      ),

Después de dar un poco de espacio, nuevamente hemos definido un CustomContainer con el color inactivo con una CustomColumn que acepta el texto como Altura con alturas que cambian dinámicamente con la ayuda del control deslizante que definimos. Hemos proporcionado propiedades personalizadas al control deslizante para que se vea y se sienta de acuerdo con nuestra aplicación.

Dart

SizedBox(
        height: 10,
      ),
      Row(
        children: [
          Expanded(
            child: CustomContainer(
              color: inactiveColor,
              child: CustomColumn(
                text: 'WEIGHT $weight',
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      CustomButton(
                        onTap: () {
                          setState(() {
                            weight = weight - 1;
                          });
                        },
                        icon: Icons.arrow_downward,
                      ),
                      SizedBox(
                        width: 10,
                      ),
                      CustomButton(
                        onTap: () {
                          setState(() {
                            weight = weight + 1;
                          });
                        },
                        icon: Icons.arrow_upward,
                      )
                    ],
                  ),
                ),
              ),
            ),
          ),
          SizedBox(
            width: 10,
          ),
          Expanded(
            child: CustomContainer(
              color: inactiveColor,
              child: CustomColumn(
                text: 'AGE $age',
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      CustomButton(
                        onTap: () {
                          setState(() {
                            age = age - 1;
                          });
                        },
                        icon: Icons.arrow_downward,
                      ),
                      SizedBox(
                        width: 10,
                      ),
                      CustomButton(
                        onTap: () {
                          setState(() {
                            age = age + 1;
                          });
                        },
                        icon: Icons.arrow_upward,
                      )
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),

Nuevamente, después de dar un poco de espacio, definimos una fila con dos CustomContainer que aceptan una CustomColumn con el texto Peso y Edad. Ambos contenedores tienen dos botones en una fila como elemento secundario de CustomColumn que definimos. La funcionalidad de estos botones es aumentar o disminuir el valor de peso y edad.

Dart

SizedBox(
        height: 10,
      ),
      Row(
        children: [
          Expanded(
            child: CustomContainer(
              onTap: () {
                setState(() {
                  bmi = '';
                });
              },
              width: double.infinity,
              child: Text(
                'CLEAR',
                style: TextStyle(
                  fontSize: 18,
                ),
                textAlign: TextAlign.center,
              ),
              color: activeColor,
            ),
          ),
          SizedBox(
            width: 10,
          ),
          Expanded(
            child: CustomContainer(
              onTap: () {
                double _bmi = weight / pow(height / 100, 2);
 
                setState(() {
                  bmi = _bmi.toStringAsFixed(1);
                });
              },
              width: double.infinity,
              child: Text(
                'GET BMI',
                style: TextStyle(
                  fontSize: 18,
                ),
                textAlign: TextAlign.center,
              ),
              color: Colors.green,
            ),
          ),
        ],
      ),

Aquí hemos definido dos botones con la ayuda de nuestro CustomContainer . El primero se usa para borrar la salida que se muestra y el otro muestra la salida en la pantalla.

Dart

SizedBox(
                height: 10,
              ),
              Expanded(
                child: CustomContainer(
                  width: double.infinity,
                  child: Column(
                    children: [
                      SizedBox(
                        height: 20,
                      ),
                      Text(
                        'YOUR BMI IS',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      SizedBox(
                        height: 20,
                      ),
                      Text(
                        bmi,
                        style: TextStyle(
                            fontSize: 100, fontWeight: FontWeight.bold),
                      )
                    ],
                  ),
                  color: inactiveColor,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

El último componente de la aplicación también es un contenedor personalizado que se utiliza para mostrar el IMC calculado en la pantalla. Esto completa nuestra aplicación. Ahora puede ejecutar la aplicación en su dispositivo.

Producción:

Publicación traducida automáticamente

Artículo escrito por muazzamfaraaz y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *