Diferencia entre revisiones de «Flutter Diálogos»
Ir a la navegación
Ir a la búsqueda
| Línea 448: | Línea 448: | ||
<br /> | <br /> | ||
| − | |||
Revisión del 17:48 22 sep 2021
Intruducción
- Los díalogos suelen ser ventanas modales (no se puede iteraccionar con la aplicación hasta que se cierre) en los que:
- Se informa de algo
- Se solicita información al usuario.
- Creamos una nueva página en la carpeta pages de nombre alert_pages.dart
- En todas las páginas haremos lo mismo:
- Importamos la librería de 'material' (recordar que existe un snippet que lo hace automático).
- Creamos una clase que derive de StatelessWidget (al hacerlo será necesario implementar el método build, ya os da un aviso el Android Studio) de nombre AlertPage.
- Dentro del método build, retornamos un widget Scaffold (recordar que existe un snippet que lo hace automático).
- En todos los tipos de diálogos vamos a tener que 'mostrarlo'. Para ello vamos a hacer uso de la clase showDialog a la que vamos a tener que:
- Enviar en forma de parámetro el buildcontext.
- Devolver el Widget que queremos que se visualice como 'contenido' del diálogo. Esto lo indicamos en el parámetro builder.
- barrierDismissible: Indicamos si queremos que se pueda cerrar el diálogo al pulsar fuera del mismo (por defecto es true).
- Al instanciar esta clase, se devuelve un Future. En dicho Future puede venir información sobre qué opción fue pulsada por parte del usuario (al cerrar al diálogo con el método pop de la clase Navigator, se puede enviar información de vuelta a quien llamó al showDialog).
AlertDialog
- Más información en https://api.flutter.dev/flutter/material/AlertDialog-class.html
- Este tipo de diálogos suelen ser empleados para informar al usuario de algo o para que escoga entre un número limitado de opciones (normalmente dos) la acción a seguir.
- Los parámetros más importantes son:
- title: Título de la alerta.
- content: Contenido de la alerta.
- actions: Lista de widget que van a conformar las 'acciones' que va a tener el alertdialog. Normalmente son botones.
- Para cerrar el diálogo podemos hacer uso del método pop de la clase Navigator.
- Veamos un ejemplo:
import 'package:flutter/material.dart'; class AlertPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Ejemplos de Alert'), ), body: Row ( mainAxisAlignment:MainAxisAlignment.center, children: [ TextButton( onPressed: () => _mostrarAlerta1(context).then((value) => print(value)), // El then no es obligatorio. Lo pondríamos si queremos recibir información de vuelta child: Text('Alerta 1',style: TextStyle(color: Colors.yellow),), style: ElevatedButton.styleFrom(primary: Colors.blue,shape: StadiumBorder()) ) ], ) ); } Future _mostrarAlerta1(BuildContext context){ // Si no vamos a 'devolver' información podríamos definir el método como void y no hacer el return del showDialog. return showDialog(context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Título del diálogo'), content: Column( mainAxisSize: MainAxisSize.min, // Para que la columna no ocupe todo el alto de la pantalla children: [ Text('Contenido del diálogo. Texto que queramos que aparezca para informar al usuario de algo'), FlutterLogo(size: 50,) ], ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancelar')), // Cerramos el diálogo TextButton(onPressed: (){ // Pondríamos el código que queramos Navigator.pop(context,'Valor de vuelta'); // Envío de vuelta a quien llamó la información que quiera. NO ES OBLIGATORIO }, child: Text('Aceptar')), ], );; }, ); } }
DatePicker / TimePicker
- Más información:
- Creamos una nueva página en la carpeta pages de nombre formulario_page.dart
- En todas las páginas haremos lo mismo:
- Importamos la librería de 'material' (recordar que existe un snippet que lo hace automático).
- Creamos una clase (FormularioPage) que derive de StatefulWidget. Se creará (si lo hacéis con un Snippet) una clase asociada que deriva de State.
- Modificamos la clase State y en su métod build hacemos que devuelva un Scaffold con una Appbar (hacerlo con un Snippet)
- Estos dos elementos gráficos muestran una ventana modal con controles gráficos para escojer una fecha / hora según el caso.
- Normalmente se muestra al usuario un TextField inhabilitado (con el parámetro readonly a true o el parámetro enableInteractiveSelection a false) y al pulsar sobre el mismo se muestra la ventana modal para que escoja la fecha / hora. Una vez escojida, se cierra la ventana modal y se muestra la fecha / hora en la caja de texto.
- El tipo de dato que vamos a recoger de la ventana modal del DatePickerDialog va a ser un objeto de la clase DateTime.
- Este devuelve la fecha-hora conjuntamente. Como para cada control vamos a querer mostrar sólo la fecha o sólo la hora, podemos hacer uso del paquete intl el cual nos permite extraer parte del DateTime (lo que nos interese) y guardarlo en forma de String. Para instalarlo debéis seguir los pasos indicados en la pestaña de instalación.
- El tipo de dato que vamos a recoger de la ventana modal del TimePickerDialog va a ser un TimeOfDay. Vamos a necesitar crear un DateTime con la hora-minuto recojida de este control y aplicar la función DateFormat del paquete anterior para mostrar sólo la hora y el minuto.
- En este caso, cuando volvemos de seleccionar una fecha - hora con la ventana modal, recojeremos el resultado de la selección, pero necesitamos modificar el contenido de las cajas de texto donde van a ir esos datos.
- Para hacerlo necesitamos:
- Crear un objeto de la clase TextEditingController (un objeto para cada caja que queramos modificar su contenido, en este ejemplo dos, uno para la hora y otro para la fecha.
- Asociar dicho controlador a cada una de las cajas de texto. Esto se consigue en el parámetro del constructor controller del TextField.
- Desde el método que nos devuelve el resultado de escojer la fecha / hora podemos modificar el contenido con la propiedad text del controlador.
- En el ejemplo aparecen dos cajas de texto que al pulsar encima de ellas, en una aparece la ventana modal para seleccionar una fecha y en el otro para seleccionar un hora-minuto.
import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class FormularioPage extends StatefulWidget { const FormularioPage({Key? key}) : super(key: key); @override _FormularioPageState createState() => _FormularioPageState(); } class _FormularioPageState extends State<FormularioPage> { String _nombre = ''; String _email = ''; String _password = ''; String _fechaEscojida = ''; String _horaEscojida = ''; List<String> _optTipoPaciente = ['Crónico', 'Terminal', 'Observación']; String _valorEscojidoTipoPaciente = 'Crónico'; bool _checkEnfermedadTerminal = false; bool _switchHospitalizado = false; String _radioAtenciomInicial=''; Set<String> _valoresRadio = {'Cabecera', 'Uci', 'Urgencias'}; TextEditingController _inputFieldDateController = new TextEditingController(); TextEditingController _inputFieldTimeController = new TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Formulario'), ), body: ListView( padding: EdgeInsets.symmetric(horizontal: 5.0,vertical: 10.0), children: [ _crearInput(), // TextField con múltiples opciones de decoración Divider(), _crearEmail(), Divider(), _crearPassword(), Divider(), _crearDropDown(), Divider(), _crearCheckBox(), Divider(), _crearSwitch(), Divider(), _crearRadio(), Divider(), _DatePickerTimePicker(), Divider(), Divider(), _visualizarDatos(), ], )); } Widget _visualizarDatos(){ return Column( children: [ Text('Nombre del paciente: $_nombre'), Text('Email del paciente: $_email'), Text('Password: $_password'), Text('Tipo de paciente: $_valorEscojidoTipoPaciente'), Text('Enfermedad terminal: $_checkEnfermedadTerminal'), Text('Hospitalizado: $_switchHospitalizado'), Text('Atendido inicialmente en: $_radioAtenciomInicial'), Text('Fecha escojida: $_fechaEscojida'), Text('Hora escojida: $_horaEscojida'), ], ); } Widget _DatePickerTimePicker(){ return Column( children: [ TextField( controller: _inputFieldDateController, decoration: InputDecoration( hintText: 'Fecha de ingreso', labelText: 'Fecha de ingreso', suffixIcon: Icon(Icons.date_range), border: OutlineInputBorder( borderRadius: BorderRadius.circular(15.0), ) ), readOnly: true, onTap: () => _mostrarDatePicker(), ), SizedBox(height: 10.0,), TextField( controller: _inputFieldTimeController, decoration: InputDecoration( hintText: 'Hora de ingreso', labelText: 'Hora de ingreso', suffixIcon: Icon(Icons.timelapse), border: OutlineInputBorder( borderRadius: BorderRadius.circular(15.0), ) ), readOnly: true, onTap: () => _mostrarTimePicker(), ), ], ); } void _mostrarDatePicker(){ // Podríamos poner async en la definición y llamar con un await para que devuelva un DateTime showDatePicker(context: context, initialDate: DateTime.now(), firstDate: DateTime(1995),lastDate: DateTime(2024), helpText: 'Fecha de ingreso') .then((value){ setState(() { // Necesitamos redibujar el texto para que muestre la fecha if (value !=null) { _fechaEscojida = DateFormat('dd-MM-yyyy').format(value); _inputFieldDateController.text = _fechaEscojida; } }); }); } void _mostrarTimePicker(){ // Podríamos poner async en la definición y llamar con un await para que devuelva un DateTime showTimePicker(context: context, initialTime: TimeOfDay(hour: DateTime.now().hour, minute: DateTime.now().minute), helpText: 'Hora de ingreso') .then((value){ setState(() { // Necesitamos redibujar el texto para que muestre la hora if (value !=null) { // Convertimos el formato TimeOfDay a HH:mm Podríamos crear una función para reutilizar siempre que sea necesario final ahora = DateTime.now(); final dt = new DateTime(ahora.year,ahora.month,ahora.day,value.hour,value.minute); final formato = DateFormat.jm(); // Formato HH:mm AM _horaEscojida = formato.format(dt); _inputFieldTimeController.text = _horaEscojida; } }); }); } Widget _crearRadio(){ return Column( mainAxisAlignment: MainAxisAlignment.center, children: _elemsRadio(), ) ; } List<Widget>_elemsRadio(){ List<Widget> lista = []; _valoresRadio.forEach((element) { RadioListTile r = RadioListTile( title: Text(element), value: element, groupValue: _radioAtenciomInicial, onChanged: (valor){ setState(() { _radioAtenciomInicial = valor; });}, ); lista.add(r); }); return lista; } Widget _crearSwitch(){ return SwitchListTile( title: Text('Hospitalizado'), subtitle: Text('Indica si está hospitalizado'), secondary: Icon(Icons.accessibility), value: _switchHospitalizado, onChanged: (valor) { setState(() { _switchHospitalizado = valor; }); }); } Widget _crearCheckBox(){ return CheckboxListTile( title: Text('Enfermedad terminal'), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0),side: BorderSide(color: Colors.grey)), value: _checkEnfermedadTerminal, onChanged: (nuevoValor) { // Hay que llamar a setState para que redibuje el CheckBox setState(() { _checkEnfermedadTerminal = nuevoValor! ;}); } ); } Widget _crearDropDown(){ return Container( height: 60.0, padding: EdgeInsets.only(left: 8.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), border: Border.all( color: Colors.grey ) ), child: Row( children: [ DropdownButton( value: _valorEscojidoTipoPaciente, onChanged: (String? valor) { setState(() { _valorEscojidoTipoPaciente = valor!; });}, items: _crearItemsDropDown(), ), Spacer(), Container(child: Icon(Icons.api), padding: EdgeInsets.only(right: 10.0), ), ], ), ); } List<DropdownMenuItem<String>> _crearItemsDropDown(){ List<DropdownMenuItem<String>> lista = []; _optTipoPaciente.forEach((element) { lista.add(DropdownMenuItem(child: Text('$element'),value: element,)); }); return lista; } Widget _crearEmail(){ return TextField( onChanged: (valor) => setState((){ _email = valor; print(valor);} ), // Fijarse que podemos guardar una cadena vacía keyboardType: TextInputType.emailAddress, decoration: InputDecoration( hintText: 'Email del paciente', labelText: 'Email', suffixIcon: Icon(Icons.email), border: OutlineInputBorder( borderRadius: BorderRadius.circular(15.0), ) ), ); } Widget _crearPassword(){ return TextField( onChanged: (valor) => setState((){_password = valor;}), // Fijarse que podemos guardar una cadena vacía obscureText: true, obscuringCharacter: '-', maxLength: 20, decoration: InputDecoration( hintText: 'Password de entrada', labelText: 'Password', suffixIcon: Icon(Icons.password), border: OutlineInputBorder( borderRadius: BorderRadius.circular(15.0), ) ), ); } Widget _crearInput() { return TextField( onChanged: (valor) => setState((){_nombre = valor;}), // Fijarse que podemos guardar una cadena vacía textCapitalization: TextCapitalization.words, keyboardType: TextInputType.name, decoration: InputDecoration( border: OutlineInputBorder( // Establece un borde cicular/otro alrededor de la caja de texto borderRadius: BorderRadius.circular(15.0), ), counterText: 'Número de caracteres: ${_nombre.length}', // Texto que aparece a la derecha y debajo de la caja hintText: 'Nombre del paciente', // Texto que aparece dentro de la caja y desaparece al escribir labelText: 'Nombre', // Texto qye aparece encima de la caja AL PULSAR EN EL INTERIOR PARA ESCRIBIR helperText: 'Introduce el nombre del paciente', // Texto que aparece a la izquierda y abajo de la caja suffixIcon: Icon(Icons.access_alarm), // Widget que aparece a la derecha dentro de la caja icon: Icon(Icons.accessibility), // Widget que aparece a la izquierda fuera de la caja ), ); } }
- NOTA: Si quieréis hacer que el DatePicker y TimePicker estén en español, seguir lo indicado en este enlace de la Wiki
Enlace a la página principal de la UD5
Enlace a la página principal del curso
-- Ángel D. Fernández González -- (2021).