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.value,
|
||||||
required this.groupValue,
|
required this.groupValue,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
|
this.mouseCursor,
|
||||||
this.toggleable = false,
|
this.toggleable = false,
|
||||||
this.activeColor,
|
this.activeColor,
|
||||||
this.inactiveColor,
|
this.inactiveColor,
|
||||||
@ -121,6 +122,28 @@ class CupertinoRadio<T> extends StatefulWidget {
|
|||||||
/// ```
|
/// ```
|
||||||
final ValueChanged<T?>? onChanged;
|
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
|
/// Set to true if this radio button is allowed to be returned to an
|
||||||
/// indeterminate state by selecting it again when selected.
|
/// 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 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;
|
final bool? accessibilitySelected;
|
||||||
// Apple devices also use `selected` to annotate radio button's semantics
|
// Apple devices also use `selected` to annotate radio button's semantics
|
||||||
// state.
|
// state.
|
||||||
@ -258,6 +290,7 @@ class _CupertinoRadioState<T> extends State<CupertinoRadio<T>> with TickerProvid
|
|||||||
checked: widget._selected,
|
checked: widget._selected,
|
||||||
selected: accessibilitySelected,
|
selected: accessibilitySelected,
|
||||||
child: buildToggleable(
|
child: buildToggleable(
|
||||||
|
mouseCursor: effectiveMouseCursor,
|
||||||
focusNode: widget.focusNode,
|
focusNode: widget.focusNode,
|
||||||
autofocus: widget.autofocus,
|
autofocus: widget.autofocus,
|
||||||
onFocusChange: onFocusChange,
|
onFocusChange: onFocusChange,
|
||||||
@ -309,7 +342,6 @@ class _RadioPainter extends ToggleablePainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
|
|
||||||
final Offset center = (Offset.zero & size).center;
|
final Offset center = (Offset.zero & size).center;
|
||||||
|
|
||||||
final Paint paint = Paint()
|
final Paint paint = Paint()
|
||||||
|
@ -447,6 +447,7 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin, Togg
|
|||||||
value: widget.value,
|
value: widget.value,
|
||||||
groupValue: widget.groupValue,
|
groupValue: widget.groupValue,
|
||||||
onChanged: widget.onChanged,
|
onChanged: widget.onChanged,
|
||||||
|
mouseCursor: widget.mouseCursor,
|
||||||
toggleable: widget.toggleable,
|
toggleable: widget.toggleable,
|
||||||
activeColor: widget.activeColor,
|
activeColor: widget.activeColor,
|
||||||
focusColor: widget.focusColor,
|
focusColor: widget.focusColor,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -428,4 +429,129 @@ void main() {
|
|||||||
// Release pointer after widget disappeared.
|
// Release pointer after widget disappeared.
|
||||||
await gesture.up();
|
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