mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1995 lines
66 KiB
Dart
1995 lines
66 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 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 isSelected = <bool>[false, true];
|
|
final 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 isSelected = <bool>[false, true];
|
|
final 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 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))
|
|
.last,
|
|
);
|
|
}
|
|
|
|
final 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))
|
|
.last,
|
|
);
|
|
}
|
|
|
|
final 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 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 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 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 WidgetState', (WidgetTester tester) async {
|
|
Material buttonColor(String text) {
|
|
return tester.widget<Material>(
|
|
find.descendant(of: find.byType(TextButton), matching: find.widgetWithText(Material, text)),
|
|
);
|
|
}
|
|
|
|
final 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 - WidgetState', (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<WidgetState> states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return selectedFillColor;
|
|
}
|
|
return defaultFillColor;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: ToggleButtons(
|
|
fillColor: WidgetStateColor.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: WidgetStateColor.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 theme = ThemeData();
|
|
final 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
|
|
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
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 theme = ThemeData();
|
|
final 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
|
|
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
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 splashColor = Color(0xff4caf50);
|
|
const highlightColor = Color(0xffcddc39);
|
|
const hoverColor = Color(0xffffeb3b);
|
|
const focusColor = Color(0xffffff00);
|
|
final 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
|
|
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
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 theme = ThemeData();
|
|
const 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 borderColor = Color(0xff4caf50);
|
|
const selectedBorderColor = Color(0xffcddc39);
|
|
const disabledBorderColor = Color(0xffffeb3b);
|
|
const 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 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 (var 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 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 (var 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 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 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 (var 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));
|
|
});
|
|
|
|
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 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 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 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 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 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: ${const Color(0xff4caf50)})',
|
|
'disabledColor: MaterialColor(primary value: ${const Color(0xff2196f3)})',
|
|
'selectedBorderColor: MaterialColor(primary value: ${const Color(0xffe91e63)})',
|
|
'disabledBorderColor: MaterialColor(primary value: ${const 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),
|
|
kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic,
|
|
);
|
|
|
|
// 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 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 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 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, SemanticsAction.focus],
|
|
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, SemanticsAction.focus],
|
|
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, SemanticsAction.focus],
|
|
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 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, SemanticsAction.focus],
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isButton,
|
|
SemanticsFlag.isEnabled,
|
|
SemanticsFlag.hasEnabledState,
|
|
SemanticsFlag.isChecked,
|
|
SemanticsFlag.hasCheckedState,
|
|
SemanticsFlag.isFocusable,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
|
|
),
|
|
],
|
|
),
|
|
ignoreId: true,
|
|
ignoreRect: true,
|
|
ignoreTransform: true,
|
|
),
|
|
);
|
|
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('ToggleButtons does not crash at zero area', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
child: Center(
|
|
child: SizedBox.shrink(
|
|
child: ToggleButtons(
|
|
isSelected: const <bool>[true],
|
|
children: const <Widget>[Text('X')],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(tester.getSize(find.byType(ToggleButtons)), Size.zero);
|
|
});
|
|
}
|