I am new to flutter and I am trying to validate a dropdownMenu. Like the other fields in the form I want the border to be red when the submit button is pressed
I am trying to validate using the controller. I know how to validate the other textfields with the form widget but I don’t know how to do the same for the dropdownMenu
This is my code
class PaymentsForm extends StatefulWidget {
const PaymentsForm({super.key});
@override
State<PaymentsForm> createState() => _PaymentsFormState();
}
class _PaymentsFormState extends State<PaymentsForm> {
final _controller = TextEditingController();
bool _isFieldEmpty = false;
final GlobalKey<FormState> _paymentsFormKey = GlobalKey<FormState>();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _validateInput() {
setState(() {
print("Validating");
_isFieldEmpty = _controller.text.isEmpty;
});
}
@override
Widget build(BuildContext context) {
return Dialog.fullscreen(
child: Container(
decoration: const BoxDecoration(color: primaryColor),
// width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(40, 40, 0, 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Gap(30),
CircleAvatar(
backgroundColor: Colors.white,
radius: 40,
child: Icon(
color: primaryColor,
Icons.credit_card,
size: 50,
),
),
Gap(20),
Text(
'Payments',
style: TextStyle(
letterSpacing: 3,
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 35,
fontFamily: customFont,
),
),
Gap(7),
Text(
'Make a payment',
style: TextStyle(
letterSpacing: 3,
fontFamily: customFont,
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w100),
),
],
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(10),
topLeft: Radius.circular(10)),
color: Colors.white,
),
child: SingleChildScrollView(
child: Form(
key: _paymentsFormKey,
child: Column(
children: [
SizedBox(
height: 62,
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(5),
child: SegmentedButton<String>(
style: SegmentedButton.styleFrom(
padding: EdgeInsets.zero,
selectedForegroundColor: Colors.white,
selectedBackgroundColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
side: const BorderSide(
color: Colors.black), // Add border
),
),
segments: const <ButtonSegment<String>>[
ButtonSegment<String>(
value: 'KSH',
label: Text('KSH'),
),
ButtonSegment<String>(
value: 'USD',
label: Text('USD'),
),
ButtonSegment<String>(
value: 'USD/KSH',
label: Text('USD/KSH'),
),
ButtonSegment<String>(
value: 'Expense',
label: Text('Expense'),
),
],
selected: {transactionType},
onSelectionChanged: (Set<String> newSelection) {
setState(() {
transactionType = newSelection.first;
});
},
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 5),
child: SizedBox(
height: 45,
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('users/$adeer/journal entries')
.where("journalEntryType",
isEqualTo: "Payment")
.where("date",
isGreaterThan: previousTimeStamp)
.orderBy('date')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return TextField(
decoration: kInputDecoration.copyWith(
label: const Text(
'Account from'))); // Placeholder for loading state
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
var accounts = snapshot.data!.docs;
// print(
// 'At j entry >>>>>>>>>>>>>>>>>> $adeer >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
List<DropdownMenuEntry<String>>
dropdownEntries = accounts.map((account) {
var firstName = account['first name'];
var lastName = account['last name'];
return DropdownMenuEntry<String>(
value: '$firstName $lastName',
label: '$firstName $lastName',
);
}).toList();
// Return the DropdownMenu with the built entries
return DropdownMenu(
controller: _controller,
expandedInsets: EdgeInsets.zero,
requestFocusOnTap: true,
label: const Text('Account from'),
enableFilter: true,
enableSearch: true,
menuHeight: 200,
inputDecorationTheme:
dropdownDecoration.copyWith(
border: OutlineInputBorder(
borderSide: BorderSide(
color: _isFieldEmpty
? Colors.red
: Colors.grey,
),
),
),
dropdownMenuEntries: dropdownEntries,
onSelected: (String? value) {},
);
}
},
),
),
),
Row(
children: [
Expanded(
flex: 2,
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 5),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
32), // Limit the number of characters
FilteringTextInputFormatter.deny(
RegExp('[0-9]')),
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Receiver full name'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(
0,
0,
5,
5,
),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
12), // Limit the number of characters
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Receiver 's ID')),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
),
],
),
if (transactionType == 'USD/KSH')
Row(
children: [
Expanded(
child: Padding(
padding:
const EdgeInsets.fromLTRB(5, 3, 5, 5),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
9), // Limit the number of characters
FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Dollar amount'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
),
Expanded(
child: Padding(
padding:
const EdgeInsets.fromLTRB(0, 3, 5, 5),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
9), // Limit the number of characters
FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Ksh amount'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
),
],
),
if (transactionType == 'USD')
Padding(
padding: const EdgeInsets.fromLTRB(5, 3, 5, 5),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
9), // Limit the number of characters
FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Dollar amount'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
if (transactionType == 'KSH')
Padding(
padding: const EdgeInsets.fromLTRB(5, 3, 5, 5),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
9), // Limit the number of characters
FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Ksh amount'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
if (transactionType == "Expense")
Padding(
padding: const EdgeInsets.fromLTRB(5, 3, 5, 5),
child: SizedBox(
height: 45,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(
9), // Limit the number of characters
FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Expense amount'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(5, 3, 5, 10),
child: TextFormField(
maxLines: 3,
minLines: 1,
inputFormatters: [
LengthLimitingTextInputFormatter(
50), // Limit the number of characters
],
decoration: kInputDecoration.copyWith(
errorStyle: TextStyle(height: 0),
label: const Text('Remarks'),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return '';
}
receiver = value;
return null;
},
),
),
Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: 130,
height: 40,
child: ElevatedButton(
style: const ButtonStyle(
foregroundColor:
WidgetStatePropertyAll<Color>(
Colors.white),
backgroundColor:
WidgetStatePropertyAll<Color>(
secondaryColor),
),
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Cancel'),
),
),
const Gap(10),
SizedBox(
width: 130,
height: 40,
child: ElevatedButton(
style: const ButtonStyle(
foregroundColor:
WidgetStatePropertyAll<Color>(
Colors.white),
backgroundColor:
WidgetStatePropertyAll<Color>(
primaryColor),
),
onPressed: () {
_validateInput();
print(_isFieldEmpty);
if (_paymentsFormKey.currentState!
.validate()) {
showDialog<String>(
context: context,
builder: (BuildContext context) =>
AlertDialog(
title: const Text('Confirm payment'),
content: Text(
'Make a payment of$receiver'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(
context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
_paymentsFormKey.currentState
?.reset();
Navigator.pop(context, 'OK');
},
child: const Text('OK'),
),
],
),
);
}
},
child: const Text('Submit'),
),
),
],
),
),
],
),
),
),
),
)
],
),
),
);
}
}
This is the dropdownMenu
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('users/$adeer/journal entries')
.where("journalEntryType",
isEqualTo: "Payment")
.where("date",
isGreaterThan: previousTimeStamp)
.orderBy('date')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return TextField(
decoration: kInputDecoration.copyWith(
label: const Text(
'Account from'))); // Placeholder for loading state
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
var accounts = snapshot.data!.docs;
// print(
// 'At j entry >>>>>>>>>>>>>>>>>> $adeer >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
List<DropdownMenuEntry<String>>
dropdownEntries = accounts.map((account) {
var firstName = account['first name'];
var lastName = account['last name'];
return DropdownMenuEntry<String>(
value: '$firstName $lastName',
label: '$firstName $lastName',
);
}).toList();
// Return the DropdownMenu with the built entries
return DropdownMenu(
controller: _controller,
expandedInsets: EdgeInsets.zero,
requestFocusOnTap: true,
label: const Text('Account from'),
enableFilter: true,
enableSearch: true,
menuHeight: 200,
inputDecorationTheme:
dropdownDecoration.copyWith(
border: OutlineInputBorder(
borderSide: BorderSide(
color: _isFieldEmpty
? Colors.red
: Colors.grey,
),
),
),
dropdownMenuEntries: dropdownEntries,
onSelected: (String? value) {},
);
}
},
),