Reputation: 2257
How can I add a validator function to a list of RadioButtons in order to have them validated (like TextFormFields
with _formKey.currentState.validate()
) after the User submits the Form
?
Upvotes: 11
Views: 15938
Reputation: 89
I know this reply is a little late.
void main() {
runApp(const MyApp());
}
enum Gender { male, female, other }
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Gender? gender;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
key: _formKey,
child: Center(
child: FormField(
validator: (value) {
if (value == null) {
return "Please select a gender";
}
return null;
},
builder: (FormFieldState<Gender> field) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ListTile(
leading: Radio<Gender>(
value: Gender.male,
groupValue: gender,
onChanged: (Gender? selectedValue) {
setState(() {
gender = selectedValue;
});
},
),
title: Text(Gender.male.name.capitalize()),
),
ListTile(
leading: Radio<Gender>(
value: Gender.female,
groupValue: gender,
onChanged: (Gender? selectedValue) {
setState(() {
gender = selectedValue;
});
},
),
title: Text(Gender.female.name.capitalize()),
),
ListTile(
leading: Radio<Gender>(
value: Gender.other,
groupValue: gender,
onChanged: (Gender? selectedValue) {
setState(() {
gender = selectedValue;
});
},
),
title: Text(Gender.other.name.capitalize()),
),
const SizedBox(height: 20),
if (field.errorText != null) Text(field.errorText!),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
_formKey.currentState?.validate() ?? false;
},
child: const Text('Submit'),
),
],
),
),
),
),
),
);
}
}
// Extension method to capitalize first letter
extension StringCapitalization on String {
String capitalize() {
return "${this[0].toUpperCase()}${substring(1)}";
}
}
Upvotes: 0
Reputation: 112
Use FormField(). You can use FormField classes validator, autovalidateMode, and other functions. Here is my sample code
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: FormField(
autovalidateMode: AutoValidateMode.onUserInteraction,
validator: (value){
return optionBool == null ? "Required field" : null;
},
builder: (FormFieldState<dynamic> field){
return MyCustomWidget();
}
),
Upvotes: 0
Reputation: 3239
I didn't want to use a whole package just for that, but this article helped me solve it manually: https://medium.com/free-code-camp/how-to-validate-forms-and-user-input-the-easy-way-using-flutter-e301a1531165
It's not complicated, you basically just check if a value is selected:
// I have multiple forms because I am using a stepper
// so each step form gets its own validation
bool _validateForm() {
switch (currentStep) {
case 0:
if (userType == null) {
showRadioError();
return false;
}
break;
// ... Other cases / steps
}
return true;
}
// Validate the whole form of the step on button press of "next" button
void onStepContinue(int stepAmount) {
bool isLastStep = (currentStep == stepAmount - 1);
if (isLastStep) {
//Do something with this information
print("FINISHED!");
} else {
if (_validateForm()) {
currentStep += 1;
notifyListeners();
}
}
}
Upvotes: 0
Reputation: 54367
You can copy paste run full code below
You can use package https://pub.dev/packages/flutter_form_builder
It support bulid-in validators
such as FormBuilderValidators.required()
you can directly use
you can also use custom validator function
https://pub.dev/packages/flutter_form_builder#custom-validator-function
FormBuilderRadio(
decoration:
InputDecoration(labelText: 'My chosen language'),
attribute: "best_language",
leadingInput: true,
onChanged: _onChanged,
validators: [FormBuilderValidators.required()],
options:
["Dart", "Kotlin", "Java", "Swift", "Objective-C"]
.map((lang) => FormBuilderFieldOption(
value: lang,
child: Text('$lang'),
))
.toList(growable: false),
),
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter FormBuilder Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
inputDecorationTheme: InputDecorationTheme(
labelStyle: TextStyle(color: Colors.purple),
),
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
MyHomePageState createState() {
return MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
var data;
bool autoValidate = true;
bool readOnly = false;
bool showSegmentedControl = true;
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
final GlobalKey<FormFieldState> _specifyTextFieldKey =
GlobalKey<FormFieldState>();
ValueChanged _onChanged = (val) => print(val);
var genderOptions = ['Male', 'Female', 'Other'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FormBuilder Example"),
),
body: Padding(
padding: EdgeInsets.all(10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
FormBuilder(
// context,
key: _fbKey,
autovalidate: true,
initialValue: {
'movie_rating': 5,
},
readOnly: false,
child: Column(
children: <Widget>[
FormBuilderRadio(
decoration:
InputDecoration(labelText: 'My chosen language'),
attribute: "best_language",
leadingInput: true,
onChanged: _onChanged,
validators: [FormBuilderValidators.required()],
options:
["Dart", "Kotlin", "Java", "Swift", "Objective-C"]
.map((lang) => FormBuilderFieldOption(
value: lang,
child: Text('$lang'),
))
.toList(growable: false),
),
],
),
),
Row(
children: <Widget>[
Expanded(
child: MaterialButton(
color: Theme.of(context).accentColor,
child: Text(
"Submit",
style: TextStyle(color: Colors.white),
),
onPressed: () {
if (_fbKey.currentState.saveAndValidate()) {
print(_fbKey.currentState.value);
} else {
print(_fbKey.currentState.value);
print("validation failed");
}
},
),
),
SizedBox(
width: 20,
),
Expanded(
child: MaterialButton(
color: Theme.of(context).accentColor,
child: Text(
"Reset",
style: TextStyle(color: Colors.white),
),
onPressed: () {
_fbKey.currentState.reset();
},
),
),
],
),
],
),
),
),
);
}
}
Upvotes: 7