Add mouse cursor property to CupertinoRadio
(#149681)
Adds `mouseCursor` property in `Radio` to `CupertinoRadio` and `Radio.adaptive`. The `mouseCursor` property added is of type `MouseCursor` and not `WidgetStateProperty<MouseCursor>` to match `Radio`'s `mouseCursor`.
This commit is contained in:
parent
b1f9d7131c
commit
3db2ece735
@ -71,6 +71,7 @@ class CupertinoRadio<T> extends StatefulWidget {
|
||||
required this.value,
|
||||
required this.groupValue,
|
||||
required this.onChanged,
|
||||
this.mouseCursor,
|
||||
this.toggleable = false,
|
||||
this.activeColor,
|
||||
this.inactiveColor,
|
||||
@ -121,6 +122,28 @@ class CupertinoRadio<T> extends StatefulWidget {
|
||||
/// ```
|
||||
final ValueChanged<T?>? onChanged;
|
||||
|
||||
/// The cursor for a mouse pointer when it enters or is hovering over the
|
||||
/// widget.
|
||||
///
|
||||
/// If [mouseCursor] is a [WidgetStateMouseCursor],
|
||||
/// [WidgetStateMouseCursor.resolve] is used for the following [WidgetState]s:
|
||||
///
|
||||
/// * [WidgetState.selected].
|
||||
/// * [WidgetState.hovered].
|
||||
/// * [WidgetState.focused].
|
||||
/// * [WidgetState.disabled].
|
||||
///
|
||||
/// If null, then [SystemMouseCursors.basic] is used when this radio button is disabled.
|
||||
/// When this radio button is enabled, [SystemMouseCursors.click] is used on Web, and
|
||||
/// [SystemMouseCursors.basic] is used on other platforms.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [WidgetStateMouseCursor], a [MouseCursor] that implements
|
||||
/// `WidgetStateProperty` which is used in APIs that need to accept
|
||||
/// either a [MouseCursor] or a [WidgetStateProperty<MouseCursor>].
|
||||
final MouseCursor? mouseCursor;
|
||||
|
||||
/// Set to true if this radio button is allowed to be returned to an
|
||||
/// indeterminate state by selecting it again when selected.
|
||||
///
|
||||
@ -239,6 +262,15 @@ class _CupertinoRadioState<T> extends State<CupertinoRadio<T>> with TickerProvid
|
||||
|
||||
final Color effectiveFillColor = widget.fillColor ?? CupertinoColors.white;
|
||||
|
||||
final WidgetStateProperty<MouseCursor> effectiveMouseCursor =
|
||||
WidgetStateProperty.resolveWith<MouseCursor>((Set<WidgetState> states) {
|
||||
return WidgetStateProperty.resolveAs<MouseCursor?>(widget.mouseCursor, states)
|
||||
?? (states.contains(WidgetState.disabled)
|
||||
? SystemMouseCursors.basic
|
||||
: kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic
|
||||
);
|
||||
});
|
||||
|
||||
final bool? accessibilitySelected;
|
||||
// Apple devices also use `selected` to annotate radio button's semantics
|
||||
// state.
|
||||
@ -258,6 +290,7 @@ class _CupertinoRadioState<T> extends State<CupertinoRadio<T>> with TickerProvid
|
||||
checked: widget._selected,
|
||||
selected: accessibilitySelected,
|
||||
child: buildToggleable(
|
||||
mouseCursor: effectiveMouseCursor,
|
||||
focusNode: widget.focusNode,
|
||||
autofocus: widget.autofocus,
|
||||
onFocusChange: onFocusChange,
|
||||
@ -309,7 +342,6 @@ class _RadioPainter extends ToggleablePainter {
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
|
||||
final Offset center = (Offset.zero & size).center;
|
||||
|
||||
final Paint paint = Paint()
|
||||
|
@ -447,6 +447,7 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin, Togg
|
||||
value: widget.value,
|
||||
groupValue: widget.groupValue,
|
||||
onChanged: widget.onChanged,
|
||||
mouseCursor: widget.mouseCursor,
|
||||
toggleable: widget.toggleable,
|
||||
activeColor: widget.activeColor,
|
||||
focusColor: widget.focusColor,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
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';
|
||||
@ -428,4 +429,129 @@ void main() {
|
||||
// Release pointer after widget disappeared.
|
||||
await gesture.up();
|
||||
});
|
||||
|
||||
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()';
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user