diff --git a/README.md b/README.md index 7717ba767..d16a01eb8 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,10 @@ FormBuilder( ), ``` +For a complete example that enables or disables the submit button from form +state and validates an email field conditionally, see +[Submit Button State](example/lib/sources/submit_button.dart). + #### Building your own custom field To build your own field within a `FormBuilder`, we use `FormBuilderField` which will require that you define your own field. diff --git a/example/README.md b/example/README.md index bdd4219f2..819787b3e 100644 --- a/example/README.md +++ b/example/README.md @@ -10,6 +10,7 @@ This example showcases various form building patterns and use cases: - **Sign Up Form** - A practical sign-up form example - **Dynamic Fields** - Forms with fields that can be added/removed at runtime - **Conditional Fields** - Fields that show/hide based on other field values +- **Submit Button State** - Submit button enabled from form state with conditional validation - **Related Fields** - Forms with interdependent field values - **Grouped Radio/Checkbox** - Selection inputs with grouping - **Decorated Radio/Checkbox** - Custom styling for selection fields @@ -48,6 +49,7 @@ lib/ │ ├── signup_form.dart # Sign up form │ ├── dynamic_fields.dart # Dynamic field management │ ├── conditional_fields.dart # Conditional visibility +│ ├── submit_button.dart # Submit button state and validation │ ├── related_fields.dart # Field dependencies │ ├── grouped_radio_checkbox.dart # Grouped selections │ ├── decorated_radio_checkbox.dart # Styled selections diff --git a/example/lib/main.dart b/example/lib/main.dart index 97b1214e9..c0f0ce8f6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,6 +3,7 @@ import 'package:example/sources/decorated_radio_checkbox.dart'; import 'package:example/sources/dynamic_fields.dart'; import 'package:example/sources/grouped_radio_checkbox.dart'; import 'package:example/sources/related_fields.dart'; +import 'package:example/sources/submit_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; @@ -132,6 +133,23 @@ class _HomePage extends StatelessWidget { }, ), const Divider(), + ListTile( + title: const Text('Submit Button State'), + trailing: const Icon(Icons.arrow_right_sharp), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return const CodePage( + title: 'Submit Button State', + child: SubmitButtonForm(), + ); + }, + ), + ); + }, + ), + const Divider(), ListTile( title: const Text('Related Fields'), trailing: const Icon(Icons.arrow_right_sharp), diff --git a/example/lib/sources/submit_button.dart b/example/lib/sources/submit_button.dart new file mode 100644 index 000000000..0f73efc9f --- /dev/null +++ b/example/lib/sources/submit_button.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; + +class SubmitButtonForm extends StatefulWidget { + const SubmitButtonForm({super.key}); + + @override + State createState() => _SubmitButtonFormState(); +} + +class _SubmitButtonFormState extends State { + final _formKey = GlobalKey(); + bool _canSubmit = false; + bool _subscribe = false; + bool _submitted = false; + + void _updateSubmitButton() { + final formState = _formKey.currentState; + if (formState == null) return; + + final isSubscribed = formState.instantValue['subscribe'] == true; + final isValid = formState.validate(focusOnInvalid: false); + + setState(() { + _canSubmit = isValid; + _subscribe = isSubscribed; + _submitted = false; + }); + } + + String? _validateEmail(String? value) { + final emailRequired = + _formKey.currentState?.instantValue['subscribe'] == true; + final hasEmail = value?.isNotEmpty ?? false; + + if (!emailRequired && !hasEmail) return null; + + return FormBuilderValidators.compose([ + FormBuilderValidators.required(), + FormBuilderValidators.email(), + ])(value); + } + + void _submit() { + if (_formKey.currentState?.saveAndValidate() ?? false) { + debugPrint(_formKey.currentState?.value.toString()); + setState(() { + _submitted = true; + }); + } + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: FormBuilder( + key: _formKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + initialValue: const {'subscribe': false}, + onChanged: _updateSubmitButton, + child: Column( + children: [ + FormBuilderTextField( + name: 'name', + decoration: const InputDecoration(labelText: 'Name'), + validator: FormBuilderValidators.required(), + ), + const SizedBox(height: 10), + FormBuilderSwitch( + name: 'subscribe', + title: const Text('Receive product updates'), + ), + const SizedBox(height: 10), + FormBuilderTextField( + name: 'email', + enabled: _subscribe, + decoration: InputDecoration( + labelText: _subscribe ? 'Email' : 'Email (optional)', + ), + keyboardType: TextInputType.emailAddress, + validator: _validateEmail, + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: _canSubmit ? _submit : null, + child: const Text('Submit'), + ), + if (_submitted) ...[ + const SizedBox(height: 10), + Text('Submitted values: ${_formKey.currentState?.value}'), + ], + ], + ), + ), + ); + } +}