Skip to content

Commit fa672cf

Browse files
committed
docs: add submit button state example #1490
1 parent 38a24f6 commit fa672cf

4 files changed

Lines changed: 126 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ FormBuilder(
154154
),
155155
```
156156

157+
For a complete example that enables or disables the submit button from form
158+
state and validates an email field conditionally, see
159+
[Submit Button State](example/lib/sources/submit_button.dart).
160+
157161
#### Building your own custom field
158162

159163
To build your own field within a `FormBuilder`, we use `FormBuilderField` which will require that you define your own field.

example/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This example showcases various form building patterns and use cases:
1010
- **Sign Up Form** - A practical sign-up form example
1111
- **Dynamic Fields** - Forms with fields that can be added/removed at runtime
1212
- **Conditional Fields** - Fields that show/hide based on other field values
13+
- **Submit Button State** - Submit button enabled from form state with conditional validation
1314
- **Related Fields** - Forms with interdependent field values
1415
- **Grouped Radio/Checkbox** - Selection inputs with grouping
1516
- **Decorated Radio/Checkbox** - Custom styling for selection fields
@@ -48,6 +49,7 @@ lib/
4849
│ ├── signup_form.dart # Sign up form
4950
│ ├── dynamic_fields.dart # Dynamic field management
5051
│ ├── conditional_fields.dart # Conditional visibility
52+
│ ├── submit_button.dart # Submit button state and validation
5153
│ ├── related_fields.dart # Field dependencies
5254
│ ├── grouped_radio_checkbox.dart # Grouped selections
5355
│ ├── decorated_radio_checkbox.dart # Styled selections

example/lib/main.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:example/sources/decorated_radio_checkbox.dart';
33
import 'package:example/sources/dynamic_fields.dart';
44
import 'package:example/sources/grouped_radio_checkbox.dart';
55
import 'package:example/sources/related_fields.dart';
6+
import 'package:example/sources/submit_button.dart';
67
import 'package:flutter/material.dart';
78
import 'package:flutter_localizations/flutter_localizations.dart';
89
import 'package:form_builder_validators/form_builder_validators.dart';
@@ -132,6 +133,23 @@ class _HomePage extends StatelessWidget {
132133
},
133134
),
134135
const Divider(),
136+
ListTile(
137+
title: const Text('Submit Button State'),
138+
trailing: const Icon(Icons.arrow_right_sharp),
139+
onTap: () {
140+
Navigator.of(context).push(
141+
MaterialPageRoute(
142+
builder: (context) {
143+
return const CodePage(
144+
title: 'Submit Button State',
145+
child: SubmitButtonForm(),
146+
);
147+
},
148+
),
149+
);
150+
},
151+
),
152+
const Divider(),
135153
ListTile(
136154
title: const Text('Related Fields'),
137155
trailing: const Icon(Icons.arrow_right_sharp),
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_form_builder/flutter_form_builder.dart';
3+
import 'package:form_builder_validators/form_builder_validators.dart';
4+
5+
class SubmitButtonForm extends StatefulWidget {
6+
const SubmitButtonForm({super.key});
7+
8+
@override
9+
State<SubmitButtonForm> createState() => _SubmitButtonFormState();
10+
}
11+
12+
class _SubmitButtonFormState extends State<SubmitButtonForm> {
13+
final _formKey = GlobalKey<FormBuilderState>();
14+
bool _canSubmit = false;
15+
bool _subscribe = false;
16+
bool _submitted = false;
17+
18+
void _updateSubmitButton() {
19+
final formState = _formKey.currentState;
20+
if (formState == null) {
21+
return;
22+
}
23+
24+
final isSubscribed = formState.instantValue['subscribe'] == true;
25+
final isValid = formState.validate(focusOnInvalid: false);
26+
27+
setState(() {
28+
_canSubmit = isValid;
29+
_subscribe = isSubscribed;
30+
_submitted = false;
31+
});
32+
}
33+
34+
String? _validateEmail(String? value) {
35+
final emailRequired =
36+
_formKey.currentState?.instantValue['subscribe'] == true;
37+
final hasEmail = value?.isNotEmpty ?? false;
38+
39+
if (!emailRequired && !hasEmail) {
40+
return null;
41+
}
42+
43+
return FormBuilderValidators.compose([
44+
FormBuilderValidators.required(),
45+
FormBuilderValidators.email(),
46+
])(value);
47+
}
48+
49+
void _submit() {
50+
if (_formKey.currentState?.saveAndValidate() ?? false) {
51+
debugPrint(_formKey.currentState?.value.toString());
52+
setState(() {
53+
_submitted = true;
54+
});
55+
}
56+
}
57+
58+
@override
59+
Widget build(BuildContext context) {
60+
return SingleChildScrollView(
61+
child: FormBuilder(
62+
key: _formKey,
63+
autovalidateMode: AutovalidateMode.onUserInteraction,
64+
initialValue: const {'subscribe': false},
65+
onChanged: _updateSubmitButton,
66+
child: Column(
67+
children: [
68+
FormBuilderTextField(
69+
name: 'name',
70+
decoration: const InputDecoration(labelText: 'Name'),
71+
validator: FormBuilderValidators.required(),
72+
),
73+
const SizedBox(height: 10),
74+
FormBuilderSwitch(
75+
name: 'subscribe',
76+
title: const Text('Receive product updates'),
77+
),
78+
const SizedBox(height: 10),
79+
FormBuilderTextField(
80+
name: 'email',
81+
enabled: _subscribe,
82+
decoration: InputDecoration(
83+
labelText: _subscribe ? 'Email' : 'Email (optional)',
84+
),
85+
keyboardType: TextInputType.emailAddress,
86+
validator: _validateEmail,
87+
),
88+
const SizedBox(height: 10),
89+
ElevatedButton(
90+
onPressed: _canSubmit ? _submit : null,
91+
child: const Text('Submit'),
92+
),
93+
if (_submitted) ...[
94+
const SizedBox(height: 10),
95+
Text('Submitted values: ${_formKey.currentState?.value}'),
96+
],
97+
],
98+
),
99+
),
100+
);
101+
}
102+
}

0 commit comments

Comments
 (0)