PIXEL FACEBOOK

Crear formularios Responsive en Flutter

Crear formularios Responsive en Flutter

En casi todas las aplicaciones que desarrolle, tarde o temprano será necesario capturar la entrada del usuario. Afortunadamente, capturar la entrada de texto es bastante sencillo en Flutter. Sin embargo, a medida que se agregan más campos y tipos de entrada a un formulario, capturar esta información rápidamente se vuelve más complejo.

Normalmente estos campos de entrada, ya sean campos de texto, campos de fecha o cualquier otro tipo de entrada, se denominan «controles». La validación también puede convertirse en un problema, ya que incluso la validación simple de algunos campos puede requerir la escritura de validadores personalizados extensos.

En este artículo, crearemos un formulario de registro con validación de entrada y campos que cambian según el valor de otros campos. Primero lograremos esto sin usar formularios reactivos y luego volveremos a implementar el mismo formulario usando formularios reactivos para comprender los beneficios de los formularios reactivos en Flutter.

Lo que cubriremos:

Vista previa del proyecto Flutter Responsive Forms

La aplicación que vamos a crear es una aplicación de registro de mascotas para un «hotel de mascotas», un lugar donde las personas pueden dejar a sus mascotas cuando se van de vacaciones.

Para que esta aplicación funcione, los usuarios deben proporcionar detalles como su nombre y número de teléfono, el tipo de mascota que tienen y los gustos y aversiones de su mascota. El resultado final se verá así:

Se muestra al usuario ingresando a la aplicación Pet Hotel e ingresando información en el formulario de respuesta de Flutter

Este formulario tiene algunos requisitos.

Primero, las tres preguntas de seguimiento deben cambiar según el tipo de animal que seleccione el usuario.

A continuación, se requieren las respuestas a estas tres preguntas, por lo que debemos agregar alguna lógica de validación de formularios de Flutter para asegurarnos de que se cumplan.

Finalmente, el número de teléfono solo debe contener números, por lo que si contiene valores no numéricos, el formulario debe rechazar esa entrada y notificar al usuario.

Haz el formulario en Flutter sin formularios responsivos

En este primer enfoque, estamos creando manualmente los formularios nosotros mismos y también nos gustaría capturar las entradas de texto que se encuentran en esos campos individuales.

Por esta razón, somos responsables de crear TextControllers individuales que podamos asociar con los widgets TextFormField. También nos encargamos de crear una variable que albergará al animal seleccionado.

Vamos a crear estas variables ahora:

final _formKey = GlobalKey<FormState>();
PetType? _petType;
final firstName = TextEditingController();
final lastName = TextEditingController();
final questionResponses = List.generate(3, (index) => TextEditingController());

Para escribir texto en estos campos, crearemos los widgets TextFormField y los vincularemos a los controladores apropiados:

TextFormField(
  decoration: InputDecoration(hintText: 'First Name'),
  controller: firstName,
),
TextFormField(
  decoration: InputDecoration(hintText: 'Last Name'),
  controller: lastName,
),

El campo de entrada del número de teléfono es un poco diferente, ya que necesitamos validar que contiene un número de teléfono válido y advertir al usuario cuando se detecta una entrada no válida:

TextFormField(
  decoration: InputDecoration(hintText: 'Phone number'),
  autovalidateMode: AutovalidateMode.always,
  validator: (val) {
    if (val == null || val == "") {
      return 'Please enter a phone number';
    }
    if (int.tryParse(val) == null) {
      return 'Only enter numbers in the phone number field';
    }
    return null;
  },
),

A continuación, especificamos el selector de animales. Este es un RadioListTile que le permite al usuario seleccionar el tipo de mascota que traerá: gato, perro o equidna.

Más artículos interesantes de LogRocket:

Cuando el usuario selecciona un tipo de mascota, también queremos revisar las respuestas anteriores dadas a esas preguntas y borrarlas para que solo se seleccione una opción a la vez.

RadioListTile<PetType>(
  value: PetType.cat,
  groupValue: _petType,
  onChanged: (val) => setState(() {
    for (final controller in questionResponses) {
      controller.clear();
    }
    _petType = val;
  }),
  title: Text('Cat'),
),

Por último, queremos modificar las preguntas que hacemos en función del tipo de mascota seleccionada.

Podemos lograr esto mediante el uso de un constructor, que actualizará el árbol de widgets en función del valor de una variable determinada. Así, si el tipo de animal seleccionado es «Gato», el formulario mostrará las preguntas para este tipo de animal, y lo mismo para los animales de tipo Perro o Equidna.

Builder(
  builder: (context) {
    switch (_petType) {
      case PetType.cat:
        return Column(
          children: [
            Text("Aw, it's a cat!"),
            PetQuestionField(question: 'Can we pat the cat?', controller: questionResponses[0]),
            PetQuestionField(question: 'Can we put a little outfit on it?', controller: questionResponses[1]),
            PetQuestionField(question: 'Does it like to jump in boxes?', controller: questionResponses[2]),
          ],
        );

      case PetType.dog:
        return Column(
          children: [
            Text("Yay, a puppy! What's its details?"),
            PetQuestionField(question: 'Can we wash your dog?', controller: questionResponses[0]),
            PetQuestionField(question: 'What is your dog\'s favourite treat?', controller: questionResponses[1]),
            PetQuestionField(question: 'Is your dog okay with other dog\'s?', controller: questionResponses[2]),
          ],
        );

      case PetType.echidna:
        return Column(
          children: [
            Text("It's a small spiky boi. Can you fill us in on some of the details?"),
            PetQuestionField(question: 'How spikey is the echidna?', controller: questionResponses[0]),
            PetQuestionField(question: 'Can we read the echidna a story?', controller: questionResponses[1]),
            PetQuestionField(question: 'Does it like leafy greens?', controller: questionResponses[2]),
          ],
        );
      case null:
        {
          return Text('Please choose your pet type from above');
        }
    }
  },
),

Con los controles de formulario individuales creados, es hora de crear un botón para que el usuario registre su mascota. Este botón solo debe permitir que el usuario continúe si las entradas proporcionadas son válidas y debe solicitar al usuario que corrija las entradas que no se pudieron validar.

ElevatedButton(
    onPressed: () {
      // Form is valid if the form controls are reporting that 
      // they are valid, and a pet type has been specified.
      final valid = (_formKey.currentState?.validate() ?? false) && _petType != null;
      if (!valid) {
      // If it's not valid, prompt the user to fix the form
        showDialog(
            context: context,
            builder: (context) => SimpleDialog(
                  contentPadding: EdgeInsets.all(20),
                  title: Text('Please check the form'),
                  children: [Text('Some details are missing or incorrect. Please check the details and try again.')],
                ));
      } else {
      // If it is valid, show the received values
        showDialog(
          context: context,
          builder: (context) => SimpleDialog(
            contentPadding: EdgeInsets.all(20),
            title: Text("All done!"),
            children: [
              Text(
                "Thanks for all the details! We're going to check your pet in with the following details.",
                style: Theme.of(context).textTheme.caption,
              ),
              Card(
                child: Column(
                  children: [
                    Text('First name: ${firstName.text}'),
                    Text('Last name: ${lastName.text}\r\n'),
                    Text('Pet type: ${_petType}'),
                    Text('Response 1: ${questionResponses[0].text}'),
                    Text('Response 2: ${questionResponses[1].text}'),
                    Text('Response 3: ${questionResponses[2].text}'),
                  ],
                ),
              )
            ],
          ),
        );
      }
    },
    child: Text('REGISTER'))

Problemas con la creación de formularios manualmente en Flutter

Usar formularios en Flutter no es demasiado difícil, pero crear nuestros propios formularios a mano puede ser un poco laborioso. Veamos por qué este es el caso.

Primero, si desea poder obtener texto de un campo o borrar la entrada del campo, debe crear su propio TextEditingController para cada campo. Es fácil ver cómo podría terminar con varios de ellos, que tendría que recordar.

En segundo lugar, debe escribir su propia lógica de validación para cosas simples como verificar si un número es correcto.

Finalmente, este enfoque genera una gran cantidad de código repetitivo. Para uno o dos campos de texto, no es tan malo, pero es fácil ver cómo podría salir mal.

Dos opciones de paquetes de formularios responsive de Flutter a considerar

Si tuviéramos que emprender un viaje para encontrar un paquete que facilitara este proceso, y tuviéramos en mente «formularios reactivos», probablemente nos encontraríamos con el paquete reactive_formsFlutter bastante rápido. Y, sin embargo, este no es el paquete que usaría para crear formularios responsive en mi aplicación.

¿Porque no?

Bueno, la primera oración en pub.dev nos dice que Reactive Forms es «… un enfoque basado en modelos para manejar entradas y validaciones de formularios, fuertemente inspirado en las formas reactivas de Angular».

Por ello, podemos establecer que la mentalidad utilizada en el paquete reactive_forms será similar a la que encontramos en Angular.

Si ya estamos familiarizados con Angular, tal vez esa sea una razón más para usar reactive_forms. Pero si no conocemos Angular, estamos más interesados ​​en la forma más fácil de obtener capacidad de respuesta dentro de nuestros formularios.

En mi experiencia, encuentro que usar el paquete flutter_form_builder es una forma más fácil y extensible de crear formularios.

Por supuesto, lo animo a investigar ambos paquetes y elegir el que prefiera, ya que un paquete no es necesariamente «mejor» que el otro, pero representan dos formas diferentes de lograr un resultado similar.

Usando flutter_form_builder para crear formularios receptivos

Ahora usemos el paquete flutter_form_builder para construir nuestros formularios. Puede reducir la cantidad de código que tenemos que escribir, facilitar la comprensión del código que hemos escrito y también evitar que escribamos nuestra propia lógica de validación.

Primero, agregaremos una dependencia al paquete flutter_form_builder en nuestro archivo pubspec.yaml:

flutter_form_builder: ^7.4.0

Con esta configuración, reimplementemos nuestros formularios para usar flutter_form_builder.

Tendremos que agregar nombres para los campos que pretendemos usar en nuestro formulario. Deberíamos asignarles un nombre de variable lógica para nosotros, ya que necesitaremos vincular nuestro FormBuilderTextField a ellos más adelante.

final String FIRST_NAME = 'FirstName';
final String LAST_NAME = 'LastName';
final String PHONE_NUMBER = 'PhoneNumber';
final String PET_CHOICE = 'PetChoice';
final String QUESTION_ANSWER_1 = 'QuestionAnswer1';
final String QUESTION_ANSWER_2 = 'QuestionAnswer2';
final String QUESTION_ANSWER_3 = 'QuestionAnswer3';

También necesitamos especificar una GlobalKeypara almacenar los detalles que captura nuestro formulario.

final _fbKey = GlobalKey<FormBuilderState>();

El siguiente gran cambio es que, en lugar de que nuestro formulario esté envuelto en un formulario, lo envolveremos en un FormBuilder y especificaremos una clave para FormBuilder.

FormBuilder(
  key: _fbKey,
  child: Column(children: [...children widgets here])
)

Esto significa que FormBuilder almacenará valores de formulario en esta clave, para que podamos recuperarlos fácilmente más adelante.

Configuración de entradas de formulario básico

Normalmente, seríamos responsables de especificar manualmente qué TextEditingController usar, así como también configurar manualmente cosas como la validación. Pero con flutter_form_builder, esas dos cosas se vuelven triviales.

Para un campo de entrada de texto, especificamos el parámetro de nombre del campo y, si queremos etiquetar el campo, la decoración. También podemos simplemente elegir entre un conjunto existente de validadores en lugar de escribir el nuestro. Esto significa que nuestros campos de entrada de nombre y apellido se ven así:

FormBuilderTextField(
  name: FIRST_NAME,
  decoration: InputDecoration(labelText: 'First Name'),
  validator: FormBuilderValidators.required(),
),

Para nuestro campo de número de teléfono, en lugar de escribir nuestro propio validador, simplemente podemos aprovechar el validador FormBuilderValidators.numeric():

FormBuilderTextField(
  name: PHONE_NUMBER,
  validator: FormBuilderValidators.numeric(),
  decoration: InputDecoration(labelText: 'Phone number'),
  autovalidateMode: AutovalidateMode.always,
),

Configurar selector de tipo de animal

Ahora queremos darle al usuario una lista de opciones de tipos de animales para elegir seleccionando el botón de opción apropiado en nuestra aplicación Flutter. Podemos generar esta lista mediante programación a partir de nuestro conjunto de enumeraciones proporcionado.

Esto significa que si agregamos o eliminamos opciones de nuestra enumeración en nuestro programa, las opciones también cambiarán en nuestro formulario. Esto será más fácil que mantener manualmente la lista nosotros mismos.

FormBuilderRadioGroup<PetType>(
  onChanged: (val) {
    print(val);
    setState(() {
      _petType = val;
    });
  },
  name: PET_CHOICE,
  validator: FormBuilderValidators.required(),
  orientation: OptionsOrientation.vertical, // Lay out the options vertically
  options: [
    // Retrieve all options from the PetType enum and show them as options
    // Capitalize the first letters of the options as well
    ...PetType.values.map(
      (e) => FormBuilderFieldOption(
        value: e,
        child: Text(
          describeEnum(e).replaceFirst(
            describeEnum(e)[0],
            describeEnum(e)[0].toUpperCase(),
          ),
        ),
      ),
    ),
  ],
),

Configuración de las tres preguntas al final.

Nuestro método de construcción sigue siendo prácticamente el mismo para esta parte de nuestro formulario Flutter, con algunas diferencias clave: ahora usamos la clase FormBuilderTextField para nuestras entradas y las asignamos a la entrada adecuada en el formulario a través del parámetro de nombre.

case PetType.cat:
  return Column(
    children: [
      Text("Aw, it's a cat!"),
      FormBuilderTextField(
        name: QUESTION_ANSWER_1,
        decoration: InputDecoration(labelText: 'Can we pat the cat?'),
      ),
      FormBuilderTextField(
        name: QUESTION_ANSWER_2,
        decoration: InputDecoration(labelText: 'Can we put a little outfit on it?'),
      ),
      FormBuilderTextField(
        name: QUESTION_ANSWER_3,
        decoration: InputDecoration(labelText: 'Does it like to jump in boxes?'),
      ),
    ],
  );

Validación y recuperación de valores de formulario

Con nuestro formulario receptivo Flutter configurado, ahora tenemos dos cosas que hacer: validar que el formulario contiene datos utilizables y recuperar esos valores del formulario.

Afortunadamente, dado que hemos definido los requisitos de validación en cada campo, nuestra validación se vuelve bastante simple:

final valid = _fbKey.currentState?.saveAndValidate() ?? false;

El resultado de hacer esto es que si el estado actual de nuestro formulario no es nulo y actualmente se considera válido, es decir, todos los campos del formulario han pasado la validación, entonces el formulario se considera válido. Si currentState es nulo, o si el formulario no es válido, esta variable devolverá false en su lugar.

En el caso de un resultado positivo, los valores se mostrarán al usuario. Podemos acceder fácilmente a los valores en el formulario accediendo al objeto currentState en el objeto _fbKey.

showDialog(
  context: context,
  builder: (context) => SimpleDialog(
    contentPadding: EdgeInsets.all(20),
    title: Text("All done!"),
    children: [
      Text(
        "Thanks for all the details! We're going to check your pet in with the following details.",
        style: Theme.of(context).textTheme.caption,
      ),
      Card(
        child: Column(
          children: [
            // It's okay to use the ! operator with currentState, because we
            // already checked that it wasn't null when we did the form
            // validation
            Text('First name: ${_fbKey.currentState!.value[FIRST_NAME]}'),
            Text('Last name: ${_fbKey.currentState!.value[LAST_NAME]}'),
            Text('Number: ${_fbKey.currentState!.value[PHONE_NUMBER]}'),
            Text('Pet type: ${_fbKey.currentState!.value[PET_CHOICE]}'),
            Text('Response 1: ${_fbKey.currentState!.value[QUESTION_ANSWER_1]}'),
            Text('Response 2: ${_fbKey.currentState!.value[QUESTION_ANSWER_2]}'),
            Text('Response 3: ${_fbKey.currentState!.value[QUESTION_ANSWER_3]}'),
          ],
        ),
      )
    ],
  ),
);

Finalmente

Como podemos ver, usar flutter_form_builder para crear formularios responsive en Flutter puede generar muchas mejoras para nosotros como desarrolladores. Como siempre, puede buscar el código de este proyecto en Github para ver cómo puede usar flutter_form_builder en su proyecto.

También puede usar estos enlaces a continuación para comparar dos confirmaciones para ver exactamente cómo ha cambiado el proyecto:

Hay varios tipos de campos diferentes proporcionados por flutter_form_builder, por lo que siempre debería poder usar el tipo de campo que se adapte a sus necesidades.

¡Diviértete y disfruta construyendo estas formas!Fuente

Artículos Relacionados

Deja una respuesta

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

Jenny Watson

Hi! beautiful people. I`m an authtor of this blog. Read our post – stay with us

Categorías

Artículos Relacionados

¡Descubre Hostinger, Crea tu Web y Empieza a Generar Presencia Online!

¿Buscas un hosting confiable y asequible para tu proyecto web? Hostinger te ofrece planes flexibles y potentes que se adaptan a tus necesidades. Desde sitios web personales hasta tiendas online, su tecnología de vanguardia garantiza un rendimiento excepcional.