// 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(['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() { setUp(() { debugResetSemanticsIdCounter(); }); testWidgets('CupertinoCheckbox semantics', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( CupertinoApp( home: Center(child: CupertinoCheckbox(value: false, onChanged: (bool? b) {})), ), ); expect( tester.getSemantics(find.byType(Focus).last), matchesSemantics( hasCheckedState: true, hasEnabledState: true, isEnabled: true, hasTapAction: true, hasFocusAction: true, isFocusable: true, ), ); await tester.pumpWidget( CupertinoApp( home: Center(child: CupertinoCheckbox(value: true, onChanged: (bool? b) {})), ), ); expect( tester.getSemantics(find.byType(Focus).last), matchesSemantics( hasCheckedState: true, hasEnabledState: true, isChecked: true, isEnabled: true, hasTapAction: true, hasFocusAction: true, isFocusable: true, ), ); await tester.pumpWidget( const CupertinoApp(home: Center(child: CupertinoCheckbox(value: false, onChanged: null))), ); expect( tester.getSemantics(find.byType(CupertinoCheckbox)), matchesSemantics( hasCheckedState: true, hasEnabledState: true, // isFocusable is delayed by 1 frame. isFocusable: true, hasFocusAction: true, ), ); await tester.pump(); // isFocusable should be false now after the 1 frame delay. expect( tester.getSemantics(find.byType(CupertinoCheckbox)), matchesSemantics(hasCheckedState: true, hasEnabledState: true), ); await tester.pumpWidget( const CupertinoApp(home: Center(child: CupertinoCheckbox(value: true, onChanged: null))), ); expect( tester.getSemantics(find.byType(CupertinoCheckbox)), matchesSemantics(hasCheckedState: true, hasEnabledState: true, isChecked: true), ); await tester.pumpWidget( const CupertinoApp( home: Center(child: CupertinoCheckbox(value: null, tristate: true, onChanged: null)), ), ); expect( tester.getSemantics(find.byType(CupertinoCheckbox)), matchesSemantics(hasCheckedState: true, hasEnabledState: true, isCheckStateMixed: true), ); await tester.pumpWidget( const CupertinoApp( home: Center(child: CupertinoCheckbox(value: true, tristate: true, onChanged: null)), ), ); expect( tester.getSemantics(find.byType(CupertinoCheckbox)), matchesSemantics(hasCheckedState: true, hasEnabledState: true, isChecked: true), ); await tester.pumpWidget( const CupertinoApp( home: Center(child: CupertinoCheckbox(value: false, tristate: true, onChanged: null)), ), ); expect( tester.getSemantics(find.byType(CupertinoCheckbox)), matchesSemantics(hasCheckedState: true, hasEnabledState: true), ); handle.dispose(); }); testWidgets('Can wrap CupertinoCheckbox with Semantics', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( CupertinoApp( home: Semantics( label: 'foo', textDirection: TextDirection.ltr, child: CupertinoCheckbox(value: false, onChanged: (bool? b) {}), ), ), ); expect( tester.getSemantics(find.byType(Focus).last), matchesSemantics( label: 'foo', textDirection: TextDirection.ltr, hasCheckedState: true, hasEnabledState: true, isEnabled: true, hasTapAction: true, hasFocusAction: true, isFocusable: true, ), ); handle.dispose(); }); testWidgets('CupertinoCheckbox tristate: true', (WidgetTester tester) async { bool? checkBoxValue; await tester.pumpWidget( CupertinoApp( home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( tristate: true, value: checkBoxValue, onChanged: (bool? value) { setState(() { checkBoxValue = value; }); }, ); }, ), ), ); expect(tester.widget(find.byType(CupertinoCheckbox)).value, null); await tester.tap(find.byType(CupertinoCheckbox)); await tester.pumpAndSettle(); expect(checkBoxValue, false); await tester.tap(find.byType(CupertinoCheckbox)); await tester.pumpAndSettle(); expect(checkBoxValue, true); await tester.tap(find.byType(CupertinoCheckbox)); await tester.pumpAndSettle(); expect(checkBoxValue, null); checkBoxValue = true; await tester.pumpAndSettle(); expect(checkBoxValue, true); checkBoxValue = null; await tester.pumpAndSettle(); expect(checkBoxValue, null); }); testWidgets('has semantics for tristate', (WidgetTester tester) async { final semantics = SemanticsTester(tester); await tester.pumpWidget( CupertinoApp( home: CupertinoCheckbox(tristate: true, value: null, onChanged: (bool? newValue) {}), ), ); expect( semantics.nodesWith( flags: [ SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, SemanticsFlag.isCheckStateMixed, ], actions: [SemanticsAction.focus, SemanticsAction.tap], ), hasLength(1), ); await tester.pumpWidget( CupertinoApp( home: CupertinoCheckbox(tristate: true, value: true, onChanged: (bool? newValue) {}), ), ); expect( semantics.nodesWith( flags: [ SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isChecked, SemanticsFlag.isFocusable, ], actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1), ); await tester.pumpWidget( CupertinoApp( home: CupertinoCheckbox(tristate: true, value: false, onChanged: (bool? newValue) {}), ), ); expect( semantics.nodesWith( flags: [ SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable, ], actions: [SemanticsAction.tap, SemanticsAction.focus], ), hasLength(1), ); semantics.dispose(); }); testWidgets('has semantic events', (WidgetTester tester) async { dynamic semanticEvent; bool? checkboxValue = false; tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler( SystemChannels.accessibility, (dynamic message) async { semanticEvent = message; }, ); final semanticsTester = SemanticsTester(tester); await tester.pumpWidget( CupertinoApp( home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( value: checkboxValue, onChanged: (bool? value) { setState(() { checkboxValue = value; }); }, ); }, ), ), ); await tester.tap(find.byType(CupertinoCheckbox)); final RenderObject object = tester.firstRenderObject(find.byType(CupertinoCheckbox)); expect(checkboxValue, true); expect(semanticEvent, { 'type': 'tap', 'nodeId': object.debugSemantics!.id, 'data': {}, }); expect(object.debugSemantics!.getSemanticsData().hasAction(SemanticsAction.tap), true); tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler( SystemChannels.accessibility, null, ); semanticsTester.dispose(); }); testWidgets('Checkbox can configure a semantic label', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoCheckbox( value: false, onChanged: (bool? b) {}, semanticLabel: 'checkbox', ), ), ), ); expect( tester.getSemantics(find.byType(Focus).last), matchesSemantics( hasCheckedState: true, hasEnabledState: true, isEnabled: true, hasTapAction: true, hasFocusAction: true, isFocusable: true, label: 'checkbox', ), ); // If wrapped with semantics, both the parent semantic label and the // checkbox's semantic label are used in annotation. await tester.pumpWidget( CupertinoApp( home: Semantics( label: 'foo', textDirection: TextDirection.ltr, child: CupertinoCheckbox( value: false, onChanged: (bool? b) {}, semanticLabel: 'checkbox', ), ), ), ); expect( tester.getSemantics(find.byType(Focus).last), matchesSemantics( label: 'foo\ncheckbox', textDirection: TextDirection.ltr, hasCheckedState: true, hasEnabledState: true, isEnabled: true, hasTapAction: true, hasFocusAction: true, isFocusable: true, ), ); handle.dispose(); }); testWidgets('Checkbox can be toggled by keyboard shortcuts', (WidgetTester tester) async { tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; bool? value = true; Widget buildApp({bool enabled = true}) { return CupertinoApp( home: Center( child: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( value: value, onChanged: enabled ? (bool? newValue) { setState(() { value = newValue; }); } : null, autofocus: true, ); }, ), ), ); } await tester.pumpWidget(buildApp()); await tester.pumpAndSettle(); await tester.sendKeyEvent(LogicalKeyboardKey.enter); await tester.pumpAndSettle(); // On web, switches don't respond to the enter key. expect(value, kIsWeb ? isTrue : isFalse); await tester.sendKeyEvent(LogicalKeyboardKey.enter); await tester.pumpAndSettle(); expect(value, isTrue); await tester.sendKeyEvent(LogicalKeyboardKey.space); await tester.pumpAndSettle(); expect(value, isFalse); await tester.sendKeyEvent(LogicalKeyboardKey.space); await tester.pumpAndSettle(); expect(value, isTrue); }); testWidgets('Checkbox respects shape and side on mobile', (WidgetTester tester) async { const roundedRectangleBorder = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(5)), ); const side = BorderSide(width: 4, color: Color(0xfff44336)); Widget buildApp() { return CupertinoApp( home: Center( child: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( value: false, onChanged: (bool? newValue) {}, shape: roundedRectangleBorder, side: side, ); }, ), ), ); } await tester.pumpWidget(buildApp()); await tester.pumpAndSettle(); expect( tester.widget(find.byType(CupertinoCheckbox)).shape, roundedRectangleBorder, ); expect(tester.widget(find.byType(CupertinoCheckbox)).side, side); expect( find.byType(CupertinoCheckbox), paints..drrect( color: const Color(0xfff44336), outer: RRect.fromLTRBR(15.0, 15.0, 29.0, 29.0, const Radius.circular(5)), inner: RRect.fromLTRBR(19.0, 19.0, 25.0, 25.0, const Radius.circular(1)), ), ); }, variant: TargetPlatformVariant.mobile()); testWidgets('Checkbox respects shape and side on desktop', (WidgetTester tester) async { const roundedRectangleBorder = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(5)), ); const side = BorderSide(width: 4, color: Color(0xfff44336)); Widget buildApp() { return CupertinoApp( home: Center( child: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( value: false, onChanged: (bool? newValue) {}, shape: roundedRectangleBorder, side: side, ); }, ), ), ); } await tester.pumpWidget(buildApp()); await tester.pumpAndSettle(); expect( tester.widget(find.byType(CupertinoCheckbox)).shape, roundedRectangleBorder, ); expect(tester.widget(find.byType(CupertinoCheckbox)).side, side); expect( find.byType(CupertinoCheckbox), paints..drrect( color: const Color(0xfff44336), outer: RRect.fromLTRBR(0.0, 0.0, 14.0, 14.0, const Radius.circular(5)), inner: RRect.fromLTRBR(4.0, 4.0, 10.0, 10.0, const Radius.circular(1)), ), ); }, variant: TargetPlatformVariant.desktop()); testWidgets('Checkbox respects tap target size', (WidgetTester tester) async { Widget buildApp() { return CupertinoApp( home: Center( child: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( value: false, onChanged: (bool? newValue) {}, tapTargetSize: const Size.square(20.0), ); }, ), ), ); } await tester.pumpWidget(buildApp()); await tester.pumpAndSettle(); expect( find.byType(CupertinoCheckbox), paints..drrect( outer: RRect.fromLTRBR(3.0, 3.0, 17.0, 17.0, const Radius.circular(4)), inner: RRect.fromLTRBR(4.0, 4.0, 16.0, 16.0, const Radius.circular(3)), ), ); }); testWidgets('Checkbox configures mouse cursor', (WidgetTester tester) async { Widget buildApp({MouseCursor? mouseCursor, bool enabled = true, bool value = true}) { return CupertinoApp( home: Center( child: CupertinoCheckbox( value: value, onChanged: enabled ? (bool? value) {} : null, mouseCursor: mouseCursor, ), ), ); } await tester.pumpWidget(buildApp(value: false)); final TestGesture gesture = await tester.createGesture( kind: PointerDeviceKind.mouse, pointer: 1, ); addTearDown(gesture.removePointer); await gesture.addPointer(location: tester.getCenter(find.byType(CupertinoCheckbox))); await tester.pump(); await gesture.moveTo(tester.getCenter(find.byType(CupertinoCheckbox))); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic, ); // Test disabled checkbox. await tester.pumpWidget(buildApp(enabled: false, value: false)); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic, ); // Test mouse cursor can be configured. await tester.pumpWidget(buildApp(mouseCursor: SystemMouseCursors.grab)); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.grab, ); }); testWidgets('Mouse cursor resolves in selected/focused/disabled states', ( WidgetTester tester, ) async { tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; final focusNode = FocusNode(debugLabel: 'Checkbox'); addTearDown(focusNode.dispose); Widget buildCheckbox({required bool value, required bool enabled}) { return CupertinoApp( home: Center( child: CupertinoCheckbox( value: value, onChanged: enabled ? (bool? value) {} : null, mouseCursor: const _CheckboxMouseCursor(), focusNode: focusNode, ), ), ); } // Test unselected case. await tester.pumpWidget(buildCheckbox(value: false, enabled: true)); final TestGesture gesture1 = await tester.createGesture( kind: PointerDeviceKind.mouse, pointer: 1, ); addTearDown(gesture1.removePointer); await gesture1.addPointer(location: tester.getCenter(find.byType(CupertinoCheckbox))); await tester.pump(); await gesture1.moveTo(tester.getCenter(find.byType(CupertinoCheckbox))); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic, ); // Test selected case. await tester.pumpWidget(buildCheckbox(value: true, enabled: true)); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click, ); // Test focused case. await tester.pumpWidget(buildCheckbox(value: true, enabled: true)); focusNode.requestFocus(); await tester.pump(); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.grab, ); // Test disabled case. await tester.pumpWidget(buildCheckbox(value: true, enabled: false)); expect( RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.forbidden, ); }); testWidgets('Checkbox default colors, and size in light mode', (WidgetTester tester) async { Widget buildCheckbox({bool value = true}) { return CupertinoApp( home: Center( child: RepaintBoundary( child: CupertinoCheckbox(value: value, onChanged: (bool? newValue) {}), ), ), ); } await tester.pumpWidget(buildCheckbox()); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.light_theme.selected.png'), ); await tester.pumpWidget(buildCheckbox(value: false)); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.light_theme.unselected.png'), ); }); testWidgets('Checkbox default colors, and size in dark mode', (WidgetTester tester) async { Widget buildCheckbox({bool value = true}) { return CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.dark), home: Center( child: RepaintBoundary( child: CupertinoCheckbox(value: value, onChanged: (bool? newValue) {}), ), ), ); } await tester.pumpWidget(buildCheckbox()); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.dark_theme.selected.png'), ); await tester.pumpWidget(buildCheckbox(value: false)); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.dark_theme.unselected.png'), ); }); testWidgets('Disabled checkbox default colors, and size in light mode', ( WidgetTester tester, ) async { Widget buildCheckbox({bool value = true}) { return CupertinoApp( home: Center( child: RepaintBoundary(child: CupertinoCheckbox(value: value, onChanged: null)), ), ); } await tester.pumpWidget(buildCheckbox()); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.disabled_light_theme.selected.png'), ); await tester.pumpWidget(buildCheckbox(value: false)); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.disabled_light_theme.unselected.png'), ); }); testWidgets('Disabled checkbox default colors, and size in dark mode', ( WidgetTester tester, ) async { Widget buildCheckbox({bool value = true}) { return CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.dark), home: Center( child: RepaintBoundary(child: CupertinoCheckbox(value: value, onChanged: null)), ), ); } await tester.pumpWidget(buildCheckbox()); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.disabled_dark_theme.selected.png'), ); await tester.pumpWidget(buildCheckbox(value: false)); await expectLater( find.byType(CupertinoCheckbox), matchesGoldenFile('checkbox.disabled_dark_theme.unselected.png'), ); }); testWidgets('Checkbox fill color resolves in enabled/disabled states', ( WidgetTester tester, ) async { const activeEnabledFillColor = Color(0xFF000001); const activeDisabledFillColor = Color(0xFF000002); Color getFillColor(Set states) { if (states.contains(WidgetState.disabled)) { return activeDisabledFillColor; } return activeEnabledFillColor; } final WidgetStateProperty fillColor = WidgetStateColor.resolveWith(getFillColor); Widget buildApp({required bool enabled}) { return CupertinoApp( home: CupertinoCheckbox( value: true, fillColor: fillColor, onChanged: enabled ? (bool? value) {} : null, ), ); } RenderBox getCheckboxRenderer() { return tester.renderObject(find.byType(CupertinoCheckbox)); } await tester.pumpWidget(buildApp(enabled: true)); await tester.pumpAndSettle(); expect(getCheckboxRenderer(), paints..path(color: activeEnabledFillColor)); await tester.pumpWidget(buildApp(enabled: false)); await tester.pumpAndSettle(); expect(getCheckboxRenderer(), paints..path(color: activeDisabledFillColor)); }); testWidgets('Checkbox fill color take precedence over active/inactive colors', ( WidgetTester tester, ) async { const activeEnabledFillColor = Color(0xFF000001); const activeDisabledFillColor = Color(0xFF000002); const activeColor = Color(0xFF000003); const inactiveColor = Color(0xFF000004); Color getFillColor(Set states) { if (states.contains(WidgetState.disabled)) { return activeDisabledFillColor; } return activeEnabledFillColor; } final WidgetStateProperty fillColor = WidgetStateColor.resolveWith(getFillColor); Widget buildApp({required bool enabled}) { return CupertinoApp( home: CupertinoCheckbox( value: true, fillColor: fillColor, activeColor: activeColor, inactiveColor: inactiveColor, onChanged: enabled ? (bool? value) {} : null, ), ); } RenderBox getCheckboxRenderer() { return tester.renderObject(find.byType(CupertinoCheckbox)); } await tester.pumpWidget(buildApp(enabled: true)); await tester.pumpAndSettle(); expect(getCheckboxRenderer(), paints..path(color: activeEnabledFillColor)); await tester.pumpWidget(buildApp(enabled: false)); await tester.pumpAndSettle(); expect(getCheckboxRenderer(), paints..path(color: activeDisabledFillColor)); }); testWidgets('Checkbox fill color resolves in hovered/focused states', ( WidgetTester tester, ) async { final focusNode = FocusNode(debugLabel: 'checkbox'); addTearDown(focusNode.dispose); tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; const hoveredFillColor = Color(0xFF000001); const focusedFillColor = Color(0xFF000002); const transparentColor = Color(0x00000000); Color getFillColor(Set states) { if (states.contains(WidgetState.hovered)) { return hoveredFillColor; } if (states.contains(WidgetState.focused)) { return focusedFillColor; } return transparentColor; } final WidgetStateProperty fillColor = WidgetStateColor.resolveWith(getFillColor); Widget buildApp({required bool enabled}) { return CupertinoApp( home: CupertinoCheckbox( focusNode: focusNode, value: enabled, fillColor: fillColor, onChanged: enabled ? (bool? value) {} : null, ), ); } RenderBox getCheckboxRenderer() { return tester.renderObject(find.byType(CupertinoCheckbox)); } await tester.pumpWidget(buildApp(enabled: true)); focusNode.requestFocus(); await tester.pumpAndSettle(); expect(focusNode.hasPrimaryFocus, isTrue); expect(getCheckboxRenderer(), paints..path(color: focusedFillColor)); // Start hovering. final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); await gesture.addPointer(); addTearDown(gesture.removePointer); await gesture.moveTo(tester.getCenter(find.byType(CupertinoCheckbox))); await tester.pumpAndSettle(); expect(getCheckboxRenderer(), paints..path(color: hoveredFillColor)); }); testWidgets('Checkbox configures focus color', (WidgetTester tester) async { const defaultCheckColor = Color(0xffffffff); const defaultActiveFillColor = Color(0xff007aff); final Color defaultFocusColor = HSLColor.fromColor(CupertinoColors.activeBlue.withOpacity(kCupertinoFocusColorOpacity)) .withLightness(kCupertinoFocusColorBrightness) .withSaturation(kCupertinoFocusColorSaturation) .toColor(); const testFocusColor = Color(0xffaabbcc); tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; final node = FocusNode(); addTearDown(node.dispose); Widget buildApp({Color? focusColor, bool autofocus = false, FocusNode? focusNode}) { return CupertinoApp( home: Center( child: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CupertinoCheckbox( value: true, onChanged: (bool? newValue) {}, autofocus: autofocus, focusNode: focusNode, focusColor: focusColor, ); }, ), ), ); } await tester.pumpWidget(buildApp(focusNode: node, autofocus: true)); await tester.pump(); expect(node.hasPrimaryFocus, isTrue); expect( find.byType(CupertinoCheckbox), paints ..path(color: defaultActiveFillColor) ..rrect() ..path(color: defaultCheckColor) ..path(color: defaultFocusColor, strokeWidth: 3.5, style: PaintingStyle.stroke), reason: 'Checkbox shows the correct focus color', ); await tester.pumpWidget(buildApp(focusColor: testFocusColor, focusNode: node, autofocus: true)); await tester.pump(); expect(node.hasPrimaryFocus, isTrue); expect( find.byType(CupertinoCheckbox), paints ..path(color: defaultActiveFillColor) ..rrect() ..path(color: defaultCheckColor) ..path(color: testFocusColor, strokeWidth: 3.5, style: PaintingStyle.stroke), reason: 'Checkbox can configure a focus color', ); }); testWidgets('Checkbox is darkened when pressed in light mode', (WidgetTester tester) async { const defaultCheckColor = Color(0xffffffff); const defaultActiveFillColor = Color(0xff007aff); const defaultInactiveFillColor = Color(0xffffffff); const pressedDarkShadow = Color(0x26ffffff); await tester.pumpWidget( CupertinoApp( home: Center(child: CupertinoCheckbox(value: false, onChanged: (_) {})), ), ); final TestGesture gesture1 = await tester.startGesture( tester.getCenter(find.byType(CupertinoCheckbox)), ); await tester.pump(); expect( find.byType(CupertinoCheckbox), paints ..path(color: defaultInactiveFillColor) ..drrect() ..path(color: pressedDarkShadow), reason: 'Inactive pressed checkbox is slightly darkened', ); await tester.pumpWidget( CupertinoApp( home: Center(child: CupertinoCheckbox(value: true, onChanged: (_) {})), ), ); final TestGesture gesture2 = await tester.startGesture( tester.getCenter(find.byType(CupertinoCheckbox)), ); await tester.pump(); expect( find.byType(CupertinoCheckbox), paints ..path(color: defaultActiveFillColor) ..rrect() ..path(color: defaultCheckColor) ..path(color: pressedDarkShadow), reason: 'Active pressed checkbox is slightly darkened', ); // Finish gestures to release resources. await gesture1.up(); await gesture2.up(); await tester.pump(); }); testWidgets('Checkbox is lightened when pressed in dark mode', (WidgetTester tester) async { const checkColor = Color(0xffdee8f8); const defaultActiveFillColor = Color(0xff3264d7); const defaultInactiveFillColor = Color(0xff000000); const pressedLightShadow = Color(0x26ffffff); await tester.pumpWidget( CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.dark), home: Center(child: CupertinoCheckbox(value: false, onChanged: (_) {})), ), ); final TestGesture gesture1 = await tester.startGesture( tester.getCenter(find.byType(CupertinoCheckbox)), ); await tester.pump(); expect( find.byType(CupertinoCheckbox), paints ..path(color: defaultInactiveFillColor) ..drrect() ..path(color: pressedLightShadow), reason: 'Inactive pressed checkbox is slightly lightened', ); await tester.pumpWidget( CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.dark), home: Center(child: CupertinoCheckbox(value: true, onChanged: (_) {})), ), ); final TestGesture gesture2 = await tester.startGesture( tester.getCenter(find.byType(CupertinoCheckbox)), ); await tester.pump(); expect( find.byType(CupertinoCheckbox), paints ..path(color: defaultActiveFillColor) ..rrect() ..path(color: checkColor) ..path(color: pressedLightShadow), reason: 'Active pressed checkbox is slightly lightened', ); // Finish gestures to release resources. await gesture1.up(); await gesture2.up(); await tester.pump(); }); testWidgets('CupertinoCheckbox does not crash at zero area', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( child: SizedBox.shrink(child: CupertinoCheckbox(value: true, onChanged: (_) {})), ), ), ); expect(tester.getSize(find.byType(CupertinoCheckbox)), Size.zero); }); } class _CheckboxMouseCursor extends WidgetStateMouseCursor { const _CheckboxMouseCursor(); @override MouseCursor resolve(Set states) { return const WidgetStateProperty.fromMap({ WidgetState.disabled: SystemMouseCursors.forbidden, WidgetState.focused: SystemMouseCursors.grab, WidgetState.selected: SystemMouseCursors.click, WidgetState.any: SystemMouseCursors.basic, }).resolve(states); } @override String get debugDescription => '_CheckboxMouseCursor()'; }