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: