Skip to content

Commit 571c43c

Browse files
Merge pull request #1508 from rednikisfun/docs-1490-submit-button-example
docs: add submit button state example #1490
2 parents 38a24f6 + f080271 commit 571c43c

4 files changed

Lines changed: 122 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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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) return;
21+
22+
final isSubscribed = formState.instantValue['subscribe'] == true;
23+
final isValid = formState.validate(focusOnInvalid: false);
24+
25+
setState(() {
26+
_canSubmit = isValid;
27+
_subscribe = isSubscribed;
28+
_submitted = false;
29+
});
30+
}
31+
32+
String? _validateEmail(String? value) {
33+
final emailRequired =
34+
_formKey.currentState?.instantValue['subscribe'] == true;
35+
final hasEmail = value?.isNotEmpty ?? false;
36+
37+
if (!emailRequired && !hasEmail) return null;
38+
39+
return FormBuilderValidators.compose([
40+
FormBuilderValidators.required(),
41+
FormBuilderValidators.email(),
42+
])(value);
43+
}
44+
45+
void _submit() {
46+
if (_formKey.currentState?.saveAndValidate() ?? false) {
47+
debugPrint(_formKey.currentState?.value.toString());
48+
setState(() {
49+
_submitted = true;
50+
});
51+
}
52+
}
53+
54+
@override
55+
Widget build(BuildContext context) {
56+
return SingleChildScrollView(
57+
child: FormBuilder(
58+
key: _formKey,
59+
autovalidateMode: AutovalidateMode.onUserInteraction,
60+
initialValue: const {'subscribe': false},
61+
onChanged: _updateSubmitButton,
62+
child: Column(
63+
children: [
64+
FormBuilderTextField(
65+
name: 'name',
66+
decoration: const InputDecoration(labelText: 'Name'),
67+
validator: FormBuilderValidators.required(),
68+
),
69+
const SizedBox(height: 10),
70+
FormBuilderSwitch(
71+
name: 'subscribe',
72+
title: const Text('Receive product updates'),
73+
),
74+
const SizedBox(height: 10),
75+
FormBuilderTextField(
76+
name: 'email',
77+
enabled: _subscribe,
78+
decoration: InputDecoration(
79+
labelText: _subscribe ? 'Email' : 'Email (optional)',
80+
),
81+
keyboardType: TextInputType.emailAddress,
82+
validator: _validateEmail,
83+
),
84+
const SizedBox(height: 10),
85+
ElevatedButton(
86+
onPressed: _canSubmit ? _submit : null,
87+
child: const Text('Submit'),
88+
),
89+
if (_submitted) ...[
90+
const SizedBox(height: 10),
91+
Text('Submitted values: ${_formKey.currentState?.value}'),
92+
],
93+
],
94+
),
95+
),
96+
);
97+
}
98+
}

0 commit comments

Comments
 (0)