I have a new inventory screen that lets you create a new inventory, the problem is, when clicking Descripcion a Keyboard appears so you can write, and I already can hide it when clicking outside of it.
To improve the user experience, I want to conceal it when you click on a dropdown menu while the keyboard is up, and I’m unable to figure out how to do it.
This id the code:
class CreateInventory extends StatefulWidget {
const CreateInventory({Key? key}) : super(key: key);
@override
_CreateInventoryState createState() => _CreateInventoryState();
}
class _CreateInventoryState extends State<CreateInventory> {
List<String> tiendas = globals.Globals.tiendasList;
List<String> almacenes = [];
List<String> tiposInventario = globals.Globals.tiposInventarioList;
final TextEditingController _dateController = TextEditingController();
DateTime _selectedDate = DateTime.now();
final dateFormatter = DateFormat('dd/MM/yyyy hh:mm');
String? descripcion = '';
String? selectedTienda='';
String? selectedAlmacen='';
String? selectedTipoInventario='';
@override
void initState() {
super.initState();
_dateController.text = dateFormatter.format(_selectedDate);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
// Cierra el teclado cuando se toca fuera del TextField
FocusScope.of(context).unfocus();
},
child: Scaffold(
bottomNavigationBar: BuildBottomAppbar(
descripcion: descripcion,
selectedAlmacen: selectedAlmacen,
selectedDate: _selectedDate,
selectedTienda: selectedTienda,
selectedTipoInventario: selectedTipoInventario,
),
appBar: buildAppBar(),
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(20.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.grey.shade300,
),
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: buildContainerDescripcion(),
),
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: buildContainerTienda(),
),
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: buildContainerAlmacen(),
),
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: buildContainerTipoInv(),
),
buildContainerFecha(context),
],
),
),
),
),
),
),
);
}
Container buildContainerFecha(BuildContext context) {
return Container(
child: buildListTileFechaHora(context),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey.shade200,
),
);
}
Container buildContainerTipoInv() {
return Container(
child: buildListTileTipoInventario(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey.shade200,
),
);
}
Container buildContainerTienda() {
return Container(
child: buildListTileTienda(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey.shade200,
),
);
}
Container buildContainerDescripcion() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey.shade200,
),
padding: const EdgeInsets.all( 16.0) ,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Descripción',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
buildTextField(),
],
),
);
}
Padding buildTextField() {
return Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Descripción del inventario',
),
onChanged: (text) {
setState(() {
descripcion = text;
});
},
),
),
);
}
Container buildContainerAlmacen() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey.shade200,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Almacen',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
SizedBox(height: 8.0),
selectedTienda != null&&selectedTienda!=''
? buildDropdownMenuAlmacenes()
: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.red,
width: 1.0,
),
borderRadius: BorderRadius.circular(8.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Seleccione una tienda',
style: TextStyle(
color: Colors.red,
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
);
}
ListTile buildListTileFechaHora(BuildContext context) {
return ListTile(
title: const Text(
'Fecha y Hora',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
subtitle: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
),
child: TextButton(
onPressed: () async {
final DateTime currentTime =
DateTime.now(); // Get the current time
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: currentTime,
firstDate: DateTime(2000),
lastDate: currentTime, // Set lastDate to current time
);
if (pickedDate != null) {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(currentTime),
);
if (pickedTime != null) {
final DateTime selectedDateTime = DateTime(
pickedDate.year,
pickedDate.month,
pickedDate.day,
pickedTime.hour,
pickedTime.minute,
);
if (selectedDateTime.isBefore(currentTime) ||
selectedDateTime.isAtSameMomentAs(currentTime)) {
// Check if the selected date and time are in the past or equal to the current time
setState(() {
_selectedDate = selectedDateTime;
_dateController.text = DateFormat('dd/MM/yyyy hh:mm')
.format(_selectedDate);
});
} else {
// Show a message or handle the case where the selected date and time are in the future
showDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text('Invalid Date/Time'),
content: Text(
'Please select a date and time that is equal to or earlier than the current time.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
);
},
);
}
}
}
},
child: Text(
DateFormat('dd/MM/yyyy hh:mm').format(_selectedDate),
style: TextStyle(fontSize: 16),
),
),
),
),
],
),
);
}
ListTile buildListTileTipoInventario() {
return ListTile(
title: const Text(
'Tipo de inventario',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
subtitle: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
),
child: LayoutBuilder(
builder: (context, constraints) {
return DropdownMenu<String>(
width: constraints.maxWidth, // Set DropdownMenu width to the width of the subtitle container
hintText: 'Selecciona un tipo de inventario',
onSelected: (String? value) {
setState(() {
selectedTipoInventario = value;
});
},
dropdownMenuEntries: tiposInventario
.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(
value: value,
label: value,
);
}).toList(),
);
},
),
),
);
}
ListTile buildListTileTienda() {
return ListTile(
title: const Text(
'Tienda',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
subtitle: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
),
child: LayoutBuilder(
builder: (context, constraints) {
return buildDropdownMenuTienda(constraints.maxWidth);
},
),
),
);
}
Container buildDropdownMenuAlmacenes() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
),
child: LayoutBuilder(
builder: (context, constraints) {
return DropdownMenu<String>(
width: constraints.maxWidth,
hintText: 'Selecciona un almacén',
onSelected: (String? value) {
final parts = value?.split('--');
if (parts!.isNotEmpty) {
final selectedPart = parts[0];
setState(() {
updateAlmacenesSelect(selectedTienda!);
selectedAlmacen = selectedPart;
});
}
},
dropdownMenuEntries: almacenes
.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(
value: value,
label: value,
);
}).toList(),
);
},
),
);
}
DropdownMenu<String> buildDropdownMenuTienda(double maxWidth) {
return DropdownMenu<String>(
width: maxWidth,
hintText: 'Selecciona una tienda',
onSelected: (String? value) {
setState(() {
selectedTienda = value!;
updateAlmacenesSelect(selectedTienda!);
});
},
dropdownMenuEntries: tiendas
.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(value: value, label: value);
}).toList(),
);
}
AppBar buildAppBar() {
return AppBar(
elevation: 0,
automaticallyImplyLeading: false,
title: const Center(
child: Column(
children: [
Text(
'Nuevo Inventario',
style: TextStyle(
color: AppColors.grisTexto,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
),
),
],
),
),
iconTheme: const IconThemeData(color: AppColors.grisTexto),
backgroundColor: Colors.white,
);
}
Future<void> updateAlmacenesSelect(String s) async {
if (s.isNotEmpty&&s!='') {
List<String> value =
await globals.Globals.filterAlmacenesByTiendaId(s);
setState(() {
almacenes = value;
});
}
}
}
class BuildBottomAppbar extends StatelessWidget {
final DateTime selectedDate;
final String? descripcion;
final String? selectedTienda;
final String? selectedAlmacen;
final String? selectedTipoInventario;
BuildBottomAppbar({
required this.selectedDate,
required this.descripcion,
required this.selectedTienda,
required this.selectedAlmacen,
required this.selectedTipoInventario,
Key? key,
});
@override
Widget build(BuildContext context) {
return BottomAppBar(
color: Colors.white.withOpacity(0),
height: 75,
elevation: 0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FloatingActionButton(
heroTag: 'btn1',
onPressed: () {
_dialogBuilder(context);
},
backgroundColor: Colors.red,
child: const Icon(Icons.cancel),
),
FloatingActionButton(
heroTag: 'btn2',
onPressed: () async {
print('started');
await uploadInventory();
print('finished');
},
backgroundColor: Colors.green,
child: const Icon(Icons.check),
)
],
),
),
);
}
Future<void> uploadInventory() async {
print('uploading inventory');
postInventario(
selectedDate,
descripcion!,
// Replace with the appropriate value for idDominio
selectedTienda!,
// Replace with the appropriate value for idTienda
selectedAlmacen!,
// Replace with the appropriate value for idAlmacen
selectedTipoInventario!, // Replace with the appropriate value for idTipoInventario
);
print('uploaded inventory');
}
}
Future<void> _dialogBuilder(BuildContext context) {
return showCupertinoDialog<void>(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text(
'¿Estás seguro que deseas cancelar la creación de un nuevo inventario?',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
content: Text(
'Si cancelas, se perderán todos los datos ingresados hasta el momento.',
style: TextStyle(fontSize: 16.0),
),
actions: <Widget>[
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Cancelar'),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
child: Text('Aceptar'),
),
],
);
},
);
}
Future<void> postInventario(DateTime fecha, String descripcion, String idTienda,
String idAlmacen, String idTipoInventario) async {
InventoryCreatorController inventoryCreatorController =
InventoryCreatorController();
String fechaFormateada = await getIds().getFormattedDate(fecha);
int idTiendaFinal = await getIds.getIdTienda(idTienda);
int idDominioFinal = await getIds().getIdDomain();
int idAlmacenFinal = getIdAlmacen(idAlmacen)!;
int idTipoInventarioFinal =
await getIds.getIdTipoInventario(idTipoInventario);
LoginController loginController = LoginController();
await loginController.updateToken();
await inventoryCreatorController.postInventario(fechaFormateada, descripcion,
idDominioFinal, idTiendaFinal, idAlmacenFinal, idTipoInventarioFinal);
}
int? getIdAlmacen(String input) {
String lastPart = input.split(' ').last;
// Parse the last part to an integer
return int.tryParse(lastPart);
}
class getIds {
MainDbManager mainDbManager = MainDbManager();
static Future<int> getIdTipoInventario(String idTipoInventario) async {
return await MainDbManager.fetchTipoInventarioId(idTipoInventario);
}
Future<int> getIdAlmacen(String idAlmacen) async {
return await MainDbManager.fetchAlmacenId(idAlmacen);
}
Future<int> getIdDomain() async {
return await MainDbManager.fetchDomainId();
}
static Future<int> getIdTienda(String idTienda) async {
return await MainDbManager.fetchTiendaId(idTienda);
}
static Future<int>getIdEstadoInventario(String estadoInventario) async {
return await MainDbManager.fetchEstadoInventarioId(estadoInventario);
}
getFormattedDate(DateTime fecha) {
String formattedDate = DateFormat('dd/MM/yyyy hh:mm').format(fecha);
return formattedDate;
}
}
2
Answers
Simply wrap your Myapp build function retrun with this GestureDetector
Here is complete code
it will dismiss keyboard when you tap on screen,
applicable for complete project