blob: 967f7bf637a9c477643333913f4a62ad2c54698e [file] [log] [blame] [edit]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// reduced-test-set:
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
library;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
void main() {
testWidgets('Radio control test', (WidgetTester tester) async {
final Key key = UniqueKey();
final List<int?> log = <int?>[];
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(key: key, value: 1, groupValue: 2, onChanged: log.add),
),
),
);
await tester.tap(find.byKey(key));
expect(log, equals(<int>[1]));
log.clear();
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
key: key,
value: 1,
groupValue: 1,
onChanged: log.add,
activeColor: CupertinoColors.systemGreen,
),
),
),
);
await tester.tap(find.byKey(key));
expect(log, isEmpty);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(key: key, value: 1, groupValue: 2, onChanged: null),
),
),
);
await tester.tap(find.byKey(key));
expect(log, isEmpty);
});
testWidgets('Radio can be toggled when toggleable is set', (WidgetTester tester) async {
final Key key = UniqueKey();
final List<int?> log = <int?>[];
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
key: key,
value: 1,
groupValue: 2,
onChanged: log.add,
toggleable: true,
),
),
),
);
await tester.tap(find.byKey(key));
expect(log, equals(<int>[1]));
log.clear();
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
key: key,
value: 1,
groupValue: 1,
onChanged: log.add,
toggleable: true,
),
),
),
);
await tester.tap(find.byKey(key));
expect(log, equals(<int?>[null]));
log.clear();
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
key: key,
value: 1,
groupValue: null,
onChanged: log.add,
toggleable: true,
),
),
),
);
await tester.tap(find.byKey(key));
expect(log, equals(<int>[1]));
});
testWidgets('Radio selected semantics - platform adaptive', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 1, onChanged: (int? i) {})),
),
);
final bool isApple =
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS;
expect(
semantics,
includesNodeWith(
flags: <SemanticsFlag>[
SemanticsFlag.isInMutuallyExclusiveGroup,
SemanticsFlag.hasCheckedState,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
SemanticsFlag.isChecked,
if (isApple) SemanticsFlag.hasSelectedState,
if (isApple) SemanticsFlag.isSelected,
],
actions: <SemanticsAction>[
SemanticsAction.tap,
if (defaultTargetPlatform != TargetPlatform.iOS) SemanticsAction.focus,
],
),
);
semantics.dispose();
}, variant: TargetPlatformVariant.all());
testWidgets('Radio semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 2, onChanged: (int? i) {})),
),
);
expect(
tester.getSemantics(find.byType(Focus).last),
matchesSemantics(
hasCheckedState: true,
hasEnabledState: true,
isEnabled: true,
hasTapAction: true,
hasFocusAction: true,
isFocusable: true,
isInMutuallyExclusiveGroup: true,
),
);
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 2, groupValue: 2, onChanged: (int? i) {})),
),
);
expect(
tester.getSemantics(find.byType(Focus).last),
matchesSemantics(
hasCheckedState: true,
hasEnabledState: true,
isEnabled: true,
hasTapAction: true,
hasFocusAction: true,
isFocusable: true,
isInMutuallyExclusiveGroup: true,
isChecked: true,
),
);
await tester.pumpWidget(
const CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 2, onChanged: null)),
),
);
expect(
tester.getSemantics(find.byType(Focus).last),
matchesSemantics(
hasCheckedState: true,
hasEnabledState: true,
isFocusable: true,
isInMutuallyExclusiveGroup: true,
hasFocusAction: true,
),
);
await tester.pump();
// Now the isFocusable should be gone.
expect(
tester.getSemantics(find.byType(Focus).last),
matchesSemantics(
hasCheckedState: true,
hasEnabledState: true,
isInMutuallyExclusiveGroup: true,
),
);
await tester.pumpWidget(
const CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 2, groupValue: 2, onChanged: null)),
),
);
expect(
tester.getSemantics(find.byType(Focus).last),
matchesSemantics(
hasCheckedState: true,
hasEnabledState: true,
isChecked: true,
isInMutuallyExclusiveGroup: true,
),
);
semantics.dispose();
});
testWidgets('has semantic events', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final Key key = UniqueKey();
dynamic semanticEvent;
int? radioValue = 2;
tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(
SystemChannels.accessibility,
(dynamic message) async {
semanticEvent = message;
},
);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
key: key,
value: 1,
groupValue: radioValue,
onChanged: (int? i) {
radioValue = i;
},
),
),
),
);
await tester.tap(find.byKey(key));
final RenderObject object = tester.firstRenderObject(find.byKey(key));
expect(radioValue, 1);
expect(semanticEvent, <String, dynamic>{
'type': 'tap',
'nodeId': object.debugSemantics!.id,
'data': <String, dynamic>{},
});
expect(object.debugSemantics!.getSemanticsData().hasAction(SemanticsAction.tap), true);
semantics.dispose();
tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(
SystemChannels.accessibility,
null,
);
});
testWidgets('Radio can be controlled by keyboard shortcuts', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
int? groupValue = 1;
const Key radioKey0 = Key('radio0');
const Key radioKey1 = Key('radio1');
const Key radioKey2 = Key('radio2');
final FocusNode focusNode2 = FocusNode(debugLabel: 'radio2');
addTearDown(focusNode2.dispose);
Widget buildApp({bool enabled = true}) {
return CupertinoApp(
home: Center(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return SizedBox(
width: 200,
height: 100,
child: Row(
children: <Widget>[
CupertinoRadio<int>(
key: radioKey0,
value: 0,
onChanged:
enabled
? (int? newValue) {
setState(() {
groupValue = newValue;
});
}
: null,
groupValue: groupValue,
autofocus: true,
),
CupertinoRadio<int>(
key: radioKey1,
value: 1,
onChanged:
enabled
? (int? newValue) {
setState(() {
groupValue = newValue;
});
}
: null,
groupValue: groupValue,
),
CupertinoRadio<int>(
key: radioKey2,
value: 2,
onChanged:
enabled
? (int? newValue) {
setState(() {
groupValue = newValue;
});
}
: null,
groupValue: groupValue,
focusNode: focusNode2,
),
],
),
);
},
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
// On web, radios don't respond to the enter key.
expect(groupValue, kIsWeb ? equals(1) : equals(0));
focusNode2.requestFocus();
await tester.pumpAndSettle();
await tester.sendKeyEvent(LogicalKeyboardKey.space);
await tester.pumpAndSettle();
expect(groupValue, equals(2));
});
testWidgets('Show a checkmark when useCheckmarkStyle is true', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 1, onChanged: (int? i) {})),
),
);
await tester.pumpAndSettle();
// Has no checkmark when useCheckmarkStyle is false
expect(
tester.firstRenderObject<RenderBox>(find.byType(CupertinoRadio<int>)),
isNot(paints..path()),
);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 2,
useCheckmarkStyle: true,
onChanged: (int? i) {},
),
),
),
);
await tester.pumpAndSettle();
// Has no checkmark when group value doesn't match the value
expect(
tester.firstRenderObject<RenderBox>(find.byType(CupertinoRadio<int>)),
isNot(paints..path()),
);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
useCheckmarkStyle: true,
onChanged: (int? i) {},
),
),
),
);
await tester.pumpAndSettle();
// Draws a path to show the checkmark when toggled on
expect(tester.firstRenderObject<RenderBox>(find.byType(CupertinoRadio<int>)), paints..path());
});
testWidgets('Do not crash when widget disappears while pointer is down', (
WidgetTester tester,
) async {
final Key key = UniqueKey();
Widget buildRadio(bool show) {
return CupertinoApp(
home: Center(
child:
show
? CupertinoRadio<bool>(
key: key,
value: true,
groupValue: false,
onChanged: (_) {},
)
: Container(),
),
);
}
await tester.pumpWidget(buildRadio(true));
final Offset center = tester.getCenter(find.byKey(key));
// Put a pointer down on the screen.
final TestGesture gesture = await tester.startGesture(center);
await tester.pump();
// While the pointer is down, the widget disappears.
await tester.pumpWidget(buildRadio(false));
expect(find.byKey(key), findsNothing);
// Release pointer after widget disappeared.
await gesture.up();
});
testWidgets('Radio has correct default active/inactive/fill/border colors in light mode', (
WidgetTester tester,
) async {
Widget buildRadio({required int value, required int groupValue}) {
return CupertinoApp(
home: Center(
child: RepaintBoundary(
child: CupertinoRadio<int>(
value: value,
groupValue: groupValue,
onChanged: (int? i) {},
),
),
),
);
}
await tester.pumpWidget(buildRadio(value: 1, groupValue: 1));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.light_theme.selected.png'),
);
await tester.pumpWidget(buildRadio(value: 1, groupValue: 2));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.light_theme.unselected.png'),
);
});
testWidgets('Radio has correct default active/inactive/fill/border colors in dark mode', (
WidgetTester tester,
) async {
Widget buildRadio({required int value, required int groupValue, bool enabled = true}) {
return CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: Center(
child: RepaintBoundary(
child: CupertinoRadio<int>(
value: value,
groupValue: groupValue,
onChanged: enabled ? (int? i) {} : null,
),
),
),
);
}
await tester.pumpWidget(buildRadio(value: 1, groupValue: 1));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.dark_theme.selected.png'),
);
await tester.pumpWidget(buildRadio(value: 1, groupValue: 2));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.dark_theme.unselected.png'),
);
});
testWidgets(
'Disabled radio has correct default active/inactive/fill/border colors in light mode',
(WidgetTester tester) async {
Widget buildRadio({required int value, required int groupValue}) {
return CupertinoApp(
home: Center(
child: RepaintBoundary(
child: CupertinoRadio<int>(value: value, groupValue: groupValue, onChanged: null),
),
),
);
}
await tester.pumpWidget(buildRadio(value: 1, groupValue: 1));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.disabled_light_theme.selected.png'),
);
await tester.pumpWidget(buildRadio(value: 1, groupValue: 2));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.disabled_light_theme.unselected.png'),
);
},
);
testWidgets(
'Disabled radio has correct default active/inactive/fill/border colors in dark mode',
(WidgetTester tester) async {
Widget buildRadio({required int value, required int groupValue}) {
return CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: Center(
child: RepaintBoundary(
child: CupertinoRadio<int>(value: value, groupValue: groupValue, onChanged: null),
),
),
);
}
await tester.pumpWidget(buildRadio(value: 1, groupValue: 1));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.disabled_dark_theme.selected.png'),
);
await tester.pumpWidget(buildRadio(value: 1, groupValue: 2));
await expectLater(
find.byType(CupertinoRadio<int>),
matchesGoldenFile('radio.disabled_dark_theme.unselected.png'),
);
},
);
testWidgets('Radio can set inactive/active/fill colors', (WidgetTester tester) async {
const Color inactiveBorderColor = Color(0xffd1d1d6);
const Color activeColor = Color(0x0000000A);
const Color fillColor = Color(0x0000000B);
const Color inactiveColor = Color(0x0000000C);
const double innerRadius = 2.975;
const double outerRadius = 7.0;
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 2,
onChanged: (int? i) {},
activeColor: activeColor,
fillColor: fillColor,
inactiveColor: inactiveColor,
),
),
),
);
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: inactiveColor)
..circle(radius: outerRadius, style: PaintingStyle.stroke, color: inactiveBorderColor),
reason: 'Unselected radio button should use inactive and border colors',
);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
onChanged: (int? i) {},
activeColor: activeColor,
fillColor: fillColor,
inactiveColor: inactiveColor,
),
),
),
);
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: activeColor)
..circle(radius: innerRadius, style: PaintingStyle.fill, color: fillColor),
reason: 'Selected radio button should use active and fill colors',
);
});
testWidgets('Radio is slightly darkened when pressed in light mode', (WidgetTester tester) async {
const Color activeInnerColor = Color(0xffffffff);
const Color activeOuterColor = Color(0xff007aff);
const Color inactiveBorderColor = Color(0xffd1d1d6);
const Color inactiveOuterColor = Color(0xffffffff);
const double innerRadius = 2.975;
const double outerRadius = 7.0;
const Color pressedShadowColor = Color(0x26ffffff);
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 2, onChanged: (int? i) {})),
),
);
final TestGesture gesture1 = await tester.startGesture(
tester.getCenter(find.byType(CupertinoRadio<int>)),
);
await tester.pump();
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: inactiveOuterColor)
..circle(radius: outerRadius, style: PaintingStyle.fill, color: pressedShadowColor)
..circle(radius: outerRadius, style: PaintingStyle.stroke, color: inactiveBorderColor),
reason: 'Unselected pressed radio button is slightly darkened',
);
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 2, groupValue: 2, onChanged: (int? i) {})),
),
);
final TestGesture gesture2 = await tester.startGesture(
tester.getCenter(find.byType(CupertinoRadio<int>)),
);
await tester.pump();
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: activeOuterColor)
..circle(radius: outerRadius, style: PaintingStyle.fill, color: pressedShadowColor)
..circle(radius: innerRadius, style: PaintingStyle.fill, color: activeInnerColor),
reason: 'Selected pressed radio button is slightly darkened',
);
// Finish gestures to release resources.
await gesture1.up();
await gesture2.up();
await tester.pump();
});
testWidgets('Radio is slightly lightened when pressed in dark mode', (WidgetTester tester) async {
const Color activeInnerColor = Color(0xffffffff);
const Color activeOuterColor = Color(0xff007aff);
const Color inactiveBorderColor = Color(0x40000000);
const double innerRadius = 2.975;
const double outerRadius = 7.0;
const Color pressedShadowColor = Color(0x26ffffff);
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 2, onChanged: (int? i) {})),
),
);
final TestGesture gesture1 = await tester.startGesture(
tester.getCenter(find.byType(CupertinoRadio<int>)),
);
await tester.pump();
expect(
find.byType(CupertinoRadio<int>),
paints
..path()
..circle(radius: outerRadius, style: PaintingStyle.fill, color: pressedShadowColor)
..circle(radius: outerRadius, style: PaintingStyle.stroke, color: inactiveBorderColor),
reason: 'Unselected pressed radio button is slightly lightened',
);
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 2, groupValue: 2, onChanged: (int? i) {})),
),
);
final TestGesture gesture2 = await tester.startGesture(
tester.getCenter(find.byType(CupertinoRadio<int>)),
);
await tester.pump();
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: activeOuterColor)
..circle(radius: outerRadius, style: PaintingStyle.fill, color: pressedShadowColor)
..circle(radius: innerRadius, style: PaintingStyle.fill, color: activeInnerColor),
reason: 'Selected pressed radio button is slightly lightened',
);
// Finish gestures to release resources.
await gesture1.up();
await gesture2.up();
await tester.pump();
});
testWidgets('Radio is focusable and has correct focus colors', (WidgetTester tester) async {
const Color activeInnerColor = Color(0xffffffff);
const Color activeOuterColor = Color(0xff007aff);
final Color defaultFocusColor =
HSLColor.fromColor(CupertinoColors.activeBlue.withOpacity(kCupertinoFocusColorOpacity))
.withLightness(kCupertinoFocusColorBrightness)
.withSaturation(kCupertinoFocusColorSaturation)
.toColor();
const double innerRadius = 2.975;
const double outerRadius = 7.0;
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final FocusNode node = FocusNode();
addTearDown(node.dispose);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
onChanged: (int? i) {},
focusNode: node,
autofocus: true,
),
),
),
);
await tester.pump();
expect(node.hasPrimaryFocus, isTrue);
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: activeOuterColor)
..circle(radius: innerRadius, style: PaintingStyle.fill, color: activeInnerColor)
..circle(strokeWidth: 3.0, style: PaintingStyle.stroke, color: defaultFocusColor),
reason: 'Radio is focusable and shows the default focus color',
);
});
testWidgets('Radio can configure a focus color', (WidgetTester tester) async {
const Color activeInnerColor = Color(0xffffffff);
const Color activeOuterColor = Color(0xff007aff);
const Color focusColor = Color(0x0000000A);
const double innerRadius = 2.975;
const double outerRadius = 7.0;
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final FocusNode node = FocusNode();
addTearDown(node.dispose);
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
onChanged: (int? i) {},
focusColor: focusColor,
focusNode: node,
autofocus: true,
),
),
),
);
await tester.pump();
expect(node.hasPrimaryFocus, isTrue);
expect(
find.byType(CupertinoRadio<int>),
paints
..circle(radius: outerRadius, style: PaintingStyle.fill, color: activeOuterColor)
..circle(radius: innerRadius, style: PaintingStyle.fill, color: activeInnerColor)
..circle(strokeWidth: 3.0, style: PaintingStyle.stroke, color: focusColor),
reason: 'Radio configures the color of the focus outline',
);
});
testWidgets('Radio configures mouse cursor', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
onChanged: (int? i) {},
mouseCursor: SystemMouseCursors.forbidden,
),
),
),
);
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
pointer: 1,
);
addTearDown(gesture.removePointer);
await gesture.addPointer(location: tester.getCenter(find.byType(CupertinoRadio<int>)));
await tester.pump();
await gesture.moveTo(tester.getCenter(find.byType(CupertinoRadio<int>)));
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.forbidden,
);
});
testWidgets('Mouse cursor resolves in disabled/hovered/focused states', (
WidgetTester tester,
) async {
final FocusNode focusNode = FocusNode(debugLabel: 'Radio');
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
onChanged: (int? i) {},
mouseCursor: const _RadioMouseCursor(),
focusNode: focusNode,
),
),
),
);
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
pointer: 1,
);
addTearDown(gesture.removePointer);
await gesture.addPointer(location: tester.getCenter(find.byType(CupertinoRadio<int>)));
await tester.pump();
// Test hovered case.
await gesture.moveTo(tester.getCenter(find.byType(CupertinoRadio<int>)));
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.click,
);
// Test focused case.
focusNode.requestFocus();
await tester.pump();
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.basic,
);
// Test disabled case.
await tester.pumpWidget(
const CupertinoApp(
home: Center(
child: CupertinoRadio<int>(
value: 1,
groupValue: 1,
onChanged: null,
mouseCursor: _RadioMouseCursor(),
),
),
),
);
await tester.pump();
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
SystemMouseCursors.forbidden,
);
focusNode.dispose();
});
testWidgets('Radio default mouse cursor', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: Center(child: CupertinoRadio<int>(value: 1, groupValue: 1, onChanged: (int? i) {})),
),
);
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
pointer: 1,
);
addTearDown(gesture.removePointer);
await gesture.addPointer(location: tester.getCenter(find.byType(CupertinoRadio<int>)));
await tester.pump();
await gesture.moveTo(tester.getCenter(find.byType(CupertinoRadio<int>)));
expect(
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
);
});
}
class _RadioMouseCursor extends WidgetStateMouseCursor {
const _RadioMouseCursor();
@override
MouseCursor resolve(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return SystemMouseCursors.forbidden;
}
if (states.contains(WidgetState.focused)) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
}
@override
String get debugDescription => '_RadioMouseCursor()';
}