blob: 0bcd18bab899d0dd28195851a3f121449fe5d55c [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.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
import '../rendering/mock_canvas.dart';
Widget boilerplate({required Widget child}) {
return Directionality(
textDirection: TextDirection.ltr,
child: Center(child: child),
);
}
void main() {
test('ToggleButtonsThemeData copyWith, ==, hashCode basics', () {
expect(const ToggleButtonsThemeData(), const ToggleButtonsThemeData().copyWith());
expect(const ToggleButtonsThemeData().hashCode, const ToggleButtonsThemeData().copyWith().hashCode);
});
test('ToggleButtonsThemeData lerp special cases', () {
expect(ToggleButtonsThemeData.lerp(null, null, 0), null);
const ToggleButtonsThemeData data = ToggleButtonsThemeData();
expect(identical(ToggleButtonsThemeData.lerp(data, data, 0.5), data), true);
});
test('ToggleButtonsThemeData defaults', () {
const ToggleButtonsThemeData themeData = ToggleButtonsThemeData();
expect(themeData.textStyle, null);
expect(themeData.constraints, null);
expect(themeData.color, null);
expect(themeData.selectedColor, null);
expect(themeData.disabledColor, null);
expect(themeData.fillColor, null);
expect(themeData.focusColor, null);
expect(themeData.highlightColor, null);
expect(themeData.hoverColor, null);
expect(themeData.splashColor, null);
expect(themeData.borderColor, null);
expect(themeData.selectedBorderColor, null);
expect(themeData.disabledBorderColor, null);
expect(themeData.borderRadius, null);
expect(themeData.borderWidth, null);
const ToggleButtonsTheme theme = ToggleButtonsTheme(data: ToggleButtonsThemeData(), child: SizedBox());
expect(theme.data.textStyle, null);
expect(theme.data.constraints, null);
expect(theme.data.color, null);
expect(theme.data.selectedColor, null);
expect(theme.data.disabledColor, null);
expect(theme.data.fillColor, null);
expect(theme.data.focusColor, null);
expect(theme.data.highlightColor, null);
expect(theme.data.hoverColor, null);
expect(theme.data.splashColor, null);
expect(theme.data.borderColor, null);
expect(theme.data.selectedBorderColor, null);
expect(theme.data.disabledBorderColor, null);
expect(theme.data.borderRadius, null);
expect(theme.data.borderWidth, null);
});
testWidgetsWithLeakTracking('Default ToggleButtonsThemeData debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const ToggleButtonsThemeData().debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[]);
});
testWidgetsWithLeakTracking('ToggleButtonsThemeData implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const ToggleButtonsThemeData(
textStyle: TextStyle(fontSize: 10),
constraints: BoxConstraints(minHeight: 10.0, maxHeight: 20.0),
color: Color(0xfffffff0),
selectedColor: Color(0xfffffff1),
disabledColor: Color(0xfffffff2),
fillColor: Color(0xfffffff3),
focusColor: Color(0xfffffff4),
highlightColor: Color(0xfffffff5),
hoverColor: Color(0xfffffff6),
splashColor: Color(0xfffffff7),
borderColor: Color(0xfffffff8),
selectedBorderColor: Color(0xfffffff9),
disabledBorderColor: Color(0xfffffffa),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
borderWidth: 2.0,
).debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[
'textStyle.inherit: true',
'textStyle.size: 10.0',
'constraints: BoxConstraints(0.0<=w<=Infinity, 10.0<=h<=20.0)',
'color: Color(0xfffffff0)',
'selectedColor: Color(0xfffffff1)',
'disabledColor: Color(0xfffffff2)',
'fillColor: Color(0xfffffff3)',
'focusColor: Color(0xfffffff4)',
'highlightColor: Color(0xfffffff5)',
'hoverColor: Color(0xfffffff6)',
'splashColor: Color(0xfffffff7)',
'borderColor: Color(0xfffffff8)',
'selectedBorderColor: Color(0xfffffff9)',
'disabledBorderColor: Color(0xfffffffa)',
'borderRadius: BorderRadius.circular(4.0)',
'borderWidth: 2.0',
]);
});
testWidgetsWithLeakTracking('Theme text style, except color, is applied', (WidgetTester tester) async {
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
textStyle: TextStyle(
color: Colors.orange,
textBaseline: TextBaseline.ideographic,
fontSize: 20.0,
),
),
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.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));
});
testWidgetsWithLeakTracking('Custom BoxConstraints', (WidgetTester tester) async {
// Test for minimum constraints
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
constraints: BoxConstraints(
minWidth: 50.0,
minHeight: 60.0,
),
),
child: ToggleButtons(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
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(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
constraints: BoxConstraints(
maxWidth: 20.0,
maxHeight: 10.0,
),
),
child: ToggleButtons(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
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);
});
testWidgetsWithLeakTracking(
'Theme text/icon colors for enabled, selected and disabled states',
(WidgetTester tester) async {
TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle),
)).style;
}
IconTheme iconTheme(IconData icon) {
return tester.widget(find.descendant(
of: find.widgetWithIcon(TextButton, icon),
matching: find.byType(IconTheme),
));
}
final ThemeData theme = ThemeData();
const Color enabledColor = Colors.lime;
const Color selectedColor = Colors.green;
const Color disabledColor = Colors.yellow;
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(),
child: ToggleButtons(
color: enabledColor,
isSelected: const <bool>[false],
onPressed: (int index) {},
children: const <Widget>[
// This Row is used like this to test for both TextStyle
// and IconTheme for Text and Icon widgets respectively.
Row(children: <Widget>[
Text('First child'),
Icon(Icons.check),
]),
],
),
),
),
),
);
// Custom theme enabled color
expect(theme.colorScheme.onSurface, isNot(enabledColor));
expect(buttonTextStyle('First child').color, enabledColor);
expect(iconTheme(Icons.check).data.color, enabledColor);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
selectedColor: selectedColor,
),
child: ToggleButtons(
color: enabledColor,
isSelected: const <bool>[true],
onPressed: (int index) {},
children: const <Widget>[
Row(children: <Widget>[
Text('First child'),
Icon(Icons.check),
]),
],
),
),
),
),
);
await tester.pumpAndSettle();
// Custom theme selected color
expect(theme.colorScheme.primary, isNot(selectedColor));
expect(buttonTextStyle('First child').color, selectedColor);
expect(iconTheme(Icons.check).data.color, selectedColor);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
disabledColor: disabledColor,
),
child: ToggleButtons(
color: enabledColor,
isSelected: const <bool>[false],
children: const <Widget>[
Row(children: <Widget>[
Text('First child'),
Icon(Icons.check),
]),
],
),
),
),
),
);
await tester.pumpAndSettle();
// Custom theme disabled color
expect(theme.disabledColor, isNot(disabledColor));
expect(buttonTextStyle('First child').color, disabledColor);
expect(iconTheme(Icons.check).data.color, disabledColor);
},
);
testWidgetsWithLeakTracking('Theme button fillColor', (WidgetTester tester) async {
const Color customFillColor = Colors.green;
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(fillColor: customFillColor),
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, customFillColor);
expect(material.type, MaterialType.button);
});
testWidgetsWithLeakTracking('Custom Theme button fillColor in different states', (WidgetTester tester) async {
Material buttonColor(String text) {
return tester.widget<Material>(
find.descendant(
of: find.byType(TextButton),
matching: find.widgetWithText(Material, text),
),
);
}
const Color enabledFillColor = Colors.green;
const Color selectedFillColor = Colors.blue;
const Color disabledFillColor = Colors.yellow;
Color getColor(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return selectedFillColor;
} else if (states.contains(MaterialState.disabled)) {
return disabledFillColor;
}
return enabledFillColor;
}
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: ToggleButtonsThemeData(fillColor: MaterialStateColor.resolveWith(getColor)),
child: ToggleButtons(
isSelected: const <bool>[true, false],
onPressed: (int index) {},
children: const <Widget> [
Text('First child'),
Text('Second child'),
],
),
),
),
),
);
await tester.pumpAndSettle();
expect(buttonColor('First child').color, selectedFillColor);
expect(buttonColor('Second child').color, enabledFillColor);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: ToggleButtonsThemeData(fillColor: MaterialStateColor.resolveWith(getColor)),
child: ToggleButtons(
isSelected: const <bool>[true, false],
children: const <Widget>[
Text('First child'),
Text('Second child'),
],
),
),
),
),
);
expect(buttonColor('First child').color, disabledFillColor);
expect(buttonColor('Second child').color, disabledFillColor);
});
testWidgetsWithLeakTracking('Theme 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: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
splashColor: splashColor,
highlightColor: highlightColor,
hoverColor: hoverColor,
focusColor: focusColor,
),
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
// highlightColor
final TestGesture touchGesture = await tester.createGesture();
await touchGesture.down(center);
await tester.pumpAndSettle();
RenderObject inkFeatures;
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
return object.runtimeType.toString() == '_RenderInkFeatures';
});
expect(
inkFeatures,
paints
..circle(color: splashColor)
);
await touchGesture.up();
await tester.pumpAndSettle();
// hoverColor
final TestGesture hoverGesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await hoverGesture.addPointer();
await hoverGesture.moveTo(center);
await tester.pumpAndSettle();
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
return object.runtimeType.toString() == '_RenderInkFeatures';
});
expect(inkFeatures, paints..rect(color: hoverColor));
await hoverGesture.moveTo(Offset.zero);
// focusColor
focusNode.requestFocus();
await tester.pumpAndSettle();
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) {
return object.runtimeType.toString() == '_RenderInkFeatures';
});
expect(inkFeatures, paints..rect(color: focusColor));
await hoverGesture.removePointer();
});
testWidgetsWithLeakTracking(
'Theme 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: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
borderColor: borderColor,
borderWidth: customWidth,
),
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 layer paint
..path()
..path(
style: PaintingStyle.stroke,
color: borderColor,
strokeWidth: customWidth,
),
);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
selectedBorderColor: selectedBorderColor,
borderWidth: customWidth,
),
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 layer paint
..path()
..path(
style: PaintingStyle.stroke,
color: selectedBorderColor,
strokeWidth: customWidth,
),
);
await tester.pumpWidget(
Material(
child: boilerplate(
child: ToggleButtonsTheme(
data: const ToggleButtonsThemeData(
disabledBorderColor: disabledBorderColor,
borderWidth: customWidth,
),
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 layer paint
..path()
..path(
style: PaintingStyle.stroke,
color: disabledBorderColor,
strokeWidth: customWidth,
),
);
},
);
}