Skip to content

Commit abea646

Browse files
Panteclaude
andauthored
Fix FSelect onChange not firing when clearing via clear button (#950)
Closes #947 Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 4e1c879 commit abea646

6 files changed

Lines changed: 113 additions & 1 deletion

File tree

forui/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ We've adjusted `FDialog`'s styling to be more visually pleasing on touch devices
6565
* Fix portal not repositioning when the child widget changes size.
6666

6767

68+
## 0.20.4
69+
70+
### `FSelect`
71+
* Fix `onChange` not being called when clearing via the clear button.
72+
73+
6874
## 0.20.3
6975

7076
### `FToaster`

forui/lib/src/widgets/select/single/select.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ abstract class _State<S extends FSelect<T>, T> extends State<S> with TickerProvi
743743
return;
744744
}
745745

746+
final previous = _controller.value;
746747
try {
747748
_mutating = true;
748749
if (_textController.text.isEmpty) {
@@ -751,6 +752,12 @@ abstract class _State<S extends FSelect<T>, T> extends State<S> with TickerProvi
751752
} finally {
752753
_mutating = false;
753754
}
755+
756+
if (previous != _controller.value) {
757+
if (widget.control case FSelectManagedControl(:final onChange?)) {
758+
onChange(_controller.value);
759+
}
760+
}
754761
}
755762

756763
void _updateTextController() {

forui/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: forui
22
description: Beautifully designed, minimalistic widgets for desktop & touch devices.
3-
version: 0.20.3
3+
version: 0.20.4
44
homepage: https://forui.dev/
55
documentation: https://forui.dev/docs
66
repository: https://github.com/duobaseio/forui/tree/main/forui

forui/test/src/widgets/date_field/input/input_date_field_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,31 @@ void main() {
4545

4646
expect(changed, DateTime.utc(2025, 1, 15));
4747
});
48+
49+
testWidgets('onChange called when clearing via clear button', (tester) async {
50+
final values = <DateTime?>[];
51+
52+
await tester.pumpWidget(
53+
TestScaffold.app(
54+
locale: const Locale('en', 'SG'),
55+
child: FDateField.input(
56+
key: key,
57+
clearable: true,
58+
control: .managed(onChange: values.add),
59+
),
60+
),
61+
);
62+
63+
await tester.enterText(find.byKey(key), '15/01/2025');
64+
await tester.pumpAndSettle();
65+
66+
expect(values, [DateTime.utc(2025, 1, 15)]);
67+
68+
await tester.tap(find.bySemanticsLabel('Clear'));
69+
await tester.pumpAndSettle();
70+
71+
expect(values, [DateTime.utc(2025, 1, 15), null]);
72+
});
4873
});
4974

5075
group('lifted', () {

forui/test/src/widgets/select/single/select_test.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,35 @@ void main() {
153153
await tester.pumpWidget(const SizedBox());
154154
});
155155

156+
testWidgets('onChange called when clearing via clear button', (tester) async {
157+
String? value = 'A';
158+
final values = <String?>[];
159+
160+
await tester.pumpWidget(
161+
TestScaffold.app(
162+
child: StatefulBuilder(
163+
builder: (context, setState) => FSelect<String>(
164+
key: key,
165+
clearable: true,
166+
control: .lifted(
167+
value: value,
168+
onChange: (v) => setState(() {
169+
value = v;
170+
values.add(v);
171+
}),
172+
),
173+
items: letters,
174+
),
175+
),
176+
),
177+
);
178+
179+
await tester.tap(find.bySemanticsLabel('Clear'));
180+
await tester.pumpAndSettle();
181+
182+
expect(values, [null]);
183+
});
184+
156185
testWidgets('showing popover does not cause error', (tester) async {
157186
String? value;
158187

@@ -199,6 +228,26 @@ void main() {
199228

200229
expect(changedValue, 'A');
201230
});
231+
232+
testWidgets('onChange called when clearing via clear button', (tester) async {
233+
final values = <String?>[];
234+
235+
await tester.pumpWidget(
236+
TestScaffold.app(
237+
child: FSelect<String>(
238+
key: key,
239+
clearable: true,
240+
control: .managed(initial: 'A', onChange: values.add),
241+
items: letters,
242+
),
243+
),
244+
);
245+
246+
await tester.tap(find.bySemanticsLabel('Clear'));
247+
await tester.pumpAndSettle();
248+
249+
expect(values, [null]);
250+
});
202251
});
203252

204253
group('form', () {

forui/test/src/widgets/time_field/input/input_time_field_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,31 @@ void main() {
3232

3333
expect(changedValue, const FTime(0, 1));
3434
});
35+
36+
testWidgets('onChange called when clearing via clear button', (tester) async {
37+
final values = <FTime?>[];
38+
39+
await tester.pumpWidget(
40+
TestScaffold.app(
41+
locale: const Locale('en', 'SG'),
42+
child: FTimeField(
43+
key: key,
44+
clearable: true,
45+
control: .managed(onChange: values.add),
46+
),
47+
),
48+
);
49+
50+
await tester.enterText(find.byKey(key), '12:30 pm');
51+
await tester.pumpAndSettle();
52+
53+
expect(values, [const FTime(12, 30)]);
54+
55+
await tester.tap(find.bySemanticsLabel('Clear'));
56+
await tester.pumpAndSettle();
57+
58+
expect(values, [const FTime(12, 30), null]);
59+
});
3560
});
3661

3762
group('lifted', () {

0 commit comments

Comments
 (0)