Improve RadioListTile Callback Behavior Consistency (#31574)
This commit is contained in:
parent
be75fb36c5
commit
0c871b8528
@ -231,7 +231,7 @@ class RadioListTile<T> extends StatelessWidget {
|
|||||||
isThreeLine: isThreeLine,
|
isThreeLine: isThreeLine,
|
||||||
dense: dense,
|
dense: dense,
|
||||||
enabled: onChanged != null,
|
enabled: onChanged != null,
|
||||||
onTap: onChanged != null ? () { onChanged(value); } : null,
|
onTap: onChanged != null && !checked ? () { onChanged(value); } : null,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -34,20 +34,175 @@ void main() {
|
|||||||
expect(log, equals(<dynamic>[false, '-', false]));
|
expect(log, equals(<dynamic>[false, '-', false]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('RadioListTile control test', (WidgetTester tester) async {
|
testWidgets('RadioListTile should initialize according to groupValue', (WidgetTester tester) async {
|
||||||
final List<dynamic> log = <dynamic>[];
|
final List<int> values = <int>[0, 1, 2];
|
||||||
await tester.pumpWidget(wrap(
|
int selectedValue;
|
||||||
child: RadioListTile<bool>(
|
// Constructor parameters are required for [RadioListTile], but they are
|
||||||
value: true,
|
// irrelevant when searching with [find.byType].
|
||||||
groupValue: false,
|
final Type radioListTileType = const RadioListTile<int>(
|
||||||
onChanged: (bool value) { log.add(value); },
|
value: 0,
|
||||||
title: const Text('Hello'),
|
groupValue: 0,
|
||||||
|
onChanged: null,
|
||||||
|
).runtimeType;
|
||||||
|
|
||||||
|
List<RadioListTile<int>> generatedRadioListTiles;
|
||||||
|
List<RadioListTile<int>> findTiles() => find
|
||||||
|
.byType(radioListTileType)
|
||||||
|
.evaluate()
|
||||||
|
.map<RadioListTile<int>>((Element element) => element.widget)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
Widget buildFrame() {
|
||||||
|
return wrap(
|
||||||
|
child: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return Scaffold(
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: values.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) => RadioListTile<int>(
|
||||||
|
onChanged: (int value) {
|
||||||
|
setState(() { selectedValue = value; });
|
||||||
|
},
|
||||||
|
value: values[index],
|
||||||
|
groupValue: selectedValue,
|
||||||
|
title: Text(values[index].toString()),
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
await tester.tap(find.text('Hello'));
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
generatedRadioListTiles = findTiles();
|
||||||
|
|
||||||
|
expect(generatedRadioListTiles[0].checked, equals(false));
|
||||||
|
expect(generatedRadioListTiles[1].checked, equals(false));
|
||||||
|
expect(generatedRadioListTiles[2].checked, equals(false));
|
||||||
|
|
||||||
|
selectedValue = 1;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
generatedRadioListTiles = findTiles();
|
||||||
|
|
||||||
|
expect(generatedRadioListTiles[0].checked, equals(false));
|
||||||
|
expect(generatedRadioListTiles[1].checked, equals(true));
|
||||||
|
expect(generatedRadioListTiles[2].checked, equals(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('RadioListTile control tests', (WidgetTester tester) async {
|
||||||
|
final List<int> values = <int>[0, 1, 2];
|
||||||
|
int selectedValue;
|
||||||
|
// Constructor parameters are required for [Radio], but they are irrelevant
|
||||||
|
// when searching with [find.byType].
|
||||||
|
final Type radioType = const Radio<int>(
|
||||||
|
value: 0,
|
||||||
|
groupValue: 0,
|
||||||
|
onChanged: null,
|
||||||
|
).runtimeType;
|
||||||
|
final List<dynamic> log = <dynamic>[];
|
||||||
|
|
||||||
|
Widget buildFrame() {
|
||||||
|
return wrap(
|
||||||
|
child: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return Scaffold(
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: values.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) => RadioListTile<int>(
|
||||||
|
onChanged: (int value) {
|
||||||
|
log.add(value);
|
||||||
|
setState(() { selectedValue = value; });
|
||||||
|
},
|
||||||
|
value: values[index],
|
||||||
|
groupValue: selectedValue,
|
||||||
|
title: Text(values[index].toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for tapping between [Radio] and [ListTile]
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
await tester.tap(find.text('1'));
|
||||||
log.add('-');
|
log.add('-');
|
||||||
await tester.tap(find.byType(const Radio<bool>(value: false, groupValue: false, onChanged: null).runtimeType));
|
await tester.tap(find.byType(radioType).at(2));
|
||||||
expect(log, equals(<dynamic>[true, '-', true]));
|
expect(log, equals(<dynamic>[1, '-', 2]));
|
||||||
|
log.add('-');
|
||||||
|
await tester.tap(find.text('1'));
|
||||||
|
|
||||||
|
log.clear();
|
||||||
|
selectedValue = null;
|
||||||
|
|
||||||
|
// Tests for tapping across [Radio]s exclusively
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
await tester.tap(find.byType(radioType).at(1));
|
||||||
|
log.add('-');
|
||||||
|
await tester.tap(find.byType(radioType).at(2));
|
||||||
|
expect(log, equals(<dynamic>[1, '-', 2]));
|
||||||
|
|
||||||
|
log.clear();
|
||||||
|
selectedValue = null;
|
||||||
|
|
||||||
|
// Tests for tapping across [ListTile]s exclusively
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
await tester.tap(find.text('1'));
|
||||||
|
log.add('-');
|
||||||
|
await tester.tap(find.text('2'));
|
||||||
|
expect(log, equals(<dynamic>[1, '-', 2]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Selected RadioListTile should not trigger onChanged', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/30311
|
||||||
|
final List<int> values = <int>[0, 1, 2];
|
||||||
|
int selectedValue;
|
||||||
|
// Constructor parameters are required for [Radio], but they are irrelevant
|
||||||
|
// when searching with [find.byType].
|
||||||
|
final Type radioType = const Radio<int>(
|
||||||
|
value: 0,
|
||||||
|
groupValue: 0,
|
||||||
|
onChanged: null,
|
||||||
|
).runtimeType;
|
||||||
|
final List<dynamic> log = <dynamic>[];
|
||||||
|
|
||||||
|
Widget buildFrame() {
|
||||||
|
return wrap(
|
||||||
|
child: StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
|
return Scaffold(
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: values.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) => RadioListTile<int>(
|
||||||
|
onChanged: (int value) {
|
||||||
|
log.add(value);
|
||||||
|
setState(() { selectedValue = value; });
|
||||||
|
},
|
||||||
|
value: values[index],
|
||||||
|
groupValue: selectedValue,
|
||||||
|
title: Text(values[index].toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
await tester.tap(find.text('0'));
|
||||||
|
await tester.pump();
|
||||||
|
expect(log, equals(<int>[0]));
|
||||||
|
|
||||||
|
await tester.tap(find.text('0'));
|
||||||
|
expect(log, equals(<int>[0]));
|
||||||
|
|
||||||
|
await tester.tap(find.byType(radioType).at(0));
|
||||||
|
await tester.pump();
|
||||||
|
expect(log, equals(<int>[0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('SwitchListTile control test', (WidgetTester tester) async {
|
testWidgets('SwitchListTile control test', (WidgetTester tester) async {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user