mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
![auto-submit[bot]](/assets/img/avatar_default.png)
Reverts: flutter/flutter#142942 Initiated by: zanderso Reason for reverting: Seems to have affected iOS platform view focus: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_ios%20native_platform_view_ui_tests_ios/10626/overview Original PR Author: gspencergoog Reviewed By: {yjbanov, goderbauer, chunhtai} This change reverts the following previous change: ## Description This causes the `Focus` widget to request focus on its focus node if the accessibility system (screen reader) focuses a widget via the `SemanticsAction.focus` action. ## Related Issues - https://github.com/flutter/flutter/issues/83809 ## Tests - Added a test to make sure that focus is requested when `SemanticsAction.focus` is sent by the engine.
2217 lines
70 KiB
Dart
2217 lines
70 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/foundation.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import '../widgets/semantics_tester.dart';
|
|
|
|
const double _defaultBorderWidth = 1.0;
|
|
|
|
Widget boilerplate({
|
|
ThemeData? theme,
|
|
MaterialTapTargetSize? tapTargetSize,
|
|
required Widget child,
|
|
}) {
|
|
return Theme(
|
|
data: theme ?? ThemeData(
|
|
materialTapTargetSize: tapTargetSize,
|
|
),
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: Material(child: child),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('Initial toggle state is reflected', (WidgetTester tester) async {
|
|
TextStyle buttonTextStyle(String text) {
|
|
return tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, text),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
}
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
onPressed: (int index) {},
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.87),
|
|
);
|
|
expect(
|
|
buttonTextStyle('Second child').color,
|
|
theme.colorScheme.primary,
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'onPressed is triggered on button tap',
|
|
(WidgetTester tester) async {
|
|
TextStyle buttonTextStyle(String text) {
|
|
return tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, text),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
}
|
|
final List<bool> isSelected = <bool>[false, true];
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return boilerplate(
|
|
child: ToggleButtons(
|
|
onPressed: (int index) {
|
|
setState(() {
|
|
isSelected[index] = !isSelected[index];
|
|
});
|
|
},
|
|
isSelected: isSelected,
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
|
|
expect(isSelected[0], isFalse);
|
|
expect(isSelected[1], isTrue);
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.87),
|
|
);
|
|
expect(
|
|
buttonTextStyle('Second child').color,
|
|
theme.colorScheme.primary,
|
|
);
|
|
|
|
await tester.tap(find.text('Second child'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(isSelected[0], isFalse);
|
|
expect(isSelected[1], isFalse);
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.87),
|
|
);
|
|
expect(
|
|
buttonTextStyle('Second child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.87),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'onPressed that is null disables buttons',
|
|
(WidgetTester tester) async {
|
|
TextStyle buttonTextStyle(String text) {
|
|
return tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, text),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
}
|
|
final List<bool> isSelected = <bool>[false, true];
|
|
final ThemeData theme = ThemeData();
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: isSelected,
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(isSelected[0], isFalse);
|
|
expect(isSelected[1], isTrue);
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.38),
|
|
);
|
|
expect(
|
|
buttonTextStyle('Second child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.38),
|
|
);
|
|
|
|
await tester.tap(find.text('Second child'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Nothing should change
|
|
expect(isSelected[0], isFalse);
|
|
expect(isSelected[1], isTrue);
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.38),
|
|
);
|
|
expect(
|
|
buttonTextStyle('Second child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.38),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'children and isSelected properties have to be the same length',
|
|
(WidgetTester tester) async {
|
|
await expectLater(
|
|
() => tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
throwsA(isAssertionError.having(
|
|
(AssertionError error) => error.toString(),
|
|
'.toString()',
|
|
allOf(
|
|
contains('children.length'),
|
|
contains('isSelected.length'),
|
|
),
|
|
)),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets('Default text style is applied', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
TextStyle textStyle;
|
|
textStyle = tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, 'First child'),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
expect(textStyle.fontFamily, theme.textTheme.bodyMedium!.fontFamily);
|
|
expect(textStyle.decoration, theme.textTheme.bodyMedium!.decoration);
|
|
|
|
textStyle = tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, 'Second child'),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
expect(textStyle.fontFamily, theme.textTheme.bodyMedium!.fontFamily);
|
|
expect(textStyle.decoration, theme.textTheme.bodyMedium!.decoration);
|
|
});
|
|
|
|
testWidgets('Custom text style except color is applied', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true],
|
|
onPressed: (int index) {},
|
|
textStyle: const TextStyle(
|
|
textBaseline: TextBaseline.ideographic,
|
|
fontSize: 20.0,
|
|
color: Colors.orange,
|
|
),
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
TextStyle textStyle;
|
|
textStyle = tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, 'First child'),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
expect(textStyle.textBaseline, TextBaseline.ideographic);
|
|
expect(textStyle.fontSize, 20.0);
|
|
expect(textStyle.color, isNot(Colors.orange));
|
|
|
|
textStyle = tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, 'Second child'),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
expect(textStyle.textBaseline, TextBaseline.ideographic);
|
|
expect(textStyle.fontSize, 20.0);
|
|
expect(textStyle.color, isNot(Colors.orange));
|
|
});
|
|
|
|
testWidgets('Default BoxConstraints', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, false, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Icon(Icons.check),
|
|
Icon(Icons.access_alarm),
|
|
Icon(Icons.cake),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
|
|
expect(firstRect.width, 48.0);
|
|
expect(firstRect.height, 48.0);
|
|
final Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
|
|
expect(secondRect.width, 48.0);
|
|
expect(secondRect.height, 48.0);
|
|
final Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
|
|
expect(thirdRect.width, 48.0);
|
|
expect(thirdRect.height, 48.0);
|
|
});
|
|
|
|
testWidgets('Custom BoxConstraints', (WidgetTester tester) async {
|
|
// Test for minimum constraints
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
constraints: const BoxConstraints(
|
|
minWidth: 50.0,
|
|
minHeight: 60.0,
|
|
),
|
|
isSelected: const <bool>[false, false, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Icon(Icons.check),
|
|
Icon(Icons.access_alarm),
|
|
Icon(Icons.cake),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
|
|
expect(firstRect.width, 50.0);
|
|
expect(firstRect.height, 60.0);
|
|
Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
|
|
expect(secondRect.width, 50.0);
|
|
expect(secondRect.height, 60.0);
|
|
Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
|
|
expect(thirdRect.width, 50.0);
|
|
expect(thirdRect.height, 60.0);
|
|
|
|
// Test for maximum constraints
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
constraints: const BoxConstraints(
|
|
maxWidth: 20.0,
|
|
maxHeight: 10.0,
|
|
),
|
|
isSelected: const <bool>[false, false, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Icon(Icons.check),
|
|
Icon(Icons.access_alarm),
|
|
Icon(Icons.cake),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
firstRect = tester.getRect(find.byType(TextButton).at(0));
|
|
expect(firstRect.width, 20.0);
|
|
expect(firstRect.height, 10.0);
|
|
secondRect = tester.getRect(find.byType(TextButton).at(1));
|
|
expect(secondRect.width, 20.0);
|
|
expect(secondRect.height, 10.0);
|
|
thirdRect = tester.getRect(find.byType(TextButton).at(2));
|
|
expect(thirdRect.width, 20.0);
|
|
expect(thirdRect.height, 10.0);
|
|
});
|
|
|
|
testWidgets(
|
|
'Default text/icon colors for enabled, selected and disabled states',
|
|
(WidgetTester tester) async {
|
|
TextStyle buttonTextStyle(String text) {
|
|
return tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, text),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
}
|
|
IconTheme iconTheme(IconData icon) {
|
|
return tester.widget(find.descendant(
|
|
of: find.widgetWithIcon(TextButton, icon),
|
|
matching: find.byType(IconTheme),
|
|
));
|
|
}
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
Icon(Icons.check),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// Default enabled color
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.87),
|
|
);
|
|
expect(
|
|
iconTheme(Icons.check).data.color,
|
|
theme.colorScheme.onSurface.withOpacity(0.87),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
Icon(Icons.check),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Default selected color
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.primary,
|
|
);
|
|
expect(
|
|
iconTheme(Icons.check).data.color,
|
|
theme.colorScheme.primary,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
Icon(Icons.check),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Default disabled color
|
|
expect(
|
|
buttonTextStyle('First child').color,
|
|
theme.colorScheme.onSurface.withOpacity(0.38),
|
|
);
|
|
expect(
|
|
iconTheme(Icons.check).data.color,
|
|
theme.colorScheme.onSurface.withOpacity(0.38),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'Custom text/icon colors for enabled, selected and disabled states',
|
|
(WidgetTester tester) async {
|
|
TextStyle buttonTextStyle(String text) {
|
|
return tester.widget<DefaultTextStyle>(find.descendant(
|
|
of: find.widgetWithText(TextButton, text),
|
|
matching: find.byType(DefaultTextStyle),
|
|
)).style;
|
|
}
|
|
IconTheme iconTheme(IconData icon) {
|
|
return tester.widget(find.descendant(
|
|
of: find.widgetWithIcon(TextButton, icon),
|
|
matching: find.byType(IconTheme),
|
|
));
|
|
}
|
|
final ThemeData theme = ThemeData();
|
|
const Color enabledColor = Colors.lime;
|
|
const Color selectedColor = Colors.green;
|
|
const Color disabledColor = Colors.yellow;
|
|
|
|
// Tests are ineffective if the custom colors are the same as the theme's
|
|
expect(theme.colorScheme.onSurface, isNot(enabledColor));
|
|
expect(theme.colorScheme.primary, isNot(selectedColor));
|
|
expect(theme.colorScheme.onSurface.withOpacity(0.38), isNot(disabledColor));
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
color: enabledColor,
|
|
isSelected: const <bool>[false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
Icon(Icons.check),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// Custom enabled color
|
|
expect(buttonTextStyle('First child').color, enabledColor);
|
|
expect(iconTheme(Icons.check).data.color, enabledColor);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
selectedColor: selectedColor,
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
Icon(Icons.check),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Custom selected color
|
|
expect(buttonTextStyle('First child').color, selectedColor);
|
|
expect(iconTheme(Icons.check).data.color, selectedColor);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
disabledColor: disabledColor,
|
|
isSelected: const <bool>[true],
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
Icon(Icons.check),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Custom disabled color
|
|
expect(buttonTextStyle('First child').color, disabledColor);
|
|
expect(iconTheme(Icons.check).data.color, disabledColor);
|
|
},
|
|
);
|
|
|
|
testWidgets('Default button fillColor - unselected', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Material material = tester.widget<Material>(find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.byType(Material),
|
|
));
|
|
expect(
|
|
material.color,
|
|
theme.colorScheme.surface.withOpacity(0.0),
|
|
);
|
|
expect(material.type, MaterialType.button);
|
|
});
|
|
|
|
testWidgets('Default button fillColor - selected', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Material material = tester.widget<Material>(find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.byType(Material),
|
|
));
|
|
expect(
|
|
material.color,
|
|
theme.colorScheme.primary.withOpacity(0.12),
|
|
);
|
|
expect(material.type, MaterialType.button);
|
|
});
|
|
|
|
testWidgets('Default button fillColor - disabled', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Material material = tester.widget<Material>(find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.byType(Material),
|
|
));
|
|
expect(
|
|
material.color,
|
|
theme.colorScheme.surface.withOpacity(0.0),
|
|
);
|
|
expect(material.type, MaterialType.button);
|
|
});
|
|
|
|
testWidgets('Custom button fillColor', (WidgetTester tester) async {
|
|
const Color customFillColor = Colors.green;
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
fillColor: customFillColor,
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Row(children: <Widget>[
|
|
Text('First child'),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Material material = tester.widget<Material>(find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.byType(Material),
|
|
));
|
|
expect(material.color, customFillColor);
|
|
expect(material.type, MaterialType.button);
|
|
});
|
|
|
|
testWidgets('Custom button fillColor - Non MaterialState', (WidgetTester tester) async {
|
|
Material buttonColor(String text) {
|
|
return tester.widget<Material>(
|
|
find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.widgetWithText(Material, text),
|
|
),
|
|
);
|
|
}
|
|
|
|
final ThemeData theme = ThemeData();
|
|
const Color selectedFillColor = Colors.yellow;
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
fillColor: selectedFillColor,
|
|
isSelected: const <bool>[false, true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(buttonColor('First child').color, theme.colorScheme.surface.withOpacity(0.0));
|
|
expect(buttonColor('Second child').color, selectedFillColor);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
fillColor: selectedFillColor,
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(buttonColor('First child').color, theme.colorScheme.surface.withOpacity(0.0));
|
|
expect(buttonColor('Second child').color, theme.colorScheme.surface.withOpacity(0.0));
|
|
});
|
|
|
|
testWidgets('Custom button fillColor - MaterialState', (WidgetTester tester) async {
|
|
Material buttonColor(String text) {
|
|
return tester.widget<Material>(
|
|
find.descendant(
|
|
of: find.byType(TextButton),
|
|
matching: find.widgetWithText(Material, text),
|
|
),
|
|
);
|
|
}
|
|
|
|
const Color selectedFillColor = Colors.orange;
|
|
const Color defaultFillColor = Colors.blue;
|
|
|
|
Color getFillColor(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return selectedFillColor;
|
|
}
|
|
return defaultFillColor;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
fillColor: MaterialStateColor.resolveWith(getFillColor),
|
|
isSelected: const <bool>[false, true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(buttonColor('First child').color, defaultFillColor);
|
|
expect(buttonColor('Second child').color, selectedFillColor);
|
|
|
|
// disabled
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
fillColor: MaterialStateColor.resolveWith(getFillColor),
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(buttonColor('First child').color, defaultFillColor);
|
|
expect(buttonColor('Second child').color, defaultFillColor);
|
|
});
|
|
|
|
testWidgets('Default InkWell colors - unselected', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
final FocusNode focusNode = FocusNode();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false],
|
|
onPressed: (int index) {},
|
|
focusNodes: <FocusNode>[focusNode],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Offset center = tester.getCenter(find.text('First child'));
|
|
|
|
// hoverColor
|
|
final TestGesture hoverGesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
await hoverGesture.addPointer();
|
|
await hoverGesture.moveTo(center);
|
|
await tester.pumpAndSettle();
|
|
await hoverGesture.moveTo(Offset.zero);
|
|
|
|
RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(
|
|
inkFeatures,
|
|
paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.04)),
|
|
);
|
|
|
|
// splashColor
|
|
final TestGesture touchGesture = await tester.createGesture();
|
|
await touchGesture.down(center); // The button is on hovered and pressed
|
|
await tester.pumpAndSettle();
|
|
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(
|
|
inkFeatures,
|
|
paints
|
|
..circle(color: theme.colorScheme.onSurface.withOpacity(0.16)),
|
|
);
|
|
|
|
await touchGesture.up();
|
|
await tester.pumpAndSettle();
|
|
await hoverGesture.moveTo(const Offset(0, 50));
|
|
await tester.pumpAndSettle();
|
|
|
|
// focusColor
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(inkFeatures, paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.12)));
|
|
|
|
await hoverGesture.removePointer();
|
|
|
|
focusNode.dispose();
|
|
});
|
|
|
|
testWidgets('Default InkWell colors - selected', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
final FocusNode focusNode = FocusNode();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
focusNodes: <FocusNode>[focusNode],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Offset center = tester.getCenter(find.text('First child'));
|
|
|
|
// hoverColor
|
|
final TestGesture hoverGesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
await hoverGesture.addPointer();
|
|
await hoverGesture.moveTo(center);
|
|
await tester.pumpAndSettle();
|
|
|
|
RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(
|
|
inkFeatures,
|
|
paints..rect(color: theme.colorScheme.primary.withOpacity(0.04)),
|
|
);
|
|
await hoverGesture.moveTo(Offset.zero);
|
|
|
|
// splashColor
|
|
final TestGesture touchGesture = await tester.createGesture();
|
|
await touchGesture.down(center); // The button is on hovered and pressed
|
|
await tester.pumpAndSettle();
|
|
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(
|
|
inkFeatures,
|
|
paints
|
|
..circle(color: theme.colorScheme.primary.withOpacity(0.16)),
|
|
);
|
|
|
|
await touchGesture.up();
|
|
await tester.pumpAndSettle();
|
|
await hoverGesture.moveTo(const Offset(0, 50));
|
|
await tester.pumpAndSettle();
|
|
|
|
// focusColor
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(inkFeatures, paints..rect(color: theme.colorScheme.primary.withOpacity(0.12)));
|
|
|
|
await hoverGesture.removePointer();
|
|
|
|
focusNode.dispose();
|
|
});
|
|
|
|
testWidgets('Custom InkWell colors', (WidgetTester tester) async {
|
|
const Color splashColor = Color(0xff4caf50);
|
|
const Color highlightColor = Color(0xffcddc39);
|
|
const Color hoverColor = Color(0xffffeb3b);
|
|
const Color focusColor = Color(0xffffff00);
|
|
final FocusNode focusNode = FocusNode();
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
splashColor: splashColor,
|
|
highlightColor: highlightColor,
|
|
hoverColor: hoverColor,
|
|
focusColor: focusColor,
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
focusNodes: <FocusNode>[focusNode],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Offset center = tester.getCenter(find.text('First child'));
|
|
|
|
// splashColor
|
|
final TestGesture touchGesture = await tester.createGesture();
|
|
await touchGesture.down(center);
|
|
await tester.pumpAndSettle();
|
|
|
|
RenderObject inkFeatures;
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(
|
|
inkFeatures,
|
|
paints
|
|
..circle(color: splashColor),
|
|
);
|
|
|
|
await touchGesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
// hoverColor
|
|
final TestGesture hoverGesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
await hoverGesture.addPointer();
|
|
await hoverGesture.moveTo(center);
|
|
await tester.pumpAndSettle();
|
|
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(inkFeatures, paints..rect(color: hoverColor));
|
|
await hoverGesture.moveTo(Offset.zero);
|
|
|
|
// focusColor
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_RenderInkFeatures';
|
|
});
|
|
expect(inkFeatures, paints..rect(color: focusColor));
|
|
|
|
await hoverGesture.removePointer();
|
|
|
|
focusNode.dispose();
|
|
});
|
|
|
|
testWidgets(
|
|
'Default border width and border colors for enabled, selected and disabled states',
|
|
(WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
const double defaultBorderWidth = 1.0;
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
RenderObject toggleButtonRenderObject;
|
|
toggleButtonRenderObject = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
});
|
|
expect(
|
|
toggleButtonRenderObject,
|
|
paints
|
|
// physical model
|
|
..path()
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
toggleButtonRenderObject = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
});
|
|
expect(
|
|
toggleButtonRenderObject,
|
|
paints
|
|
// physical model
|
|
..path()
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
toggleButtonRenderObject = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
});
|
|
expect(
|
|
toggleButtonRenderObject,
|
|
paints
|
|
// physical model
|
|
..path()
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: defaultBorderWidth,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'Custom border width and border colors for enabled, selected and disabled states',
|
|
(WidgetTester tester) async {
|
|
const Color borderColor = Color(0xff4caf50);
|
|
const Color selectedBorderColor = Color(0xffcddc39);
|
|
const Color disabledBorderColor = Color(0xffffeb3b);
|
|
const double customWidth = 2.0;
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
borderColor: borderColor,
|
|
borderWidth: customWidth,
|
|
isSelected: const <bool>[false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
RenderObject toggleButtonRenderObject;
|
|
toggleButtonRenderObject = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
});
|
|
expect(
|
|
toggleButtonRenderObject,
|
|
paints
|
|
// physical model
|
|
..path()
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: borderColor,
|
|
strokeWidth: customWidth,
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
selectedBorderColor: selectedBorderColor,
|
|
borderWidth: customWidth,
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
toggleButtonRenderObject = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
});
|
|
expect(
|
|
toggleButtonRenderObject,
|
|
paints
|
|
// physical model
|
|
..path()
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: selectedBorderColor,
|
|
strokeWidth: customWidth,
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
disabledBorderColor: disabledBorderColor,
|
|
borderWidth: customWidth,
|
|
isSelected: const <bool>[false],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
toggleButtonRenderObject = tester.allRenderObjects.firstWhere((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
});
|
|
expect(
|
|
toggleButtonRenderObject,
|
|
paints
|
|
// physical model
|
|
..path()
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: disabledBorderColor,
|
|
strokeWidth: customWidth,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets('Height of segmented control is determined by tallest widget', (WidgetTester tester) async {
|
|
final List<Widget> children = <Widget>[
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(height: 100.0),
|
|
),
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(height: 400.0), // tallest widget
|
|
),
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(height: 200.0),
|
|
),
|
|
];
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true, false],
|
|
children: children,
|
|
),
|
|
),
|
|
);
|
|
|
|
final List<Widget> toggleButtons = tester.allWidgets.where((Widget widget) {
|
|
return widget.runtimeType.toString() == '_SelectToggleButton';
|
|
}).toList();
|
|
|
|
for (int i = 0; i < toggleButtons.length; i++) {
|
|
final Rect rect = tester.getRect(find.byWidget(toggleButtons[i]));
|
|
expect(rect.height, 400.0 + 2 * _defaultBorderWidth);
|
|
}
|
|
});
|
|
|
|
testWidgets('Sizes of toggle buttons rebuilds with the correct dimensions', (WidgetTester tester) async {
|
|
final List<Widget> children = <Widget>[
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
),
|
|
),
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
),
|
|
),
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
),
|
|
),
|
|
];
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true, false],
|
|
children: children,
|
|
),
|
|
),
|
|
);
|
|
|
|
List<Widget> toggleButtons;
|
|
toggleButtons = tester.allWidgets.where((Widget widget) {
|
|
return widget.runtimeType.toString() == '_SelectToggleButton';
|
|
}).toList();
|
|
|
|
for (int i = 0; i < toggleButtons.length; i++) {
|
|
final Rect rect = tester.getRect(find.byWidget(toggleButtons[i]));
|
|
expect(rect.height, 100.0 + 2 * _defaultBorderWidth);
|
|
|
|
// Only the last button paints both leading and trailing borders.
|
|
// Other buttons only paint the leading border.
|
|
if (i == toggleButtons.length - 1) {
|
|
expect(rect.width, 100.0 + 2 * _defaultBorderWidth);
|
|
} else {
|
|
expect(rect.width, 100.0 + 1 * _defaultBorderWidth);
|
|
}
|
|
}
|
|
|
|
final List<Widget> childrenRebuilt = <Widget>[
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
),
|
|
),
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
),
|
|
),
|
|
Container(
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
),
|
|
),
|
|
];
|
|
|
|
// Update border width and widget sized to verify layout updates correctly
|
|
const double customBorderWidth = 5.0;
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
borderWidth: customBorderWidth,
|
|
isSelected: const <bool>[false, true, false],
|
|
children: childrenRebuilt,
|
|
),
|
|
),
|
|
);
|
|
|
|
toggleButtons = tester.allWidgets.where((Widget widget) {
|
|
return widget.runtimeType.toString() == '_SelectToggleButton';
|
|
}).toList();
|
|
|
|
// Only the last button paints both leading and trailing borders.
|
|
// Other buttons only paint the leading border.
|
|
for (int i = 0; i < toggleButtons.length; i++) {
|
|
final Rect rect = tester.getRect(find.byWidget(toggleButtons[i]));
|
|
expect(rect.height, 200.0 + 2 * customBorderWidth);
|
|
if (i == toggleButtons.length - 1) {
|
|
expect(rect.width, 200.0 + 2 * customBorderWidth);
|
|
} else {
|
|
expect(rect.width, 200.0 + 1 * customBorderWidth);
|
|
}
|
|
}
|
|
});
|
|
|
|
testWidgets('Material2 - ToggleButtons text baseline alignment', (WidgetTester tester) async {
|
|
// The font size must be a multiple of 4 until
|
|
// https://github.com/flutter/flutter/issues/122066 is resolved.
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
theme: ThemeData(useMaterial3: false),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
textBaseline: TextBaseline.alphabetic,
|
|
children: <Widget>[
|
|
ToggleButtons(
|
|
borderWidth: 5.0,
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 8.0)),
|
|
Text('Second child', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 8.0)),
|
|
],
|
|
),
|
|
ElevatedButton(
|
|
onPressed: null,
|
|
style: ElevatedButton.styleFrom(textStyle: const TextStyle(
|
|
fontFamily: 'FlutterTest',
|
|
fontSize: 20.0,
|
|
)),
|
|
child: const Text('Elevated Button'),
|
|
),
|
|
const Text('Text', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 28.0)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// The test font extends 0.25 * fontSize below the baseline.
|
|
// So the three row elements line up like this:
|
|
//
|
|
// ToggleButton MaterialButton Text
|
|
// ------------------------------------ baseline
|
|
// 2.0 5.0 7.0 space below the baseline = 0.25 * fontSize
|
|
// ------------------------------------ widget text dy values
|
|
|
|
final double firstToggleButtonDy = tester.getBottomLeft(find.text('First child')).dy;
|
|
final double secondToggleButtonDy = tester.getBottomLeft(find.text('Second child')).dy;
|
|
final double elevatedButtonDy = tester.getBottomLeft(find.text('Elevated Button')).dy;
|
|
final double textDy = tester.getBottomLeft(find.text('Text')).dy;
|
|
|
|
expect(firstToggleButtonDy, secondToggleButtonDy);
|
|
expect(firstToggleButtonDy, elevatedButtonDy - 3.0);
|
|
expect(firstToggleButtonDy, textDy - 5.0);
|
|
});
|
|
|
|
testWidgets('Material3 - ToggleButtons text baseline alignment', (WidgetTester tester) async {
|
|
// The point size of the fonts must be a multiple of 4 until
|
|
// https://github.com/flutter/flutter/issues/122066 is resolved.
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
textBaseline: TextBaseline.alphabetic,
|
|
children: <Widget>[
|
|
ToggleButtons(
|
|
borderWidth: 5.0,
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 8.0)),
|
|
Text('Second child', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 8.0)),
|
|
],
|
|
),
|
|
ElevatedButton(
|
|
onPressed: null,
|
|
style: ElevatedButton.styleFrom(textStyle: const TextStyle(
|
|
fontFamily: 'FlutterTest',
|
|
fontSize: 20.0,
|
|
)),
|
|
child: const Text('Elevated Button'),
|
|
),
|
|
const Text('Text', style: TextStyle(fontFamily: 'FlutterTest', fontSize: 28.0)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// The test font extends 0.25 * fontSize below the baseline.
|
|
// So the three row elements line up like this:
|
|
//
|
|
// ToggleButton MaterialButton Text
|
|
// ------------------------------------ baseline
|
|
// 2.0 5.0 7.0 space below the baseline = 0.25 * fontSize
|
|
// ------------------------------------ widget text dy values
|
|
|
|
final double firstToggleButtonDy = tester.getBottomLeft(find.text('First child')).dy;
|
|
final double secondToggleButtonDy = tester.getBottomLeft(find.text('Second child')).dy;
|
|
final double elevatedButtonDy = tester.getBottomLeft(find.text('Elevated Button')).dy;
|
|
final double textDy = tester.getBottomLeft(find.text('Text')).dy;
|
|
|
|
expect(firstToggleButtonDy, secondToggleButtonDy);
|
|
expect(firstToggleButtonDy, closeTo(elevatedButtonDy - 1.7, 0.1));
|
|
expect(firstToggleButtonDy, closeTo(textDy - 9.7, 0.1));
|
|
}, skip: kIsWeb && !isSkiaWeb); // https://github.com/flutter/flutter/issues/99933
|
|
|
|
testWidgets('Directionality test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Material(
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: ToggleButtons(
|
|
onPressed: (int index) {},
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
tester.getTopRight(find.text('First child')).dx < tester.getTopRight(find.text('Second child')).dx,
|
|
isTrue,
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
Material(
|
|
child: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Center(
|
|
child: ToggleButtons(
|
|
onPressed: (int index) {},
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
tester.getTopRight(find.text('First child')).dx > tester.getTopRight(find.text('Second child')).dx,
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
testWidgets(
|
|
'Properly draws borders based on state',
|
|
(WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
Text('Third child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final List<RenderObject> toggleButtonRenderObject = tester.allRenderObjects.where((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
}).toSet().toList();
|
|
|
|
// The first button paints the leading, top and bottom sides with a path
|
|
expect(
|
|
toggleButtonRenderObject[0],
|
|
paints
|
|
// physical model
|
|
..path()
|
|
// leading side, top and bottom - enabled
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
// The middle buttons paint a leading side path first, followed by a
|
|
// top and bottom side path
|
|
expect(
|
|
toggleButtonRenderObject[1],
|
|
paints
|
|
// physical model
|
|
..path()
|
|
// leading side - selected
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
)
|
|
// top and bottom - selected
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
// The last button paints a leading side path first, followed by
|
|
// a trailing, top and bottom side path
|
|
expect(
|
|
toggleButtonRenderObject[2],
|
|
paints
|
|
// physical model
|
|
..path()
|
|
// leading side - selected, since previous button is selected
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
)
|
|
// trailing side, top and bottom - enabled
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'Properly draws borders based on state when direction is vertical and verticalDirection is down.',
|
|
(WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
direction: Axis.vertical,
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
Text('Third child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// The children should be laid out along vertical and the first child at top.
|
|
// The item height is icon height + default border width (48.0 + 1.0) pixels.
|
|
expect(tester.getCenter(find.text('First child')), const Offset(400.0, 251.0));
|
|
expect(tester.getCenter(find.text('Second child')), const Offset(400.0, 300.0));
|
|
expect(tester.getCenter(find.text('Third child')), const Offset(400.0, 349.0));
|
|
|
|
final List<RenderObject> toggleButtonRenderObject = tester.allRenderObjects.where((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
}).toSet().toList();
|
|
|
|
// The first button paints the left, top and right sides with a path.
|
|
expect(
|
|
toggleButtonRenderObject[0],
|
|
paints
|
|
// physical model
|
|
..path()
|
|
// left side, top and right - enabled.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
// The middle buttons paint a top side path first, followed by a
|
|
// left and right side path.
|
|
expect(
|
|
toggleButtonRenderObject[1],
|
|
paints
|
|
// physical model
|
|
..path()
|
|
// top side - selected.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
)
|
|
// left and right - selected.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
// The last button paints a top side path first, followed by
|
|
// a left, bottom and right side path
|
|
expect(
|
|
toggleButtonRenderObject[2],
|
|
paints
|
|
// physical model
|
|
..path()
|
|
// top side - selected, since previous button is selected.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
)
|
|
// left side, bottom and right - enabled.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'VerticalDirection test when direction is vertical.',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
direction: Axis.vertical,
|
|
verticalDirection: VerticalDirection.up,
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
Text('Third child'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// The children should be laid out along vertical and the last child at top.
|
|
expect(tester.getCenter(find.text('Third child')), const Offset(400.0, 251.0));
|
|
expect(tester.getCenter(find.text('Second child')), const Offset(400.0, 300.0));
|
|
expect(tester.getCenter(find.text('First child')), const Offset(400.0, 349.0));
|
|
},
|
|
);
|
|
|
|
testWidgets('Material2 - Tap target size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
|
|
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
|
|
return boilerplate(
|
|
theme: ThemeData(useMaterial3: false),
|
|
tapTargetSize: tapTargetSize,
|
|
child: ToggleButtons(
|
|
key: key,
|
|
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First'),
|
|
Text('Second'),
|
|
Text('Third'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final Key key1 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
|
|
expect(tester.getSize(find.byKey(key1)), const Size(228.0, 48.0));
|
|
|
|
final Key key2 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
|
|
expect(tester.getSize(find.byKey(key2)), const Size(228.0, 48.0));
|
|
});
|
|
|
|
testWidgets('Material3 - Tap target size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
|
|
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
|
|
return boilerplate(
|
|
tapTargetSize: tapTargetSize,
|
|
child: ToggleButtons(
|
|
key: key,
|
|
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First'),
|
|
Text('Second'),
|
|
Text('Third'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final Key key1 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
|
|
expect(tester.getSize(find.byKey(key1)), const Size(232.0, 48.0));
|
|
|
|
final Key key2 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
|
|
expect(tester.getSize(find.byKey(key2)), const Size(232.0, 34.0));
|
|
});
|
|
|
|
testWidgets('Material2 - Tap target size is configurable', (WidgetTester tester) async {
|
|
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
|
|
return boilerplate(
|
|
theme: ThemeData(useMaterial3: false),
|
|
child: ToggleButtons(
|
|
key: key,
|
|
tapTargetSize: tapTargetSize,
|
|
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First'),
|
|
Text('Second'),
|
|
Text('Third'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final Key key1 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
|
|
expect(tester.getSize(find.byKey(key1)), const Size(228.0, 48.0));
|
|
|
|
final Key key2 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
|
|
expect(tester.getSize(find.byKey(key2)), const Size(228.0, 34.0));
|
|
});
|
|
|
|
testWidgets('Material3 - Tap target size is configurable', (WidgetTester tester) async {
|
|
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
|
|
return boilerplate(
|
|
child: ToggleButtons(
|
|
key: key,
|
|
tapTargetSize: tapTargetSize,
|
|
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First'),
|
|
Text('Second'),
|
|
Text('Third'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final Key key1 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
|
|
expect(tester.getSize(find.byKey(key1)), const Size(232.0, 48.0));
|
|
|
|
final Key key2 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
|
|
expect(tester.getSize(find.byKey(key2)), const Size(232.0, 34.0));
|
|
});
|
|
|
|
testWidgets('Tap target size is configurable for vertical axis', (WidgetTester tester) async {
|
|
Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) {
|
|
return boilerplate(
|
|
child: ToggleButtons(
|
|
key: key,
|
|
tapTargetSize: tapTargetSize,
|
|
constraints: const BoxConstraints(minWidth: 32.0, minHeight: 32.0),
|
|
direction: Axis.vertical,
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('1'),
|
|
Text('2'),
|
|
Text('3'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final Key key1 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.padded, key1));
|
|
expect(tester.getSize(find.byKey(key1)), const Size(48.0, 100.0));
|
|
|
|
final Key key2 = UniqueKey();
|
|
await tester.pumpWidget(buildFrame(MaterialTapTargetSize.shrinkWrap, key2));
|
|
expect(tester.getSize(find.byKey(key2)), const Size(34.0, 100.0));
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/73725
|
|
testWidgets('Material2 - Border radius paint test when there is only one button', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData(useMaterial3: false);
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
theme: theme,
|
|
child: RepaintBoundary(
|
|
child: ToggleButtons(
|
|
borderRadius: const BorderRadius.all(Radius.circular(7.0)),
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// The only button should be laid out at the center of the screen.
|
|
expect(tester.getCenter(find.text('First child')), const Offset(400.0, 300.0));
|
|
|
|
final List<RenderObject> toggleButtonRenderObject = tester.allRenderObjects.where((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
}).toSet().toList();
|
|
|
|
// The first button paints the left, top and right sides with a path.
|
|
expect(
|
|
toggleButtonRenderObject[0],
|
|
paints
|
|
// physical model paints
|
|
..path()
|
|
// left side, top and right - enabled.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byType(RepaintBoundary),
|
|
matchesGoldenFile('m2_toggle_buttons.oneButton.boardsPaint.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('Material3 - Border radius paint test when there is only one button', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: RepaintBoundary(
|
|
child: ToggleButtons(
|
|
borderRadius: const BorderRadius.all(Radius.circular(7.0)),
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// The only button should be laid out at the center of the screen.
|
|
expect(tester.getCenter(find.text('First child')), const Offset(400.0, 300.0));
|
|
|
|
final List<RenderObject> toggleButtonRenderObject = tester.allRenderObjects.where((RenderObject object) {
|
|
return object.runtimeType.toString() == '_SelectToggleButtonRenderObject';
|
|
}).toSet().toList();
|
|
|
|
// The first button paints the left, top and right sides with a path.
|
|
expect(
|
|
toggleButtonRenderObject[0],
|
|
paints
|
|
// physical model paints
|
|
..path()
|
|
// left side, top and right - enabled.
|
|
..path(
|
|
style: PaintingStyle.stroke,
|
|
color: theme.colorScheme.onSurface.withOpacity(0.12),
|
|
strokeWidth: _defaultBorderWidth,
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byType(RepaintBoundary),
|
|
matchesGoldenFile('m3_toggle_buttons.oneButton.boardsPaint.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('Material2 - Border radius paint test when Radius.x or Radius.y equal 0.0', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
theme: ThemeData(useMaterial3: false),
|
|
child: RepaintBoundary(
|
|
child: ToggleButtons(
|
|
borderRadius: const BorderRadius.only(
|
|
topRight: Radius.elliptical(10, 0),
|
|
topLeft: Radius.elliptical(0, 10),
|
|
bottomRight: Radius.elliptical(0, 10),
|
|
bottomLeft: Radius.elliptical(10, 0),
|
|
),
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byType(RepaintBoundary),
|
|
matchesGoldenFile('m2_toggle_buttons.oneButton.boardsPaint2.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('Material3 - Border radius paint test when Radius.x or Radius.y equal 0.0', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: RepaintBoundary(
|
|
child: ToggleButtons(
|
|
borderRadius: const BorderRadius.only(
|
|
topRight: Radius.elliptical(10, 0),
|
|
topLeft: Radius.elliptical(0, 10),
|
|
bottomRight: Radius.elliptical(0, 10),
|
|
bottomLeft: Radius.elliptical(10, 0),
|
|
),
|
|
isSelected: const <bool>[true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await expectLater(
|
|
find.byType(RepaintBoundary),
|
|
matchesGoldenFile('m3_toggle_buttons.oneButton.boardsPaint2.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('ToggleButtons implements debugFillProperties', (WidgetTester tester) async {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
|
|
ToggleButtons(
|
|
direction: Axis.vertical,
|
|
verticalDirection: VerticalDirection.up,
|
|
borderWidth: 3.0,
|
|
color: Colors.green,
|
|
selectedBorderColor: Colors.pink,
|
|
disabledColor: Colors.blue,
|
|
disabledBorderColor: Colors.yellow,
|
|
borderRadius: const BorderRadius.all(Radius.circular(7.0)),
|
|
isSelected: const <bool>[false, true, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
Text('Third child'),
|
|
],
|
|
).debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString()).toList();
|
|
|
|
expect(description, <String>[
|
|
'Buttons are enabled',
|
|
'color: MaterialColor(primary value: Color(0xff4caf50))',
|
|
'disabledColor: MaterialColor(primary value: Color(0xff2196f3))',
|
|
'selectedBorderColor: MaterialColor(primary value: Color(0xffe91e63))',
|
|
'disabledBorderColor: MaterialColor(primary value: Color(0xffffeb3b))',
|
|
'borderRadius: BorderRadius.circular(7.0)',
|
|
'borderWidth: 3.0',
|
|
'direction: Axis.vertical',
|
|
'verticalDirection: VerticalDirection.up',
|
|
]);
|
|
});
|
|
|
|
testWidgets('ToggleButtons changes mouse cursor when the button is hovered', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.forbidden,
|
|
child: ToggleButtons(
|
|
mouseCursor: SystemMouseCursors.text,
|
|
onPressed: (int index) {},
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
|
|
await gesture.addPointer(location: tester.getCenter(find.text('First child')));
|
|
|
|
await tester.pump();
|
|
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
|
|
|
// Test default cursor
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.forbidden,
|
|
child: ToggleButtons(
|
|
onPressed: (int index) {},
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click);
|
|
|
|
// Test default cursor when disabled
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.forbidden,
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true],
|
|
children: const <Widget>[
|
|
Text('First child'),
|
|
Text('Second child'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
|
|
});
|
|
|
|
testWidgets('ToggleButtons focus, hover, and highlight elevations are 0', (WidgetTester tester) async {
|
|
final List<FocusNode> focusNodes = <FocusNode>[FocusNode(), FocusNode()];
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true, false],
|
|
onPressed: (int index) { },
|
|
focusNodes: focusNodes,
|
|
children: const <Widget>[Text('one'), Text('two')],
|
|
),
|
|
),
|
|
);
|
|
|
|
double toggleButtonElevation(String text) {
|
|
return tester.widget<Material>(find.widgetWithText(Material, text).first).elevation;
|
|
}
|
|
|
|
// Default toggle button elevation
|
|
expect(toggleButtonElevation('one'), 0); // highlighted
|
|
expect(toggleButtonElevation('two'), 0); // not highlighted
|
|
|
|
// Hovered button elevation
|
|
final TestGesture hoverGesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
await hoverGesture.addPointer();
|
|
await hoverGesture.moveTo(tester.getCenter(find.text('one')));
|
|
await tester.pumpAndSettle();
|
|
expect(toggleButtonElevation('one'), 0);
|
|
await hoverGesture.moveTo(tester.getCenter(find.text('two')));
|
|
await tester.pumpAndSettle();
|
|
expect(toggleButtonElevation('two'), 0);
|
|
|
|
// Focused button elevation
|
|
focusNodes[0].requestFocus();
|
|
await tester.pumpAndSettle();
|
|
expect(focusNodes[0].hasFocus, isTrue);
|
|
expect(focusNodes[1].hasFocus, isFalse);
|
|
expect(toggleButtonElevation('one'), 0);
|
|
focusNodes[1].requestFocus();
|
|
await tester.pumpAndSettle();
|
|
expect(focusNodes[0].hasFocus, isFalse);
|
|
expect(focusNodes[1].hasFocus, isTrue);
|
|
expect(toggleButtonElevation('two'), 0);
|
|
|
|
await hoverGesture.removePointer();
|
|
|
|
for (final FocusNode n in focusNodes) {
|
|
n.dispose();
|
|
}
|
|
});
|
|
|
|
testWidgets('Toggle buttons height matches MaterialTapTargetSize.padded height', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, false, false],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Icon(Icons.check),
|
|
Icon(Icons.access_alarm),
|
|
Icon(Icons.cake),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
|
|
expect(firstRect.height, 48.0);
|
|
final Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
|
|
expect(secondRect.height, 48.0);
|
|
final Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
|
|
expect(thirdRect.height, 48.0);
|
|
});
|
|
|
|
testWidgets('Toggle buttons constraints size does not affect minimum input padding', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/97302
|
|
final SemanticsTester semantics = SemanticsTester(tester);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, false, false],
|
|
onPressed: (int index) {},
|
|
constraints: const BoxConstraints.tightFor(
|
|
width: 86,
|
|
height: 32,
|
|
),
|
|
children: const <Widget>[
|
|
Icon(Icons.check),
|
|
Icon(Icons.access_alarm),
|
|
Icon(Icons.cake),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
// Button's height is constrained to `32.0`.
|
|
final Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
|
|
expect(firstRect.height, 32.0);
|
|
final Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
|
|
expect(secondRect.height, 32.0);
|
|
final Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
|
|
expect(thirdRect.height, 32.0);
|
|
|
|
// While button's height is constrained to `32.0`, semantic node height
|
|
// should remain `48.0`, matching `MaterialTapTargetSize.padded` height (default).
|
|
expect(
|
|
semantics,
|
|
hasSemantics(
|
|
TestSemantics.root(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isButton,
|
|
SemanticsFlag.isEnabled,
|
|
SemanticsFlag.hasEnabledState,
|
|
SemanticsFlag.hasCheckedState,
|
|
SemanticsFlag.isFocusable,
|
|
],
|
|
actions: <SemanticsAction>[
|
|
SemanticsAction.tap,
|
|
],
|
|
rect: const Rect.fromLTRB(0.0, 0.0, 87.0, 48.0),
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isButton,
|
|
SemanticsFlag.isEnabled,
|
|
SemanticsFlag.hasEnabledState,
|
|
SemanticsFlag.hasCheckedState,
|
|
SemanticsFlag.isFocusable,
|
|
],
|
|
actions: <SemanticsAction>[
|
|
SemanticsAction.tap,
|
|
],
|
|
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0)
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isButton,
|
|
SemanticsFlag.isEnabled,
|
|
SemanticsFlag.hasEnabledState,
|
|
SemanticsFlag.hasCheckedState,
|
|
SemanticsFlag.isFocusable,
|
|
],
|
|
actions: <SemanticsAction>[
|
|
SemanticsAction.tap,
|
|
],
|
|
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
|
|
),
|
|
],
|
|
),
|
|
ignoreId: true,
|
|
ignoreRect: true,
|
|
ignoreTransform: true,
|
|
),
|
|
);
|
|
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('Toggle buttons have correct semantics', (WidgetTester tester) async {
|
|
final SemanticsTester semantics = SemanticsTester(tester);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[false, true],
|
|
onPressed: (int index) {},
|
|
children: const <Widget>[
|
|
Icon(Icons.check),
|
|
Icon(Icons.access_alarm),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
semantics,
|
|
hasSemantics(
|
|
TestSemantics.root(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isButton,
|
|
SemanticsFlag.isEnabled,
|
|
SemanticsFlag.hasEnabledState,
|
|
SemanticsFlag.hasCheckedState,
|
|
SemanticsFlag.isFocusable,
|
|
],
|
|
actions: <SemanticsAction>[
|
|
SemanticsAction.tap,
|
|
],
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isButton,
|
|
SemanticsFlag.isEnabled,
|
|
SemanticsFlag.hasEnabledState,
|
|
SemanticsFlag.isChecked,
|
|
SemanticsFlag.hasCheckedState,
|
|
SemanticsFlag.isFocusable,
|
|
],
|
|
actions: <SemanticsAction>[
|
|
SemanticsAction.tap,
|
|
],
|
|
),
|
|
],
|
|
),
|
|
ignoreId: true,
|
|
ignoreRect: true,
|
|
ignoreTransform: true,
|
|
),
|
|
);
|
|
|
|
semantics.dispose();
|
|
});
|
|
}
|