1210 lines
37 KiB
Dart
1210 lines
37 KiB
Dart
// 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.
|
|
|
|
// 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/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
|
|
|
void main() {
|
|
testWidgetsWithLeakTracking('Switch can toggle on tap', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(value, isFalse);
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('CupertinoSwitch can be toggled by keyboard shortcuts', (WidgetTester tester) async {
|
|
bool value = true;
|
|
Widget buildApp({bool enabled = true}) {
|
|
return CupertinoApp(
|
|
home: CupertinoPageScaffold(
|
|
child: Center(
|
|
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
|
return CupertinoSwitch(
|
|
value: value,
|
|
onChanged: enabled ? (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
} : null,
|
|
);
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
await tester.pumpWidget(buildApp());
|
|
await tester.pumpAndSettle();
|
|
expect(value, isTrue);
|
|
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
|
await tester.pumpAndSettle();
|
|
await tester.sendKeyEvent(LogicalKeyboardKey.space);
|
|
await tester.pumpAndSettle();
|
|
expect(value, isFalse);
|
|
await tester.sendKeyEvent(LogicalKeyboardKey.space);
|
|
await tester.pumpAndSettle();
|
|
expect(value, isTrue);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch emits light haptic vibration on tap', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
|
|
final List<MethodCall> log = <MethodCall>[];
|
|
|
|
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
|
|
log.add(methodCall);
|
|
return null;
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(1));
|
|
expect(log.single, isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact'));
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
|
|
|
|
testWidgetsWithLeakTracking('Using other widgets that rebuild the switch will not cause vibrations', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
final Key switchKey2 = UniqueKey();
|
|
bool value = false;
|
|
bool value2 = false;
|
|
final List<MethodCall> log = <MethodCall>[];
|
|
|
|
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
|
|
log.add(methodCall);
|
|
return null;
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: Column(
|
|
children: <Widget>[
|
|
CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
CupertinoSwitch(
|
|
key: switchKey2,
|
|
value: value2,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value2 = newValue;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(1));
|
|
expect(log[0], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact'));
|
|
|
|
await tester.tap(find.byKey(switchKey2));
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(2));
|
|
expect(log[1], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact'));
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(3));
|
|
expect(log[2], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact'));
|
|
|
|
await tester.tap(find.byKey(switchKey2));
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(4));
|
|
expect(log[3], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact'));
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
|
|
|
|
testWidgetsWithLeakTracking('Haptic vibration triggers on drag', (WidgetTester tester) async {
|
|
bool value = false;
|
|
final List<MethodCall> log = <MethodCall>[];
|
|
|
|
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
|
|
log.add(methodCall);
|
|
return null;
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
|
|
expect(value, isTrue);
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(1));
|
|
expect(log[0], isMethodCall('HapticFeedback.vibrate', arguments: 'HapticFeedbackType.lightImpact'));
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
|
|
|
|
testWidgetsWithLeakTracking('No haptic vibration triggers from a programmatic value change', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
|
|
final List<MethodCall> log = <MethodCall>[];
|
|
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
|
|
log.add(methodCall);
|
|
return null;
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: Column(
|
|
children: <Widget>[
|
|
CupertinoButton(
|
|
child: const Text('Button'),
|
|
onPressed: () {
|
|
setState(() {
|
|
value = !value;
|
|
});
|
|
},
|
|
),
|
|
CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(value, isFalse);
|
|
|
|
await tester.tap(find.byType(CupertinoButton));
|
|
expect(value, isTrue);
|
|
await tester.pump();
|
|
|
|
expect(log, hasLength(0));
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
|
|
|
|
testWidgetsWithLeakTracking('Switch can drag (LTR)', (WidgetTester tester) async {
|
|
bool value = false;
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(value, isFalse);
|
|
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(-48.0, 0.0));
|
|
|
|
expect(value, isFalse);
|
|
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(48.0, 0.0));
|
|
|
|
expect(value, isTrue);
|
|
|
|
await tester.pump();
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(48.0, 0.0));
|
|
|
|
expect(value, isTrue);
|
|
|
|
await tester.pump();
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(-48.0, 0.0));
|
|
|
|
expect(value, isFalse);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch can drag with dragStartBehavior', (WidgetTester tester) async {
|
|
bool value = false;
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(value, isFalse);
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(-30.0, 0.0));
|
|
expect(value, isFalse);
|
|
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
|
|
expect(value, isTrue);
|
|
await tester.pump();
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
|
|
expect(value, isTrue);
|
|
await tester.pump();
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(-30.0, 0.0));
|
|
expect(value, isFalse);
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
final Rect switchRect = tester.getRect(find.byType(CupertinoSwitch));
|
|
expect(value, isFalse);
|
|
|
|
TestGesture gesture = await tester.startGesture(switchRect.center);
|
|
// We have to execute the drag in two frames because the first update will
|
|
// just set the start position.
|
|
await gesture.moveBy(const Offset(20.0, 0.0));
|
|
await gesture.moveBy(const Offset(20.0, 0.0));
|
|
expect(value, isFalse);
|
|
await gesture.up();
|
|
expect(value, isTrue);
|
|
await tester.pump();
|
|
|
|
gesture = await tester.startGesture(switchRect.center);
|
|
await gesture.moveBy(const Offset(20.0, 0.0));
|
|
await gesture.moveBy(const Offset(20.0, 0.0));
|
|
expect(value, isTrue);
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
gesture = await tester.startGesture(switchRect.center);
|
|
await gesture.moveBy(const Offset(-20.0, 0.0));
|
|
await gesture.moveBy(const Offset(-20.0, 0.0));
|
|
expect(value, isTrue);
|
|
await gesture.up();
|
|
expect(value, isFalse);
|
|
await tester.pump();
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch can drag (RTL)', (WidgetTester tester) async {
|
|
bool value = false;
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(value, isFalse);
|
|
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
|
|
|
|
expect(value, isFalse);
|
|
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(-30.0, 0.0));
|
|
|
|
expect(value, isTrue);
|
|
|
|
await tester.pump();
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(-30.0, 0.0));
|
|
|
|
expect(value, isTrue);
|
|
|
|
await tester.pump();
|
|
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
|
|
|
|
expect(value, isFalse);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('can veto switch dragging result', (WidgetTester tester) async {
|
|
bool value = false;
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Material(
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
value: value,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = value || newValue;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
// Move a little to the right, not past the middle.
|
|
TestGesture gesture = await tester.startGesture(tester.getRect(find.byType(CupertinoSwitch)).center);
|
|
await gesture.moveBy(const Offset(kTouchSlop + 0.1, 0.0));
|
|
await tester.pump();
|
|
await gesture.moveBy(const Offset(-kTouchSlop + 5.1, 0.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pump();
|
|
expect(value, isFalse);
|
|
// ignore: avoid_dynamic_calls
|
|
final CurvedAnimation position = (tester.state(find.byType(CupertinoSwitch)) as dynamic).position as CurvedAnimation;
|
|
expect(position.value, lessThan(0.5));
|
|
await tester.pump();
|
|
await tester.pumpAndSettle();
|
|
expect(value, isFalse);
|
|
expect(position.value, 0);
|
|
|
|
// Move past the middle.
|
|
gesture = await tester.startGesture(tester.getRect(find.byType(CupertinoSwitch)).center);
|
|
await gesture.moveBy(const Offset(kTouchSlop + 0.1, 0.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pump();
|
|
expect(value, isTrue);
|
|
expect(position.value, greaterThan(0.5));
|
|
|
|
await tester.pump();
|
|
await tester.pumpAndSettle();
|
|
expect(value, isTrue);
|
|
expect(position.value, 1.0);
|
|
|
|
// Now move back to the left, the revert animation should play.
|
|
gesture = await tester.startGesture(tester.getRect(find.byType(CupertinoSwitch)).center);
|
|
await gesture.moveBy(const Offset(-kTouchSlop - 0.1, 0.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pump();
|
|
expect(value, isTrue);
|
|
expect(position.value, lessThan(0.5));
|
|
|
|
await tester.pump();
|
|
await tester.pumpAndSettle();
|
|
expect(value, isTrue);
|
|
expect(position.value, 1.0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch is translucent when disabled', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(Opacity), findsOneWidget);
|
|
expect(tester.widget<Opacity>(find.byType(Opacity).first).opacity, 0.5);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch is using track color when set', (WidgetTester tester) async {
|
|
const Color trackColor = Color(0xFF00FF00);
|
|
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
trackColor: trackColor,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(CupertinoSwitch), findsOneWidget);
|
|
expect(tester.widget<CupertinoSwitch>(find.byType(CupertinoSwitch)).trackColor, trackColor);
|
|
expect(find.byType(CupertinoSwitch), paints..rrect(color: trackColor));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch is using default thumb color', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
onChanged: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(CupertinoSwitch), findsOneWidget);
|
|
expect(tester.widget<CupertinoSwitch>(find.byType(CupertinoSwitch)).thumbColor, null);
|
|
expect(
|
|
find.byType(CupertinoSwitch),
|
|
paints
|
|
..rrect()
|
|
..rrect()
|
|
..rrect()
|
|
..rrect()
|
|
..rrect(color: CupertinoColors.white),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch is using thumb color when set', (WidgetTester tester) async {
|
|
const Color thumbColor = Color(0xFF000000);
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
thumbColor: thumbColor,
|
|
onChanged: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(CupertinoSwitch), findsOneWidget);
|
|
expect(tester.widget<CupertinoSwitch>(find.byType(CupertinoSwitch)).thumbColor, thumbColor);
|
|
expect(
|
|
find.byType(CupertinoSwitch),
|
|
paints
|
|
..rrect()
|
|
..rrect()
|
|
..rrect()
|
|
..rrect()
|
|
..rrect(color: thumbColor),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch is opaque when enabled', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(Opacity), findsOneWidget);
|
|
expect(tester.widget<Opacity>(find.byType(Opacity).first).opacity, 1.0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch turns translucent after becoming disabled', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(Opacity), findsOneWidget);
|
|
expect(tester.widget<Opacity>(find.byType(Opacity).first).opacity, 0.5);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch turns opaque after becoming enabled', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: false,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(Opacity), findsOneWidget);
|
|
expect(tester.widget<Opacity>(find.byType(Opacity).first).opacity, 1.0);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch renders correctly before, during, and after being tapped', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byKey(switchKey),
|
|
matchesGoldenFile('switch.tap.off.png'),
|
|
);
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
|
|
// Kick off animation, then advance to intermediate frame.
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
await expectLater(
|
|
find.byKey(switchKey),
|
|
matchesGoldenFile('switch.tap.turningOn.png'),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
await expectLater(
|
|
find.byKey(switchKey),
|
|
matchesGoldenFile('switch.tap.on.png'),
|
|
);
|
|
});
|
|
|
|
PaintPattern onLabelPaintPattern({
|
|
required int alpha,
|
|
bool isRtl = false,
|
|
}) =>
|
|
paints
|
|
..rect(
|
|
rect: Rect.fromLTWH(isRtl ? 43.5 : 14.5, 14.5, 1.0, 10.0),
|
|
color: const Color(0xffffffff).withAlpha(alpha),
|
|
style: PaintingStyle.fill,
|
|
);
|
|
|
|
PaintPattern offLabelPaintPattern({
|
|
required int alpha,
|
|
bool highContrast = false,
|
|
bool isRtl = false,
|
|
}) =>
|
|
paints
|
|
..circle(
|
|
x: isRtl ? 16.0 : 43.0,
|
|
y: 19.5,
|
|
radius: 5.0,
|
|
color:
|
|
(highContrast ? const Color(0xffffffff) : const Color(0xffb3b3b3))
|
|
.withAlpha(alpha),
|
|
strokeWidth: 1.0,
|
|
style: PaintingStyle.stroke,
|
|
);
|
|
|
|
testWidgetsWithLeakTracking('Switch renders switch labels correctly before, during, and after being tapped', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
MediaQuery(
|
|
data: const MediaQueryData(onOffSwitchLabels: true),
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderObject switchRenderObject =
|
|
tester.element(find.byType(CupertinoSwitch)).renderObject!;
|
|
|
|
expect(switchRenderObject, offLabelPaintPattern(alpha: 255));
|
|
expect(switchRenderObject, onLabelPaintPattern(alpha: 0));
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
|
|
// Kick off animation, then advance to intermediate frame.
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
expect(switchRenderObject, onLabelPaintPattern(alpha: 131));
|
|
expect(switchRenderObject, offLabelPaintPattern(alpha: 124));
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(switchRenderObject, onLabelPaintPattern(alpha: 255));
|
|
expect(switchRenderObject, offLabelPaintPattern(alpha: 0));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch renders switch labels correctly before, during, and after being tapped in high contrast', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
MediaQuery(
|
|
data: const MediaQueryData(
|
|
onOffSwitchLabels: true,
|
|
highContrast: true,
|
|
),
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderObject switchRenderObject =
|
|
tester.element(find.byType(CupertinoSwitch)).renderObject!;
|
|
|
|
expect(switchRenderObject, offLabelPaintPattern(highContrast: true, alpha: 255));
|
|
expect(switchRenderObject, onLabelPaintPattern(alpha: 0));
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
|
|
// Kick off animation, then advance to intermediate frame.
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
expect(switchRenderObject, onLabelPaintPattern(alpha: 131));
|
|
expect(switchRenderObject, offLabelPaintPattern(highContrast: true, alpha: 124));
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(switchRenderObject, onLabelPaintPattern(alpha: 255));
|
|
expect(switchRenderObject, offLabelPaintPattern(highContrast: true, alpha: 0));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch renders switch labels correctly before, during, and after being tapped with direction rtl', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
MediaQuery(
|
|
data: const MediaQueryData(onOffSwitchLabels: true),
|
|
child: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderObject switchRenderObject =
|
|
tester.element(find.byType(CupertinoSwitch)).renderObject!;
|
|
|
|
expect(switchRenderObject, offLabelPaintPattern(isRtl: true, alpha: 255));
|
|
expect(switchRenderObject, onLabelPaintPattern(isRtl: true, alpha: 0));
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
|
|
// Kick off animation, then advance to intermediate frame.
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
expect(switchRenderObject, onLabelPaintPattern(isRtl: true, alpha: 131));
|
|
expect(switchRenderObject, offLabelPaintPattern(isRtl: true, alpha: 124));
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(switchRenderObject, onLabelPaintPattern(isRtl: true, alpha: 255));
|
|
expect(switchRenderObject, offLabelPaintPattern(isRtl: true, alpha: 0));
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch renders correctly in dark mode', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
MediaQuery(
|
|
data: const MediaQueryData(platformBrightness: Brightness.dark),
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byKey(switchKey),
|
|
matchesGoldenFile('switch.tap.off.dark.png'),
|
|
);
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
|
|
await tester.pumpAndSettle();
|
|
await expectLater(
|
|
find.byKey(switchKey),
|
|
matchesGoldenFile('switch.tap.on.dark.png'),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Switch can apply the ambient theme and be opted out', (WidgetTester tester) async {
|
|
final Key switchKey = UniqueKey();
|
|
bool value = false;
|
|
await tester.pumpWidget(
|
|
CupertinoTheme(
|
|
data: const CupertinoThemeData(primaryColor: Colors.amber, applyThemeToAll: true),
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: Column(
|
|
children: <Widget>[
|
|
CupertinoSwitch(
|
|
key: switchKey,
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
applyTheme: true,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
CupertinoSwitch(
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
applyTheme: false,
|
|
onChanged: (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byType(Column),
|
|
matchesGoldenFile('switch.tap.off.themed.png'),
|
|
);
|
|
|
|
await tester.tap(find.byKey(switchKey));
|
|
expect(value, isTrue);
|
|
|
|
await tester.pumpAndSettle();
|
|
await expectLater(
|
|
find.byType(Column),
|
|
matchesGoldenFile('switch.tap.on.themed.png'),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('Hovering over Cupertino switch updates cursor to clickable on Web', (WidgetTester tester) async {
|
|
const bool value = false;
|
|
// Disabled CupertinoSwitch does not update cursor on Web.
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return const Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: null,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
|
|
final Offset cupertinoSwitch = tester.getCenter(find.byType(CupertinoSwitch));
|
|
await gesture.addPointer(location: cupertinoSwitch);
|
|
await tester.pumpAndSettle();
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
|
|
|
|
// Enabled CupertinoSwitch updates cursor when hovering on Web.
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
dragStartBehavior: DragStartBehavior.down,
|
|
onChanged: (bool newValue) { },
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await gesture.moveTo(const Offset(10, 10));
|
|
await tester.pumpAndSettle();
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
|
|
|
|
await gesture.moveTo(cupertinoSwitch);
|
|
await tester.pumpAndSettle();
|
|
expect(
|
|
RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1),
|
|
kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('CupertinoSwitch is focusable and has correct focus color', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode(debugLabel: 'CupertinoSwitch');
|
|
addTearDown(focusNode.dispose);
|
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
bool value = true;
|
|
const Color focusColor = Color(0xffff0000);
|
|
|
|
Widget buildApp({bool enabled = true}) {
|
|
return Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Center(
|
|
child: CupertinoSwitch(
|
|
value: value,
|
|
onChanged: enabled ? (bool newValue) {
|
|
setState(() {
|
|
value = newValue;
|
|
});
|
|
} : null,
|
|
focusColor: focusColor,
|
|
focusNode: focusNode,
|
|
autofocus: true,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildApp());
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(focusNode.hasPrimaryFocus, isTrue);
|
|
expect(
|
|
find.byType(CupertinoSwitch),
|
|
paints
|
|
..rrect(color: const Color(0xff34c759))
|
|
..rrect(color: focusColor)
|
|
..clipRRect()
|
|
..rrect(color: const Color(0x26000000))
|
|
..rrect(color: const Color(0x0f000000))
|
|
..rrect(color: const Color(0x0a000000))
|
|
..rrect(color: const Color(0xffffffff)),
|
|
);
|
|
|
|
// Check the false value.
|
|
value = false;
|
|
await tester.pumpWidget(buildApp());
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(focusNode.hasPrimaryFocus, isTrue);
|
|
expect(
|
|
find.byType(CupertinoSwitch),
|
|
paints
|
|
..rrect(color: const Color(0x28787880))
|
|
..rrect(color: focusColor)
|
|
..clipRRect()
|
|
..rrect(color: const Color(0x26000000))
|
|
..rrect(color: const Color(0x0f000000))
|
|
..rrect(color: const Color(0x0a000000))
|
|
..rrect(color: const Color(0xffffffff)),
|
|
);
|
|
|
|
// Check what happens when disabled.
|
|
value = false;
|
|
await tester.pumpWidget(buildApp(enabled: false));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(focusNode.hasPrimaryFocus, isFalse);
|
|
expect(
|
|
find.byType(CupertinoSwitch),
|
|
paints
|
|
..rrect(color: const Color(0x28787880))
|
|
..clipRRect()
|
|
..rrect(color: const Color(0x26000000))
|
|
..rrect(color: const Color(0x0f000000))
|
|
..rrect(color: const Color(0x0a000000))
|
|
..rrect(color: const Color(0xffffffff)),
|
|
);
|
|
});
|
|
|
|
testWidgetsWithLeakTracking('CupertinoSwitch.onFocusChange callback', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode(debugLabel: 'CupertinoSwitch');
|
|
addTearDown(focusNode.dispose);
|
|
bool focused = false;
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: CupertinoSwitch(
|
|
value: true,
|
|
focusNode: focusNode,
|
|
onFocusChange: (bool value) {
|
|
focused = value;
|
|
},
|
|
onChanged:(bool newValue) {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
focusNode.requestFocus();
|
|
await tester.pump();
|
|
expect(focused, isTrue);
|
|
expect(focusNode.hasFocus, isTrue);
|
|
|
|
focusNode.unfocus();
|
|
await tester.pump();
|
|
expect(focused, isFalse);
|
|
expect(focusNode.hasFocus, isFalse);
|
|
});
|
|
}
|