blob: 3bddd9c8a58491a5b0bd896314d30d7308a379be [file] [log] [blame]
// 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.
// @dart = 2.8
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
const double _defaultBorderWidth = 1.0;
Widget boilerplate({Widget child}) {
return Directionality(
textDirection: TextDirection.ltr,
child: Center(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(RawMaterialButton, text),
matching: find.byType(DefaultTextStyle),
)).style;
}
final ThemeData theme = ThemeData();
await tester.pumpWidget(
Material(
child: 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(RawMaterialButton, text),
matching: find.byType(DefaultTextStyle),
)).style;
}
final List<bool> _isSelected = <bool>[false, true];
final ThemeData theme = ThemeData();
await tester.pumpWidget(
Material(
child: 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(RawMaterialButton, text),
matching: find.byType(DefaultTextStyle),
)).style;
}
final List<bool> _isSelected = <bool>[false, true];
final ThemeData theme = ThemeData();
await tester.pumpWidget(
Material(
child: 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 property cannot be null', (WidgetTester tester) async {
try {
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[false, true],
onPressed: (int index) {},
children: null,
),
),
),
);
fail('Should not be possible to create a toggle button with no children.');
} on AssertionError catch (e) {
expect(e.toString(), contains('children != null'));
}
});
testWidgets(
'isSelected property cannot be null',
(WidgetTester tester) async {
try {
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: null,
onPressed: (int index) {},
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
);
fail('Should not be possible to create a toggle button with no isSelected.');
} on AssertionError catch (e) {
expect(e.toString(), contains('isSelected != null'));
}
},
);
testWidgets(
'children and isSelected properties have to be the same length',
(WidgetTester tester) async {
try {
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[false],
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
);
fail('Should not be possible to create a toggle button with mismatching '
'children.length and isSelected.length.');
} on AssertionError catch (e) {
expect(e.toString(), contains('children.length'));
expect(e.toString(), contains('isSelected.length'));
}
},
);
testWidgets('Default text style is applied', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
await tester.pumpWidget(
Material(
child: 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(RawMaterialButton, 'First child'),
matching: find.byType(DefaultTextStyle),
)).style;
expect(textStyle.fontFamily, theme.textTheme.bodyText2.fontFamily);
expect(textStyle.decoration, theme.textTheme.bodyText2.decoration);
textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'Second child'),
matching: find.byType(DefaultTextStyle),
)).style;
expect(textStyle.fontFamily, theme.textTheme.bodyText2.fontFamily);
expect(textStyle.decoration, theme.textTheme.bodyText2.decoration);
});
testWidgets('Custom text style except color is applied', (WidgetTester tester) async {
await tester.pumpWidget(
Material(
child: 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(RawMaterialButton, '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(RawMaterialButton, '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(
Material(
child: 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(RawMaterialButton).at(0));
expect(firstRect.width, 48.0);
expect(firstRect.height, 48.0);
final Rect secondRect = tester.getRect(find.byType(RawMaterialButton).at(1));
expect(secondRect.width, 48.0);
expect(secondRect.height, 48.0);
final Rect thirdRect = tester.getRect(find.byType(RawMaterialButton).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(
Material(
child: 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(RawMaterialButton).at(0));
expect(firstRect.width, 50.0);
expect(firstRect.height, 60.0);
Rect secondRect = tester.getRect(find.byType(RawMaterialButton).at(1));
expect(secondRect.width, 50.0);
expect(secondRect.height, 60.0);
Rect thirdRect = tester.getRect(find.byType(RawMaterialButton).at(2));
expect(thirdRect.width, 50.0);
expect(thirdRect.height, 60.0);
// Test for maximum constraints
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
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(RawMaterialButton).at(0));
expect(firstRect.width, 20.0);
expect(firstRect.height, 10.0);
secondRect = tester.getRect(find.byType(RawMaterialButton).at(1));
expect(secondRect.width, 20.0);
expect(secondRect.height, 10.0);
thirdRect = tester.getRect(find.byType(RawMaterialButton).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(RawMaterialButton, text),
matching: find.byType(DefaultTextStyle),
)).style;
}
IconTheme iconTheme(IconData icon) {
return tester.widget(find.descendant(
of: find.widgetWithIcon(RawMaterialButton, icon),
matching: find.byType(IconTheme),
));
}
final ThemeData theme = ThemeData();
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[false],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <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(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[true],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <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(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[true],
children: <Widget>[
Row(children: const <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(RawMaterialButton, text),
matching: find.byType(DefaultTextStyle),
)).style;
}
IconTheme iconTheme(IconData icon) {
return tester.widget(find.descendant(
of: find.widgetWithIcon(RawMaterialButton, 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(
Material(
child: boilerplate(
child: ToggleButtons(
color: enabledColor,
isSelected: const <bool>[false],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <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(
Material(
child: boilerplate(
child: ToggleButtons(
selectedColor: selectedColor,
isSelected: const <bool>[true],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <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(
Material(
child: boilerplate(
child: ToggleButtons(
disabledColor: disabledColor,
isSelected: const <bool>[true],
children: <Widget>[
Row(children: const <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(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[false],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <Widget>[
Text('First child'),
]),
],
),
),
),
);
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton),
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(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[true],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <Widget>[
Text('First child'),
]),
],
),
),
),
);
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton),
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(
Material(
child: boilerplate(
child: ToggleButtons(
isSelected: const <bool>[true],
onPressed: null,
children: <Widget>[
Row(children: const <Widget>[
Text('First child'),
]),
],
),
),
),
);
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton),
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(
Material(
child: boilerplate(
child: ToggleButtons(
fillColor: customFillColor,
isSelected: const <bool>[true],
onPressed: (int index) {},
children: <Widget>[
Row(children: const <Widget>[
Text('First child'),
]),
],
),
),
),
);
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton),
matching: find.byType(Material),
));
expect(material.color, customFillColor);
expect(material.type, MaterialType.button);
});
testWidgets('Default InkWell colors - unselected', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(
Material(
child: 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'));
// 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: theme.colorScheme.onSurface.withOpacity(0.16)),
);
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: theme.colorScheme.onSurface.withOpacity(0.04)),
);
await hoverGesture.removePointer();
// 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)));
});
testWidgets('Default InkWell colors - selected', (WidgetTester tester) async {
final ThemeData theme = ThemeData();
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(
Material(
child: 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'));
// 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: theme.colorScheme.primary.withOpacity(0.16)),
);
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: theme.colorScheme.primary.withOpacity(0.04)),
);
await hoverGesture.removePointer();
// 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)));
});
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(
Material(
child: 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.removePointer();
// focusColor
focusNode.requestFocus();
await tester.pumpAndSettle();
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
return object.runtimeType.toString() == '_RenderInkFeatures';
});
expect(inkFeatures, paints..rect(color: focusColor));
});
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(
Material(
child: 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
// trailing side
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
strokeWidth: defaultBorderWidth,
)
// leading side, top and bottom
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
strokeWidth: defaultBorderWidth,
),
);
await tester.pumpWidget(
Material(
child: 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
// trailing side
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
strokeWidth: defaultBorderWidth,
)
// leading side, top and bottom
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
strokeWidth: defaultBorderWidth,
),
);
await tester.pumpWidget(
Material(
child: 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
// trailing side
..path(
style: PaintingStyle.stroke,
color: theme.colorScheme.onSurface.withOpacity(0.12),
strokeWidth: defaultBorderWidth,
)
// leading side, top and bottom
..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(
Material(
child: 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
// trailing side
..path(
style: PaintingStyle.stroke,
color: borderColor,
strokeWidth: customWidth,
)
// leading side, top and bottom
..path(
style: PaintingStyle.stroke,
color: borderColor,
strokeWidth: customWidth,
),
);
await tester.pumpWidget(
Material(
child: 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
// trailing side
..path(
style: PaintingStyle.stroke,
color: selectedBorderColor,
strokeWidth: customWidth,
)
// leading side, top and bottom
..path(
style: PaintingStyle.stroke,
color: selectedBorderColor,
strokeWidth: customWidth,
),
);
await tester.pumpWidget(
Material(
child: 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
// trailing side
..path(
style: PaintingStyle.stroke,
color: disabledBorderColor,
strokeWidth: customWidth,
)
// leading side, top and bottom
..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(
Material(
child: 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(
Material(
child: 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(
Material(
child: 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('ToggleButtons text baseline alignment', (WidgetTester tester) async {
await tester.pumpWidget(
Material(
child: 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: 'Ahem', fontSize: 10.0)),
Text('Second child', style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0)),
],
),
const MaterialButton(
onPressed: null,
child: Text('Material Button', style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0)),
),
const Text('Text', style: TextStyle(fontFamily: 'Ahem', fontSize: 30.0)),
],
),
),
),
);
// The Ahem font extends 0.2 * fontSize below the baseline.
// So the three row elements line up like this:
//
// ToggleButton MaterialButton Text
// ------------------------------------ baseline
// 2 4 6 space below the baseline = 0.2 * 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 materialButtonDy = tester.getBottomLeft(find.text('Material Button')).dy;
final double textDy = tester.getBottomLeft(find.text('Text')).dy;
expect(firstToggleButtonDy, secondToggleButtonDy);
expect(firstToggleButtonDy, closeTo(materialButtonDy - 2.0, 0.001));
expect(firstToggleButtonDy, closeTo(textDy - 4.0, 0.001));
});
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(
Material(
child: 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
// 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
// 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
// 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('ToggleButtons changes mouse cursor when the button is hovered', (WidgetTester tester) async {
await tester.pumpWidget(
Material(
child: 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')));
addTearDown(gesture.removePointer);
await tester.pump();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
// Test default cursor
await tester.pumpWidget(
Material(
child: 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(
Material(
child: 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);
});
}