diff --git a/docs/pages/docs/checkbox.mdx b/docs/pages/docs/checkbox.mdx
index 211c12eec..6fe04682a 100644
--- a/docs/pages/docs/checkbox.mdx
+++ b/docs/pages/docs/checkbox.mdx
@@ -17,13 +17,28 @@ For touch devices, a [switch](/docs/switch) is generally recommended over a chec
- ```dart
- FCheckbox(
- label: const Text('Accept terms and conditions'),
- description: const Text('You agree to our terms and conditions.'),
- semanticLabel: 'Accept terms and conditions',
- onChange: (value) {}, // Do something.
- );
+ ```dart {12-20}
+ class Page extends StatefulWidget {
+ const Page();
+
+ @override
+ State createState() => PageState();
+ }
+
+ class PageState extends State {
+ bool state = false;
+
+ @override
+ Widget build(BuildContext context) => FCheckbox(
+ label: const Text('Accept terms and conditions'),
+ description: const Text('You agree to our terms and conditions.'),
+ semanticLabel: 'Accept terms and conditions',
+ value: state,
+ onChange: (value) {
+ setState(() => state = value);
+ },
+ );
+ }
```
@@ -36,11 +51,12 @@ For touch devices, a [switch](/docs/switch) is generally recommended over a chec
FCheckbox(
label: const Text('Accept terms and conditions'),
description: const Text('You agree to our terms and conditions.'),
+ error: const Text('Please accept the terms and conditions.'),
semanticLabel: 'Accept terms and conditions',
+ value: true,
+ onChanged: (value) {},
enabled: true,
- initialValue: true,
autofocus: true,
- onChanged: (value) {},
);
```
@@ -50,37 +66,66 @@ FCheckbox(
-
+
- ```dart {6}
- FCheckbox(
- label: const Text('Accept terms and conditions'),
- description: const Text('You agree to our terms and conditions.'),
- semanticLabel: 'Accept terms and conditions',
- onChange: (value) {}, // Do something.
- enabled: false,
- );
+ ```dart {20}
+ class Page extends StatefulWidget {
+ const Page();
+
+ @override
+ State createState() => PageState();
+ }
+
+ class PageState extends State {
+ bool state = true;
+
+ @override
+ Widget build(BuildContext context) => FCheckbox(
+ label: const Text('Accept terms and conditions'),
+ description: const Text('You agree to our terms and conditions.'),
+ semanticLabel: 'Accept terms and conditions',
+ value: state,
+ onChange: (value) {
+ setState(() => state = value);
+ },
+ enabled: false,
+ );
+ }
```
-### Force Error
-Force an error state without using the `validator` function.
+### Error
-
+
- ```dart {6}
- FCheckbox(
- label: const Text('Accept terms and conditions'),
- description: const Text('You agree to our terms and conditions.'),
- semanticLabel: 'Accept terms and conditions',
- onChange: (value) {}, // Do something.
- forceErrorText: 'Please accept the terms and conditions.',
- );
+ ```dart {15}
+ class Page extends StatefulWidget {
+ const Page();
+
+ @override
+ State createState() => PageState();
+ }
+
+ class PageState extends State {
+ bool state = false;
+
+ @override
+ Widget build(BuildContext context) => FCheckbox(
+ label: const Text('Accept terms and conditions'),
+ description: const Text('You agree to our terms and conditions.'),
+ error: const Text('Please accept the terms and conditions.'),
+ semanticLabel: 'Accept terms and conditions',
+ value: state,
+ onChange: (value) {
+ setState(() => state = value);
+ },
+ );
+ }
```
@@ -92,11 +137,25 @@ Force an error state without using the `validator` function.
- ```dart
- FCheckbox(
- onChange: (value) {}, // Do something.
- enabled: enabled,
- );
+ ```dart {12-17}
+ class Page extends StatefulWidget {
+ const Page();
+
+ @override
+ State createState() => PageState();
+ }
+
+ class PageState extends State {
+ bool state = false;
+
+ @override
+ Widget build(BuildContext context) => FCheckbox(
+ value: state,
+ onChange: (value) {
+ setState(() => state = value);
+ },
+ );
+ }
```
@@ -108,7 +167,7 @@ Force an error state without using the `validator` function.
- ```dart {32-36}
+ ```dart {32-47}
class RegisterForm extends StatefulWidget {
const RegisterForm({super.key});
@@ -140,10 +199,21 @@ Force an error state without using the `validator` function.
validator: (value) => 8 <= (value?.length ?? 0) ? null : 'Password must be at least 8 characters long.',
),
const SizedBox(height: 15),
- FCheckbox(
- label: const Text('Accept terms and conditions'),
- description: const Text('You agree to our terms and conditions.'),
+ FormField(
+ initialValue: false,
+ onSaved: (value) {
+ // Save values somewhere.
+ },
validator: (value) => (value ?? false) ? null : 'Please accept the terms and conditions.',
+ builder: (state) => FCheckbox(
+ label: const Text('Accept terms and conditions'),
+ description: const Text('You agree to our terms and conditions.'),
+ error: state.errorText != null ? Text(state.errorText!) : null,
+ value: state.value ?? false,
+ onChange: (value) {
+ state.didChange(value);
+ },
+ ),
),
const SizedBox(height: 20),
FButton(
@@ -153,6 +223,8 @@ Force an error state without using the `validator` function.
// Handle errors here.
return;
}
+
+ _formKey.currentState!.save();
// Do something.
},
),
diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md
index 3c2ad90d3..b22e898b0 100644
--- a/forui/CHANGELOG.md
+++ b/forui/CHANGELOG.md
@@ -28,6 +28,8 @@
* Add `FTooltip`.
+* Add `FSelectGroup`.
+
### Changes
* **Breaking:** Change `FAlertIconStyle.height` to `FAlertIconStyle.size`.
@@ -61,6 +63,9 @@ and`FStyle.errorFormFieldStyle`.
* Improve platform detection for web when initializing platform-specific variables.
+* **Breaking:** `FCheckbox` and `FSwitch` no longer wraps `FormField` - consider wrapping them in a `FormField` if
+required.
+
### Fixes
* Fix `FResizable` not rendering properly in an expanded widget when its crossAxisExtent is null.
diff --git a/samples/lib/widgets/checkbox.dart b/samples/lib/widgets/checkbox.dart
index 8db1babe0..1300b983e 100644
--- a/samples/lib/widgets/checkbox.dart
+++ b/samples/lib/widgets/checkbox.dart
@@ -7,13 +7,15 @@ import 'package:forui_samples/sample_scaffold.dart';
@RoutePage()
class CheckboxPage extends SampleScaffold {
+ final bool initialValue;
final bool enabled;
- final String? forceErrorText;
+ final String? error;
CheckboxPage({
@queryParam super.theme,
+ @queryParam this.initialValue = false,
@queryParam this.enabled = true,
- @queryParam this.forceErrorText,
+ @queryParam this.error,
});
@override
@@ -22,19 +24,54 @@ class CheckboxPage extends SampleScaffold {
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 290),
- child: FCheckbox(
- label: const Text('Accept terms and conditions'),
- description: const Text('You agree to our terms and conditions.'),
- semanticLabel: 'Accept terms and conditions',
- onChange: (value) {}, // Do something.
- forceErrorText: forceErrorText,
+ child: _Checkbox(
+ initialValue: initialValue,
enabled: enabled,
+ error: error,
),
),
],
);
}
+class _Checkbox extends StatefulWidget {
+ final bool initialValue;
+ final bool enabled;
+ final String? error;
+
+ const _Checkbox({
+ required this.initialValue,
+ required this.enabled,
+ this.error,
+ });
+
+ @override
+ State<_Checkbox> createState() => _CheckboxState();
+}
+
+class _CheckboxState extends State<_Checkbox> {
+ bool state = false;
+
+ @override
+ void initState() {
+ super.initState();
+ state = widget.initialValue;
+ }
+
+ @override
+ Widget build(BuildContext context) => FCheckbox(
+ label: const Text('Accept terms and conditions'),
+ description: const Text('You agree to our terms and conditions.'),
+ error: widget.error != null ? Text(widget.error!) : null,
+ semanticLabel: 'Accept terms and conditions',
+ value: state,
+ onChange: (value) {
+ setState(() => state = value);
+ },
+ enabled: widget.enabled,
+ );
+}
+
@RoutePage()
class RawCheckboxPage extends SampleScaffold {
final bool enabled;
@@ -50,15 +87,34 @@ class RawCheckboxPage extends SampleScaffold {
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300),
- child: FCheckbox(
- onChange: (value) {}, // Do something.
- enabled: enabled,
- ),
+ child: _RawCheckbox(enabled: enabled),
),
],
);
}
+class _RawCheckbox extends StatefulWidget {
+ final bool enabled;
+
+ const _RawCheckbox({required this.enabled});
+
+ @override
+ State<_RawCheckbox> createState() => _RawCheckboxState();
+}
+
+class _RawCheckboxState extends State<_RawCheckbox> {
+ bool state = false;
+
+ @override
+ Widget build(BuildContext context) => FCheckbox(
+ value: state,
+ onChange: (value) {
+ setState(() => state = value);
+ },
+ enabled: widget.enabled,
+ );
+}
+
@RoutePage()
class FormCheckboxPage extends SampleScaffold {
FormCheckboxPage({
@@ -103,10 +159,21 @@ class _RegisterFormState extends State {
validator: (value) => 8 <= (value?.length ?? 0) ? null : 'Password must be at least 8 characters long.',
),
const SizedBox(height: 15),
- FCheckbox(
- label: const Text('Accept terms and conditions'),
- description: const Text('You agree to our terms and conditions.'),
+ FormField(
+ initialValue: false,
+ onSaved: (value) {
+ // Save values somewhere.
+ },
validator: (value) => (value ?? false) ? null : 'Please accept the terms and conditions.',
+ builder: (state) => FCheckbox(
+ label: const Text('Accept terms and conditions'),
+ description: const Text('You agree to our terms and conditions.'),
+ error: state.errorText != null ? Text(state.errorText!) : null,
+ value: state.value ?? false,
+ onChange: (value) {
+ state.didChange(value);
+ },
+ ),
),
const SizedBox(height: 20),
FButton(
@@ -116,6 +183,8 @@ class _RegisterFormState extends State {
// Handle errors here.
return;
}
+
+ _formKey.currentState!.save();
// Do something.
},
),
diff --git a/samples/pubspec.lock b/samples/pubspec.lock
index f923d9cd8..0c55bb373 100644
--- a/samples/pubspec.lock
+++ b/samples/pubspec.lock
@@ -367,18 +367,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
- sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
+ sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
- version: "10.0.5"
+ version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
- sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
+ sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
- version: "3.0.5"
+ version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
@@ -407,18 +407,18 @@ packages:
dependency: transitive
description:
name: material_color_utilities
- sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
- version: "0.11.1"
+ version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
- sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
+ sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
- version: "1.15.0"
+ version: "1.12.0"
mime:
dependency: transitive
description:
@@ -652,10 +652,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
+ sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
- version: "0.7.2"
+ version: "0.7.0"
timing:
dependency: transitive
description:
@@ -708,10 +708,10 @@ packages:
dependency: transitive
description:
name: vm_service
- sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
+ sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
- version: "14.2.5"
+ version: "14.2.1"
watcher:
dependency: transitive
description: