From 3cbcfbf301699383d2f8ce6db1a91e9dafa89d4f Mon Sep 17 00:00:00 2001 From: Matthias Ngeo Date: Sun, 6 Oct 2024 17:56:22 +0800 Subject: [PATCH] Fix select group not applying correct style (#216) * Fix select group not applying correct style * Commit from GitHub Actions (Forui Presubmit) * Fix PR issues * Update forui/lib/src/widgets/select_group/select_group.dart Co-authored-by: Joe Kawai --------- Co-authored-by: Pante Co-authored-by: Joe Kawai --- CONTRIBUTING.md | 25 ++ forui/CHANGELOG.md | 4 + .../widgets/select_group/select_group.dart | 27 +- .../select_group/select_group_item.dart | 306 +++++++++++++----- forui/lib/widgets/select_group.dart | 2 +- forui/test/golden/blue-screen.png | Bin 0 -> 20961 bytes forui/test/src/test_scaffold.dart | 25 ++ .../select_group_controller_test.dart | 64 ++-- .../select_group_golden_test.dart | 57 +++- samples/pubspec.lock | 4 +- 10 files changed, 358 insertions(+), 156 deletions(-) create mode 100644 forui/test/golden/blue-screen.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0049a10bc..ab9ffcfdc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -167,6 +167,31 @@ Golden images are generated in the `test/golden` directory instead of relative t Only the `Inter` font is loaded by default. +### Blue Screen Test + +All widgets should have a blue screen test. This uses a special theme that is all blue. It allows us to verify +that custom/inherited themes are being applied correctly. The resultant image should be completely blue if applied +correctly, hence the name. + +Example +```dart +testWidgets('blue screen', (tester) async { + await tester.pumpWidget( + TestScaffold.blue( // (1) Always use the TestScaffold.blue(...) constructor. + child: FSelectGroup( + style: TestScaffold.blueScreen.selectGroupStyle, // (2) Always use the TestScaffold.blueScreen theme. + children: [ + FSelectGroupItem.checkbox(value: 1), + ], + ), + ), + ); + + // (3) Always match against blue-screen.png. + await expectLater(find.byType(TestScaffold), matchesGoldenFile('blue-screen.png')); +}); +``` + ### Configuring Golden Test Threshold By default, `matchesGoldenFile(...)` has a 0.5% threshold. In other words, images that differ by 0.5% or less will be diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index 42ec6f9ec..ee986e5b9 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -48,6 +48,8 @@ * **Breaking** Change `FHeaderAction.icon` from `SvgAsset` to `Widget` - wrap the asset in ` FIcon` instead. +* **Breaking** Change `FSelectGroup.builder` parameters. + ### Fixes * Fix `FBottomNavigationBar` items hit region being smaller than intended. @@ -66,6 +68,8 @@ * Fix `FCheckbox`, `FRadio`, `FSelectGroup`, `FSwitch` and `FTextField` styles causing the widget inspector to crash. +* Fix `FSelectGroup` not applying correct style if a custom widget-specific style is given. + ## 0.5.1 diff --git a/forui/lib/src/widgets/select_group/select_group.dart b/forui/lib/src/widgets/select_group/select_group.dart index 6422c2e3d..73dfbe89e 100644 --- a/forui/lib/src/widgets/select_group/select_group.dart +++ b/forui/lib/src/widgets/select_group/select_group.dart @@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; import 'package:forui/forui.dart'; +import 'package:forui/src/widgets/select_group/select_group_item.dart'; /// A set of items that are treated as a single selection. /// @@ -15,7 +16,7 @@ import 'package:forui/forui.dart'; /// * https://forui.dev/docs/select-group for working examples. /// * [FSelectGroupStyle] for customizing a select group's appearance. class FSelectGroup extends FormField> { - static Widget _defaultErrorBuilder(BuildContext context, String error) => Text(error); + static Widget _errorBuilder(BuildContext context, String error) => Text(error); /// The controller. /// @@ -46,7 +47,7 @@ class FSelectGroup extends FormField> { this.style, this.label, this.description, - this.errorBuilder = _defaultErrorBuilder, + this.errorBuilder = _errorBuilder, super.onSaved, super.validator, super.initialValue, @@ -59,10 +60,10 @@ class FSelectGroup extends FormField> { builder: (field) { final state = field as _State; final groupStyle = style ?? state.context.theme.selectGroupStyle; - final labelState = switch (state) { - _ when !enabled => FLabelState.disabled, - _ when state.errorText != null => FLabelState.error, - _ => FLabelState.enabled, + final (labelState, error) = switch (state.errorText) { + _ when !enabled => (FLabelState.disabled, null), + final text? => (FLabelState.error, errorBuilder(state.context, text)), + null => (FLabelState.enabled, null), }; return FLabel( @@ -71,14 +72,15 @@ class FSelectGroup extends FormField> { style: groupStyle.labelStyle, label: label, description: description, - error: labelState == FLabelState.error ? errorBuilder(state.context, state.errorText!) : null, + error: error, child: Column( children: [ - for (final item in items) - item.builder( - state.context, - controller.select, - controller.contains(item.value), + for (final child in items) + FSelectGroupItemData( + controller: controller, + style: groupStyle, + selected: controller.contains(child.value), + child: child, ), ], ), @@ -96,6 +98,7 @@ class FSelectGroup extends FormField> { ..add(DiagnosticsProperty('style', style)) ..add(DiagnosticsProperty('controller', controller)) ..add(ObjectFlagProperty.has('errorBuilder', errorBuilder)) + ..add(DiagnosticsProperty('testing', (1, 2, 3))) ..add(IterableProperty('items', items)); } } diff --git a/forui/lib/src/widgets/select_group/select_group_item.dart b/forui/lib/src/widgets/select_group/select_group_item.dart index cc4e62bab..3e27247f2 100644 --- a/forui/lib/src/widgets/select_group/select_group_item.dart +++ b/forui/lib/src/widgets/select_group/select_group_item.dart @@ -1,136 +1,275 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; + import 'package:forui/forui.dart'; -/// An item that represents a selection in a [FSelectGroup]. -class FSelectGroupItem with Diagnosticable { - /// The value of the item. - T value; +@internal +class FSelectGroupItemData extends InheritedWidget { + static FSelectGroupItemData of(BuildContext context) { + final FSelectGroupItemData? result = context.dependOnInheritedWidgetOfExactType>(); + assert(result != null, 'No FSelectGroupItemData found in context'); + return result!; + } - /// The builder that creates the item's widget. - // ignore: avoid_positional_boolean_parameters - Widget Function(BuildContext context, void Function(T value, bool selected) onChange, bool selected) builder; + final FSelectGroupController controller; + final FSelectGroupStyle style; + final bool selected; - /// Creates a [FSelectGroupItem]. - FSelectGroupItem({ - required this.value, - required this.builder, + const FSelectGroupItemData({ + required this.controller, + required this.style, + required this.selected, + required super.child, + super.key, }); - /// Creates a [FSelectGroupItem] that wraps a [FCheckbox]. + @override + bool updateShouldNotify(FSelectGroupItemData old) => + controller != old.controller || style != old.style || selected != old.selected; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('controller', controller)) + ..add(DiagnosticsProperty('style', style)) + ..add(FlagProperty('selected', value: selected, ifTrue: 'selected')); + } +} + +/// A [FSelectGroupItem]'s state. +typedef FSelectGroupItemState = ({ + FSelectGroupController controller, + FSelectGroupStyle style, + T value, + bool selected +}); + +/// Represents a selection in a [FSelectGroup]. +abstract class FSelectGroupItem extends StatelessWidget { + /// The item's value. + final T value; + + /// Creates a [FSelectGroupItem]. + const factory FSelectGroupItem({ + required T value, + required ValueWidgetBuilder> builder, + Widget? child, + Key? key, + }) = _Builder; + + /// Creates a checkbox wrapped in a [FSelectGroupItem]. + // TODO: Making this const causes a false positive in the list_element_type_not_assignable lint when declared + // in a const list inside FSelectGroup. I still can't replicate this issue in other cases. + // ignore: prefer_const_constructors_in_immutables factory FSelectGroupItem.checkbox({ required T value, FCheckboxSelectGroupStyle? style, Widget? label, Widget? description, - String? semanticLabel, Widget? error, - bool enabled = true, - bool autofocus = false, + String? semanticLabel, + bool enabled, + bool autofocus, FocusNode? focusNode, ValueChanged? onFocusChange, Key? key, - }) => - FSelectGroupItem( - value: value, - builder: (context, onChange, selected) { - final computedStyle = style ?? context.theme.selectGroupStyle.checkboxStyle; - - return Padding( - padding: computedStyle.padding, - child: FCheckbox( - style: computedStyle, - label: label, - description: description, - semanticLabel: semanticLabel, - error: error, - value: selected, - onChange: (state) => onChange(value, state), - enabled: enabled, - autofocus: autofocus, - focusNode: focusNode, - onFocusChange: onFocusChange, - key: key, - ), - ); - }, - ); - - /// Creates a [FSelectGroupItem] that wraps a [FRadio]. + }) = _Checkbox; + + /// Creates a radio button wrapped in a [FSelectGroupItem]. + // TODO: Making this const causes a false positive in the list_element_type_not_assignable lint when declared + // in a const list inside FSelectGroup. I still can't replicate this issue in other cases. + // ignore: prefer_const_constructors_in_immutables factory FSelectGroupItem.radio({ required T value, FRadioSelectGroupStyle? style, Widget? label, Widget? description, - String? semanticLabel, Widget? error, - bool enabled = true, - bool autofocus = false, + String? semanticLabel, + bool enabled, + bool autofocus, FocusNode? focusNode, ValueChanged? onFocusChange, Key? key, - }) => - FSelectGroupItem( - value: value, - builder: (context, onChange, selected) { - final computedStyle = style ?? context.theme.selectGroupStyle.radioStyle; - - return Padding( - padding: computedStyle.padding, - child: FRadio( - style: computedStyle, - label: label, - description: description, - semanticLabel: semanticLabel, - error: error, - value: selected, - onChange: (state) => onChange(value, state), - enabled: enabled, - autofocus: autofocus, - focusNode: focusNode, - onFocusChange: onFocusChange, - key: key, - ), - ); - }, - ); + }) = _Radio; + + const FSelectGroupItem._({required this.value, super.key}); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('value', value)); + } +} + +class _Builder extends FSelectGroupItem { + final ValueWidgetBuilder> builder; + final Widget? child; + + const _Builder({ + required this.builder, + required super.value, + this.child, + super.key, + }) : super._(); + + @override + Widget build(BuildContext context) { + final FSelectGroupItemData(:controller, :style, :selected) = FSelectGroupItemData.of(context); + return builder(context, (controller: controller, style: style, value: value, selected: selected), child); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(ObjectFlagProperty.has('builder', builder)); + } +} + +class _Checkbox extends FSelectGroupItem { + final FCheckboxSelectGroupStyle? style; + final Widget? label; + final Widget? description; + final Widget? error; + final String? semanticLabel; + final bool enabled; + final bool autofocus; + final FocusNode? focusNode; + final ValueChanged? onFocusChange; + + const _Checkbox({ + required super.value, + this.style, + this.label, + this.description, + this.error, + this.semanticLabel, + this.enabled = true, + this.autofocus = false, + this.focusNode, + this.onFocusChange, + super.key, + }) : super._(); + + @override + Widget build(BuildContext context) { + final FSelectGroupItemData(:controller, :style) = FSelectGroupItemData.of(context); + final checkboxStyle = this.style ?? style.checkboxStyle; + + return Padding( + padding: checkboxStyle.padding, + child: FCheckbox( + style: checkboxStyle, + label: label, + description: description, + semanticLabel: semanticLabel, + error: error, + value: controller.contains(value), + onChange: (state) => controller.select(value, state), + enabled: enabled, + autofocus: autofocus, + focusNode: focusNode, + onFocusChange: onFocusChange, + key: key, + ), + ); + } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('value', value)) - ..add(ObjectFlagProperty.has('builder', builder)); + ..add(DiagnosticsProperty('style', style)) + ..add(StringProperty('semanticLabel', semanticLabel)) + ..add(FlagProperty('enabled', value: enabled, ifFalse: 'disabled')) + ..add(FlagProperty('autofocus', value: autofocus, ifFalse: 'not autofocus')) + ..add(DiagnosticsProperty('focusNode', focusNode)) + ..add(ObjectFlagProperty.has('onFocusChange', onFocusChange)); } +} + +class _Radio extends FSelectGroupItem { + final FRadioSelectGroupStyle? style; + final Widget? label; + final Widget? description; + final Widget? error; + final String? semanticLabel; + final bool enabled; + final bool autofocus; + final FocusNode? focusNode; + final ValueChanged? onFocusChange; + + const _Radio({ + required super.value, + this.style, + this.label, + this.description, + this.error, + this.semanticLabel, + this.enabled = true, + this.autofocus = false, + this.focusNode, + this.onFocusChange, + super.key, + }) : super._(); @override - bool operator ==(Object other) => - identical(this, other) || - other is FSelectGroupItem && runtimeType == other.runtimeType && value == other.value && builder == other.builder; + Widget build(BuildContext context) { + final FSelectGroupItemData(:controller, :style) = FSelectGroupItemData.of(context); + final radioStyle = this.style ?? style.radioStyle; + + return Padding( + padding: radioStyle.padding, + child: FRadio( + style: radioStyle, + label: label, + description: description, + semanticLabel: semanticLabel, + error: error, + value: controller.contains(value), + onChange: (state) => controller.select(value, state), + enabled: enabled, + autofocus: autofocus, + focusNode: focusNode, + onFocusChange: onFocusChange, + key: key, + ), + ); + } @override - int get hashCode => value.hashCode ^ builder.hashCode; + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('style', style)) + ..add(StringProperty('semanticLabel', semanticLabel)) + ..add(FlagProperty('enabled', value: enabled, ifFalse: 'disabled')) + ..add(FlagProperty('autofocus', value: autofocus, ifFalse: 'not autofocus')) + ..add(DiagnosticsProperty('focusNode', focusNode)) + ..add(ObjectFlagProperty.has('onFocusChange', onFocusChange)); + } } /// A [FSelectGroupItem.checkbox]'s style. class FCheckboxSelectGroupStyle extends FCheckboxStyle { - /// The padding around the checkbox. + /// The padding around the checkbox. Defaults to `EdgeInsets.symmetric(vertical: 2)`. final EdgeInsets padding; /// Creates a [FCheckboxSelectGroupStyle]. FCheckboxSelectGroupStyle({ - required this.padding, required super.labelLayoutStyle, required super.enabledStyle, required super.disabledStyle, required super.errorStyle, + this.padding = const EdgeInsets.symmetric(vertical: 2), }); /// Creates a [FCheckboxSelectGroupStyle] that inherits its properties from the given parameters. FCheckboxSelectGroupStyle.inherit({required FCheckboxStyle style}) - : padding = const EdgeInsets.symmetric(vertical: 2), - super( + : this( labelLayoutStyle: style.labelLayoutStyle, enabledStyle: style.enabledStyle, disabledStyle: style.disabledStyle, @@ -157,24 +296,23 @@ class FCheckboxSelectGroupStyle extends FCheckboxStyle { /// A [FSelectGroupItem.radio]'s style. class FRadioSelectGroupStyle extends FRadioStyle { - /// The padding around the radio. + /// The padding around the radio. Defaults to `EdgeInsets.symmetric(vertical: 2)`. final EdgeInsets padding; /// Creates a [FRadioSelectGroupStyle]. FRadioSelectGroupStyle({ - required this.padding, required super.animationDuration, required super.curve, required super.labelLayoutStyle, required super.enabledStyle, required super.disabledStyle, required super.errorStyle, + this.padding = const EdgeInsets.symmetric(vertical: 2), }); /// Creates a [FRadioSelectGroupStyle] that inherits its properties from the given parameters. FRadioSelectGroupStyle.inherit({required FRadioStyle style}) - : padding = const EdgeInsets.symmetric(vertical: 2), - super( + : this( animationDuration: style.animationDuration, curve: style.curve, labelLayoutStyle: style.labelLayoutStyle, diff --git a/forui/lib/widgets/select_group.dart b/forui/lib/widgets/select_group.dart index 9bcd67538..376709c42 100644 --- a/forui/lib/widgets/select_group.dart +++ b/forui/lib/widgets/select_group.dart @@ -7,4 +7,4 @@ library forui.widgets.select_group; export '../src/widgets/select_group/select_group.dart'; export '../src/widgets/select_group/select_group_controller.dart'; -export '../src/widgets/select_group/select_group_item.dart'; +export '../src/widgets/select_group/select_group_item.dart' hide FSelectGroupItemData; diff --git a/forui/test/golden/blue-screen.png b/forui/test/golden/blue-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..dca7a73f62eeba2420335948acc67f590f954630 GIT binary patch literal 20961 zcmeI4&1(};6vc09Y}06@idZZuq#wADBB6_dE1M9B1Eoj_SSZ4P*%Z1Erldtghpt)# zQQWu?>|fB2ML{g+fSaz|RZBp+E~d~;OARzQ$=ufgcj>B<-)wT{kr&9BbIyJD^5!i) z9M31>J#i69SPya&BAvEKM=aLvk9^v$M*WY7Gm+2A%J<}+e-m}GRw3qJ&tt_!k(5}u zOkw^*rJPB)Y4N=EKiv!WYGdE3eS>rH+si*cf1LAY1=rJ=7riEygGcQ{=EmJ}FPgaP z=9&qm|H2Iuj~2Z)6E9w#G@-nWTrzQL*p8Su$B!E*fCaWF7Z4HNqEx_gNfstUkV9Eo zC>L09u@F@%u!N?vP%2PxQHLrOD9`CyC>3Zd{I89Lk9Aq0dF)PNd9umY?AD^Mpu4GILgp(S&Jvt3P?y#PJ{u%IPbz>}wRZV;cf zVW59}5Fh-vQc9Kf8(l;D&DXN5egwR`^;e z!vQ`nm;^vT2nZplp*hzq_57L=x$4x<1!!(2-xk4gupBH$od7i`5ab|o1G&Mt0%|}F zr~x(j2w(;0(+b@3#*_M4Y;LCZ!`XS949;a6Ydd|7-Wv>$-(fg-h+v{*h7e~W#P#E& UuGQ(;lP~qz*8TC^O4eTZ3xhg!P5=M^ literal 0 HcmV?d00001 diff --git a/forui/test/src/test_scaffold.dart b/forui/test/src/test_scaffold.dart index 622772000..358829333 100644 --- a/forui/test/src/test_scaffold.dart +++ b/forui/test/src/test_scaffold.dart @@ -4,6 +4,26 @@ import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; class TestScaffold extends StatelessWidget { + static final blueScreen = FThemeData.inherit( + colorScheme: const FColorScheme( + brightness: Brightness.light, + disabledColorLightness: 0.1, + background: Color(0xFF03A9F4), + foreground: Color(0xFF03A9F4), + primary: Color(0xFF03A9F4), + primaryForeground: Color(0xFF03A9F4), + secondary: Color(0xFF03A9F4), + secondaryForeground: Color(0xFF03A9F4), + muted: Color(0xFF03A9F4), + mutedForeground: Color(0xFF03A9F4), + destructive: Color(0xFF03A9F4), + destructiveForeground: Color(0xFF03A9F4), + error: Color(0xFF03A9F4), + errorForeground: Color(0xFF03A9F4), + border: Color(0xFF03A9F4), + ), + ); + static List<(String, FThemeData, Color)> get themes => [ ('zinc-light', FThemes.zinc.light, const Color(0xFFD5FFFF)), ('zinc-dark', FThemes.zinc.dark, const Color(0xFF104963)), @@ -18,6 +38,11 @@ class TestScaffold extends StatelessWidget { const TestScaffold.app({required this.data, required this.child, this.background, super.key}) : wrapped = true; + TestScaffold.blue({required this.child, super.key}) + : data = blueScreen, + background = blueScreen.colorScheme.background, + wrapped = false; + @override Widget build(BuildContext context) { if (wrapped) { diff --git a/forui/test/src/widgets/select_group/select_group_controller_test.dart b/forui/test/src/widgets/select_group/select_group_controller_test.dart index 648b2c2ab..6b7d32ffe 100644 --- a/forui/test/src/widgets/select_group/select_group_controller_test.dart +++ b/forui/test/src/widgets/select_group/select_group_controller_test.dart @@ -3,56 +3,50 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:forui/forui.dart'; void main() { + late int count; + + setUp(() => count = 0); + group('FRadioSelectGroupController', () { test('should initialize with a single value', () { - bool notified = false; - - final controller = FRadioSelectGroupController(value: 1)..addListener(() => notified = true); + final controller = FRadioSelectGroupController(value: 1)..addListener(() => count++); expect(controller.values, equals({1})); - expect(notified, isFalse); + expect(count, 0); }); test('should initialize with an empty set when no value is provided', () { - bool notified = false; - - final controller = FRadioSelectGroupController()..addListener(() => notified = true); + final controller = FRadioSelectGroupController()..addListener(() => count++); expect(controller.values, isEmpty); - expect(notified, isFalse); + expect(count, 0); }); test('should change selection when a new value is selected', () { - bool notified = false; - final controller = FRadioSelectGroupController() - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(1, true); expect(controller.values, equals({1})); - expect(notified, isTrue); + expect(count, 1); }); test('should not change selection when the same value is selected', () { - bool notified = false; - final controller = FRadioSelectGroupController(value: 1) - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(1, true); expect(controller.values, equals({1})); - expect(notified, isFalse); + expect(count, 0); }); test('should not change selection when trying to deselect', () { - bool notified = false; - final controller = FRadioSelectGroupController(value: 1) - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(1, false); expect(controller.values, equals({1})); - expect(notified, isFalse); + expect(count, 0); }); test('contains(...)', () { @@ -70,56 +64,46 @@ void main() { group('FMultiSelectGroupController', () { test('should initialize with given values', () { - bool notified = false; - - final controller = FMultiSelectGroupController(values: {1, 2, 3})..addListener(() => notified = true); + final controller = FMultiSelectGroupController(values: {1, 2, 3})..addListener(() => count++); expect(controller.values, equals({1, 2, 3})); - expect(notified, isFalse); + expect(count, 0); }); test('should add a value when selected', () { - bool notified = false; - final controller = FMultiSelectGroupController() - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(1, true); expect(controller.values, equals({1})); - expect(notified, isTrue); + expect(count, 1); }); test('should remove a value when deselected', () { - bool notified = false; - final controller = FMultiSelectGroupController(values: {1, 2}) - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(1, false); expect(controller.values, equals({2})); - expect(notified, isTrue); + expect(count, 1); }); test('should not add a value when max limit is reached', () { - bool notified = false; - final controller = FMultiSelectGroupController(max: 2, values: {1, 2}) - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(3, true); expect(controller.values, equals({1, 2})); - expect(notified, isFalse); + expect(count, 0); }); test('should not remove a value when min limit is reached', () { - bool notified = false; - final controller = FMultiSelectGroupController(min: 2, values: {1, 2}) - ..addListener(() => notified = true) + ..addListener(() => count++) ..select(1, false); expect(controller.values, equals({1, 2})); - expect(notified, isFalse); + expect(count, 0); }); test('contains(...)', () { diff --git a/forui/test/src/widgets/select_group/select_group_golden_test.dart b/forui/test/src/widgets/select_group/select_group_golden_test.dart index 63a8b60c9..c423cd8b2 100644 --- a/forui/test/src/widgets/select_group/select_group_golden_test.dart +++ b/forui/test/src/widgets/select_group/select_group_golden_test.dart @@ -9,7 +9,42 @@ import 'package:forui/forui.dart'; import '../../test_scaffold.dart'; void main() { - group('FCard', () { + group('FCheckbox', () { + testWidgets('blue screen', (tester) async { + await tester.pumpWidget( + TestScaffold.blue( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 300, + child: FSelectGroup( + style: TestScaffold.blueScreen.selectGroupStyle, + label: const Text('Select Group'), + description: const Text('Select Group Description'), + controller: FMultiSelectGroupController(values: {1}), + items: [ + FSelectGroupItem.checkbox( + value: 1, + label: const Text('Checkbox 1'), + semanticLabel: 'Checkbox 1', + ), + FSelectGroupItem.radio( + value: 2, + label: const Text('Checkbox 2'), + semanticLabel: 'Checkbox 2', + ), + ], + ), + ), + ], + ), + ), + ); + + await expectLater(find.byType(TestScaffold), matchesGoldenFile('blue-screen.png')); + }); + for (final (name, theme, background) in TestScaffold.themes) { testWidgets('$name with checkbox', (tester) async { await tester.pumpWidget( @@ -49,10 +84,7 @@ void main() { ), ); - await expectLater( - find.byType(TestScaffold), - matchesGoldenFile('select-group/$name-checkbox.png'), - ); + await expectLater(find.byType(TestScaffold), matchesGoldenFile('select-group/$name-checkbox.png')); }); testWidgets('$name with checkbox error', (tester) async { @@ -94,10 +126,7 @@ void main() { ), ); - await expectLater( - find.byType(TestScaffold), - matchesGoldenFile('select-group/$name-checkbox-error.png'), - ); + await expectLater(find.byType(TestScaffold), matchesGoldenFile('select-group/$name-checkbox-error.png')); }); } }); @@ -142,10 +171,7 @@ void main() { ), ); - await expectLater( - find.byType(TestScaffold), - matchesGoldenFile('select-group/$name-radio.png'), - ); + await expectLater(find.byType(TestScaffold), matchesGoldenFile('select-group/$name-radio.png')); }); testWidgets('$name with radio error', (tester) async { @@ -187,10 +213,7 @@ void main() { ), ); - await expectLater( - find.byType(TestScaffold), - matchesGoldenFile('select-group/$name-radio-error.png'), - ); + await expectLater(find.byType(TestScaffold), matchesGoldenFile('select-group/$name-radio-error.png')); }); } }); diff --git a/samples/pubspec.lock b/samples/pubspec.lock index 33fd75b2c..448d1786e 100644 --- a/samples/pubspec.lock +++ b/samples/pubspec.lock @@ -761,10 +761,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.1.0" xml: dependency: transitive description: