diff --git a/example/lib/main.dart b/example/lib/main.dart index ede785cd0..c22b2aa9f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,7 @@ import 'package:example/sources/conditional_fields.dart'; 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:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -162,6 +163,23 @@ class _HomePage extends StatelessWidget { ); }, ), + const Divider(), + ListTile( + title: const Text('GroupedRadio and GroupedCheckbox Orientation'), + trailing: const Icon(Icons.arrow_right_sharp), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return const CodePage( + title: 'GroupedRadio and GroupedCheckbox', + child: GroupedRadioCheckbox(), + ); + }, + ), + ); + }, + ), ], ), ); diff --git a/example/lib/sources/grouped_radio_checkbox.dart b/example/lib/sources/grouped_radio_checkbox.dart new file mode 100644 index 000000000..d6f95a0b3 --- /dev/null +++ b/example/lib/sources/grouped_radio_checkbox.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; + +class GroupedRadioCheckbox extends StatefulWidget { + const GroupedRadioCheckbox({Key? key}) : super(key: key); + + @override + State createState() { + return _GroupedRadioCheckbox(); + } +} + +class _GroupedRadioCheckbox extends State { + bool autoValidate = true; + bool readOnly = false; + bool showSegmentedControl = true; + final _formKey = GlobalKey(); + + var genderOptions = ['Male', 'Female', 'Other']; + + void _onChanged(dynamic val) => debugPrint(val.toString()); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + FormBuilder( + key: _formKey, + // enabled: false, + onChanged: () { + _formKey.currentState!.save(); + debugPrint(_formKey.currentState!.value.toString()); + }, + autovalidateMode: AutovalidateMode.disabled, + initialValue: const { + 'movie_rating': 5, + 'best_language': 'Dart', + 'age': '13', + 'gender': 'Male', + 'languages_filter': ['Dart'] + }, + skipDisabled: true, + child: Column( + children: [ + // + const SizedBox(height: 15), + FormBuilderRadioGroup( + decoration: const InputDecoration( + labelText: 'My chosen language', + ), + initialValue: null, + name: 'best_language_horiz', + onChanged: _onChanged, + separator: const SizedBox( + width: 8.0, + height: 8.0, + child: DecoratedBox( + decoration: BoxDecoration(color: Colors.red), + ), + ), + validator: FormBuilderValidators.compose( + [FormBuilderValidators.required()]), + options: ['Dart', 'Kotlin', 'Java', 'Swift', 'Objective-C'] + .map((lang) => FormBuilderFieldOption( + value: lang, + child: Text(lang), + )) + .toList(growable: false), + controlAffinity: ControlAffinity.leading, + orientation: OptionsOrientation.wrap, + ), + // + const SizedBox(height: 15), + FormBuilderRadioGroup( + decoration: const InputDecoration( + labelText: 'My chosen language', + ), + initialValue: null, + name: 'best_language_vert', + onChanged: _onChanged, + separator: const SizedBox( + width: 8.0, + height: 8.0, + child: DecoratedBox( + decoration: BoxDecoration(color: Colors.red), + ), + ), + validator: FormBuilderValidators.compose( + [FormBuilderValidators.required()]), + options: ['Dart', 'Kotlin', 'Java', 'Swift', 'Objective-C'] + .map((lang) => FormBuilderFieldOption( + value: lang, + child: Text(lang), + )) + .toList(growable: false), + controlAffinity: ControlAffinity.leading, + orientation: OptionsOrientation.vertical, + ), + // + const SizedBox(height: 15), + FormBuilderCheckboxGroup( + autovalidateMode: AutovalidateMode.onUserInteraction, + decoration: const InputDecoration( + labelText: 'The language of my people'), + name: 'languages_horiz', + // initialValue: const ['Dart'], + options: const [ + FormBuilderFieldOption(value: 'Dart'), + FormBuilderFieldOption(value: 'Kotlin'), + FormBuilderFieldOption(value: 'Java'), + FormBuilderFieldOption(value: 'Swift'), + FormBuilderFieldOption(value: 'Objective-C'), + ], + onChanged: _onChanged, + separator: const SizedBox( + width: 8.0, + height: 8.0, + child: DecoratedBox( + decoration: BoxDecoration(color: Colors.red), + ), + ), + validator: FormBuilderValidators.compose([ + FormBuilderValidators.minLength(1), + FormBuilderValidators.maxLength(3), + ]), + orientation: OptionsOrientation.wrap, + ), + // + const SizedBox(height: 15), + FormBuilderCheckboxGroup( + autovalidateMode: AutovalidateMode.onUserInteraction, + decoration: const InputDecoration( + labelText: 'The language of my people'), + name: 'languages_vert', + // initialValue: const ['Dart'], + options: const [ + FormBuilderFieldOption(value: 'Dart'), + FormBuilderFieldOption(value: 'Kotlin'), + FormBuilderFieldOption(value: 'Java'), + FormBuilderFieldOption(value: 'Swift'), + FormBuilderFieldOption(value: 'Objective-C'), + ], + onChanged: _onChanged, + separator: const SizedBox( + width: 8.0, + height: 8.0, + child: DecoratedBox( + decoration: BoxDecoration(color: Colors.red), + ), + ), + validator: FormBuilderValidators.compose([ + FormBuilderValidators.minLength(1), + FormBuilderValidators.maxLength(3), + ]), + orientation: OptionsOrientation.vertical, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/widgets/grouped_checkbox.dart b/lib/src/widgets/grouped_checkbox.dart index 31f977347..3b354684f 100644 --- a/lib/src/widgets/grouped_checkbox.dart +++ b/lib/src/widgets/grouped_checkbox.dart @@ -295,13 +295,26 @@ class GroupedCheckbox extends StatelessWidget { child: option, ); - Widget compositeItem = Row( + Widget compositeItem = Column( mainAxisSize: MainAxisSize.min, - children: [ - if (controlAffinity == ControlAffinity.leading) control, - Flexible(flex: 1, child: label), - if (controlAffinity == ControlAffinity.trailing) control, - if (separator != null && index != options.length - 1) separator!, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (controlAffinity == ControlAffinity.leading) control, + Flexible(flex: 1, child: label), + if (controlAffinity == ControlAffinity.trailing) control, + if (orientation != OptionsOrientation.vertical && + separator != null && + index != options.length - 1) + separator!, + ], + ), + if (orientation == OptionsOrientation.vertical && + separator != null && + index != options.length - 1) + separator!, ], ); diff --git a/test/form_builder_checkbox_group_test.dart b/test/form_builder_checkbox_group_test.dart index 9982fe34f..0e74d588c 100644 --- a/test/form_builder_checkbox_group_test.dart +++ b/test/form_builder_checkbox_group_test.dart @@ -51,6 +51,8 @@ void main() { // same as wrapSpacing Container foo = tester.firstWidget(find.byType(Container)); expect(foo.margin, const EdgeInsets.only(right: 10.0)); + // verify separator counts + expect(find.byType(VerticalDivider), findsNothing); }); testWidgets('FormBuilderCheckboxGroup -- decoration vertical', @@ -75,8 +77,49 @@ void main() { // same as wrapSpacing Container foo = tester.firstWidget(find.byType(Container)); expect(foo.margin, const EdgeInsets.only(bottom: 10.0)); + // verify separator counts + expect(find.byType(VerticalDivider), findsNothing); }); + testWidgets('FormBuilderCheckboxGroup -- separator horizontal', + (WidgetTester tester) async { + const widgetName = 'cbg1'; + final testWidget = FormBuilderCheckboxGroup( + name: widgetName, + orientation: OptionsOrientation.horizontal, + wrapSpacing: 10.0, + options: const [ + FormBuilderFieldOption(key: ValueKey('1'), value: 1), + FormBuilderFieldOption(key: ValueKey('2'), value: 2), + FormBuilderFieldOption(key: ValueKey('3'), value: 2), + ], + separator: const VerticalDivider(width: 8.0, color: Colors.red), + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + // verify separator counts + expect(find.byType(VerticalDivider), findsNWidgets(2)); + }); + + testWidgets('FormBuilderCheckboxGroup -- separator vertical', + (WidgetTester tester) async { + const widgetName = 'cbg1'; + final testWidget = FormBuilderCheckboxGroup( + name: widgetName, + orientation: OptionsOrientation.vertical, + wrapSpacing: 10.0, + options: const [ + FormBuilderFieldOption(key: ValueKey('1'), value: 1), + FormBuilderFieldOption(key: ValueKey('2'), value: 2), + FormBuilderFieldOption(key: ValueKey('3'), value: 2), + ], + separator: const VerticalDivider(width: 8.0, color: Colors.red), + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + // verify separator counts + expect(find.byType(VerticalDivider), findsNWidgets(2)); + }); testWidgets('FormBuilderCheckboxGroup -- didChange', (WidgetTester tester) async { const fieldName = 'cbg1'; diff --git a/test/form_builder_radio_group_test.dart b/test/form_builder_radio_group_test.dart index 80bfae791..68dc19109 100644 --- a/test/form_builder_radio_group_test.dart +++ b/test/form_builder_radio_group_test.dart @@ -50,6 +50,8 @@ void main() { // same as wrapSpacing Container foo = tester.firstWidget(find.byType(Container)); expect(foo.margin, const EdgeInsets.only(right: 10.0)); + // verify separator counts + expect(find.byType(VerticalDivider), findsNothing); }); testWidgets('FormBuilderRadioGroup -- decoration vertical', @@ -74,5 +76,50 @@ void main() { // same as wrapSpacing Container foo = tester.firstWidget(find.byType(Container)); expect(foo.margin, const EdgeInsets.only(bottom: 10.0)); + // verify separator counts + expect(find.byType(VerticalDivider), findsNothing); + }); + + testWidgets('FormBuilderRadioGroup -- separators horizontal', + (WidgetTester tester) async { + const widgetName = 'rg1'; + final testWidget = FormBuilderRadioGroup( + name: widgetName, + orientation: OptionsOrientation.horizontal, + wrapSpacing: 10.0, + options: const [ + FormBuilderFieldOption(key: ValueKey('1'), value: 1), + FormBuilderFieldOption(key: ValueKey('2'), value: 2), + FormBuilderFieldOption(key: ValueKey('3'), value: 2), + ], + separator: const VerticalDivider(width: 8.0, color: Colors.red), + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + // verify separator counts + expect(find.byType(VerticalDivider), findsNWidgets(2)); + }); + + testWidgets('FormBuilderRadioGroup -- separators vertical', + (WidgetTester tester) async { + const widgetName = 'rg1'; + final testWidget = FormBuilderRadioGroup( + name: widgetName, + orientation: OptionsOrientation.vertical, + wrapSpacing: 10.0, + options: const [ + FormBuilderFieldOption(key: ValueKey('1'), value: 1), + FormBuilderFieldOption(key: ValueKey('2'), value: 2), + FormBuilderFieldOption(key: ValueKey('2'), value: 2), + ], + itemDecoration: BoxDecoration( + border: Border.all(color: Colors.blueAccent), + ), + separator: const VerticalDivider(width: 8.0, color: Colors.red), + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + // verify separator counts + expect(find.byType(VerticalDivider), findsNWidgets(2)); }); }