Unverified Commit 0c3c38dc authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

[reland] Refactor ToggleButtons (remove RawMaterialButton) (#101760)

parent 3f2cb241
...@@ -8,11 +8,12 @@ import 'package:flutter/foundation.dart'; ...@@ -8,11 +8,12 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button.dart'; import 'button_style.dart';
import 'color_scheme.dart'; import 'color_scheme.dart';
import 'constants.dart'; import 'constants.dart';
import 'debug.dart'; import 'ink_ripple.dart';
import 'material_state.dart'; import 'material_state.dart';
import 'text_button.dart';
import 'theme.dart'; import 'theme.dart';
import 'theme_data.dart'; import 'theme_data.dart';
import 'toggle_buttons_theme.dart'; import 'toggle_buttons_theme.dart';
...@@ -667,45 +668,129 @@ class ToggleButtons extends StatelessWidget { ...@@ -667,45 +668,129 @@ class ToggleButtons extends StatelessWidget {
final BorderSide borderSide = _getBorderSide(index, theme, toggleButtonsTheme); final BorderSide borderSide = _getBorderSide(index, theme, toggleButtonsTheme);
final BorderSide trailingBorderSide = _getTrailingBorderSide(index, theme, toggleButtonsTheme); final BorderSide trailingBorderSide = _getTrailingBorderSide(index, theme, toggleButtonsTheme);
return _ToggleButton( final Set<MaterialState> states = <MaterialState>{
selected: isSelected[index], if (isSelected[index] && onPressed != null) MaterialState.selected,
textStyle: textStyle, if (onPressed == null) MaterialState.disabled,
constraints: constraints, };
color: color, final Color effectiveFillColor = _ResolveFillColor(fillColor
selectedColor: selectedColor, ?? toggleButtonsTheme.fillColor).resolve(states)
disabledColor: disabledColor, ?? _DefaultFillColor(theme.colorScheme).resolve(states);
fillColor: fillColor, final Color currentColor;
focusColor: focusColor ?? toggleButtonsTheme.focusColor, if (onPressed != null && isSelected[index]) {
highlightColor: highlightColor ?? toggleButtonsTheme.highlightColor, currentColor = selectedColor
hoverColor: hoverColor ?? toggleButtonsTheme.hoverColor, ?? toggleButtonsTheme.selectedColor
splashColor: splashColor ?? toggleButtonsTheme.splashColor, ?? theme.colorScheme.primary;
focusNode: focusNodes != null ? focusNodes![index] : null, } else if (onPressed != null && !isSelected[index]) {
onPressed: onPressed != null currentColor = color
? () {onPressed!(index);} ?? toggleButtonsTheme.color
: null, ?? theme.colorScheme.onSurface.withOpacity(0.87);
mouseCursor: mouseCursor, } else {
currentColor = disabledColor
?? toggleButtonsTheme.disabledColor
?? theme.colorScheme.onSurface.withOpacity(0.38);
}
final TextStyle currentTextStyle = textStyle
?? toggleButtonsTheme.textStyle
?? theme.textTheme.bodyText2!;
final BoxConstraints? currentConstraints = constraints
?? toggleButtonsTheme.constraints;
final Size minimumSize = currentConstraints == null
? const Size.square(kMinInteractiveDimension)
: Size(currentConstraints.minWidth, currentConstraints.minHeight);
final Size? maximumSize = currentConstraints == null
? null
: Size(currentConstraints.maxWidth, currentConstraints.maxHeight);
final Size minPaddingSize;
switch (tapTargetSize ?? theme.materialTapTargetSize) {
case MaterialTapTargetSize.padded:
if (direction == Axis.horizontal) {
minPaddingSize = const Size(
0.0,
kMinInteractiveDimension,
);
} else {
minPaddingSize = const Size(
kMinInteractiveDimension,
0.0,
);
}
assert(minPaddingSize.width >= 0.0);
assert(minPaddingSize.height >= 0.0);
break;
case MaterialTapTargetSize.shrinkWrap:
minPaddingSize = Size.zero;
break;
}
Widget button = _SelectToggleButton(
leadingBorderSide: leadingBorderSide, leadingBorderSide: leadingBorderSide,
borderSide: borderSide, borderSide: borderSide,
trailingBorderSide: trailingBorderSide, trailingBorderSide: trailingBorderSide,
borderRadius: edgeBorderRadius, borderRadius: edgeBorderRadius,
clipRadius: clipBorderRadius,
isFirstButton: index == 0, isFirstButton: index == 0,
isLastButton: index == children.length - 1, isLastButton: index == children.length - 1,
direction: direction, direction: direction,
verticalDirection: verticalDirection, verticalDirection: verticalDirection,
child: ClipRRect(
borderRadius: clipBorderRadius,
child: TextButton(
focusNode: focusNodes != null ? focusNodes![index] : null,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color?>(effectiveFillColor),
foregroundColor: MaterialStateProperty.all<Color?>(currentColor),
overlayColor: _ToggleButtonDefaultOverlay(
selected: onPressed != null && isSelected[index],
unselected: onPressed != null && !isSelected[index],
colorScheme: theme.colorScheme,
disabledColor: disabledColor ?? toggleButtonsTheme.disabledColor,
focusColor: focusColor ?? toggleButtonsTheme.focusColor,
highlightColor: highlightColor ?? toggleButtonsTheme.highlightColor,
hoverColor: hoverColor ?? toggleButtonsTheme.hoverColor,
splashColor: splashColor ?? toggleButtonsTheme.splashColor,
),
elevation: MaterialStateProperty.all<double>(0),
textStyle: MaterialStateProperty.all<TextStyle?>(currentTextStyle.copyWith(
color: currentColor,
)),
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(EdgeInsets.zero),
minimumSize: MaterialStateProperty.all<Size?>(minimumSize),
maximumSize: MaterialStateProperty.all<Size?>(maximumSize),
shape: MaterialStateProperty.all<OutlinedBorder>(const RoundedRectangleBorder()),
mouseCursor: MaterialStateProperty.all<MouseCursor?>(mouseCursor),
visualDensity: VisualDensity.standard,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
splashFactory: InkRipple.splashFactory,
),
onPressed: onPressed != null
? () {onPressed!(index);}
: null,
child: children[index], child: children[index],
),
),
); );
});
final Widget result = direction == Axis.horizontal if (currentConstraints != null) {
? IntrinsicHeight( button = Center(child: button);
child: Row( }
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, return MergeSemantics(
children: buttons, child: Semantics(
container: true,
enabled: onPressed != null,
child: _InputPadding(
minSize: minPaddingSize,
direction: direction,
child: button,
),
), ),
) );
: IntrinsicWidth( });
if (direction == Axis.vertical) {
return IntrinsicWidth(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
...@@ -713,18 +798,15 @@ class ToggleButtons extends StatelessWidget { ...@@ -713,18 +798,15 @@ class ToggleButtons extends StatelessWidget {
children: buttons, children: buttons,
), ),
); );
}
final MaterialTapTargetSize resolvedTapTargetSize = tapTargetSize ?? theme.materialTapTargetSize; return IntrinsicHeight(
switch (resolvedTapTargetSize) { child: Row(
case MaterialTapTargetSize.padded: mainAxisSize: MainAxisSize.min,
return _InputPadding( crossAxisAlignment: CrossAxisAlignment.stretch,
minSize: const Size(kMinInteractiveDimension, kMinInteractiveDimension), children: buttons,
direction: direction, ),
child: result,
); );
case MaterialTapTargetSize.shrinkWrap:
return result;
}
} }
@override @override
...@@ -754,250 +836,6 @@ class ToggleButtons extends StatelessWidget { ...@@ -754,250 +836,6 @@ class ToggleButtons extends StatelessWidget {
} }
} }
/// An individual toggle button, otherwise known as a segmented button.
///
/// This button is used by [ToggleButtons] to implement a set of segmented controls.
class _ToggleButton extends StatelessWidget {
/// Creates a toggle button based on [RawMaterialButton].
///
/// This class adds some logic to distinguish between enabled, active, and
/// disabled states, to determine the appropriate colors to use.
///
/// It takes in a [shape] property to modify the borders of the button,
/// which is used by [ToggleButtons] to customize borders based on the
/// order in which this button appears in the list.
const _ToggleButton({
this.selected = false,
this.textStyle,
this.constraints,
this.color,
this.selectedColor,
this.disabledColor,
required this.fillColor,
required this.focusColor,
required this.highlightColor,
required this.hoverColor,
required this.splashColor,
this.focusNode,
this.onPressed,
this.mouseCursor,
required this.leadingBorderSide,
required this.borderSide,
required this.trailingBorderSide,
required this.borderRadius,
required this.clipRadius,
required this.isFirstButton,
required this.isLastButton,
required this.direction,
required this.verticalDirection,
required this.child,
});
/// Determines if the button is displayed as active/selected or enabled.
final bool selected;
/// The [TextStyle] to apply to any text that appears in this button.
final TextStyle? textStyle;
/// Defines the button's size.
///
/// Typically used to constrain the button's minimum size.
final BoxConstraints? constraints;
/// The color for [Text] and [Icon] widgets if the button is enabled.
///
/// If [selected] is false and [onPressed] is not null, this color will be used.
final Color? color;
/// The color for [Text] and [Icon] widgets if the button is selected.
///
/// If [selected] is true and [onPressed] is not null, this color will be used.
final Color? selectedColor;
/// The color for [Text] and [Icon] widgets if the button is disabled.
///
/// If [onPressed] is null, this color will be used.
final Color? disabledColor;
/// The color of the button's [Material].
final Color? fillColor;
/// The color for the button's [Material] when it has the input focus.
final Color? focusColor;
/// The color for the button's [Material] when a pointer is hovering over it.
final Color? hoverColor;
/// The highlight color for the button's [InkWell].
final Color? highlightColor;
/// The splash color for the button's [InkWell].
final Color? splashColor;
/// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode;
/// Called when the button is tapped or otherwise activated.
///
/// If this is null, the button will be disabled, see [enabled].
final VoidCallback? onPressed;
/// {@macro flutter.material.RawMaterialButton.mouseCursor}
///
/// If this property is null, [MaterialStateMouseCursor.clickable] will be used.
final MouseCursor? mouseCursor;
/// The width and color of the button's leading side border.
final BorderSide leadingBorderSide;
/// If [direction] is [Axis.horizontal], this corresponds the width and color
/// of the button's top and bottom side borders.
///
/// If [direction] is [Axis.vertical], this corresponds the width and color
/// of the button's left and right side borders.
final BorderSide borderSide;
/// The width and color of the button's trailing side border.
final BorderSide trailingBorderSide;
/// The border radii of each corner of the button.
final BorderRadius borderRadius;
/// The corner radii used to clip the button's contents.
///
/// This is used to have the button's contents be properly clipped taking
/// the [borderRadius] and the border's width into account.
final BorderRadius clipRadius;
/// Whether or not this toggle button is the first button in the list.
final bool isFirstButton;
/// Whether or not this toggle button is the last button in the list.
final bool isLastButton;
/// The direction along which the buttons are rendered.
final Axis direction;
/// If [direction] is [Axis.vertical], this property defines whether or not this button in its list
/// of buttons is laid out starting from top to bottom or from bottom to top.
final VerticalDirection verticalDirection;
/// The button's label, which is usually an [Icon] or a [Text] widget.
final Widget child;
Color _resolveColor(Set<MaterialState> states, MaterialStateProperty<Color?> widgetColor,
MaterialStateProperty<Color?> themeColor, MaterialStateProperty<Color> defaultColor) {
return widgetColor.resolve(states)
?? themeColor.resolve(states)
?? defaultColor.resolve(states);
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
final Color currentColor;
Color? currentFocusColor;
Color? currentHoverColor;
Color? currentSplashColor;
final ThemeData theme = Theme.of(context);
final ToggleButtonsThemeData toggleButtonsTheme = ToggleButtonsTheme.of(context);
final Set<MaterialState> states = <MaterialState>{
if (selected && onPressed != null) MaterialState.selected,
if (onPressed == null) MaterialState.disabled,
};
final Color currentFillColor = _resolveColor(
states,
_ResolveFillColor(fillColor),
_ResolveFillColor(toggleButtonsTheme.fillColor),
_DefaultFillColor(theme.colorScheme),
);
if (onPressed != null && selected) {
currentColor = selectedColor
?? toggleButtonsTheme.selectedColor
?? theme.colorScheme.primary;
currentFocusColor = focusColor
?? toggleButtonsTheme.focusColor
?? theme.colorScheme.primary.withOpacity(0.12);
currentHoverColor = hoverColor
?? toggleButtonsTheme.hoverColor
?? theme.colorScheme.primary.withOpacity(0.04);
currentSplashColor = splashColor
?? toggleButtonsTheme.splashColor
?? theme.colorScheme.primary.withOpacity(0.16);
} else if (onPressed != null && !selected) {
currentColor = color
?? toggleButtonsTheme.color
?? theme.colorScheme.onSurface.withOpacity(0.87);
currentFocusColor = focusColor
?? toggleButtonsTheme.focusColor
?? theme.colorScheme.onSurface.withOpacity(0.12);
currentHoverColor = hoverColor
?? toggleButtonsTheme.hoverColor
?? theme.colorScheme.onSurface.withOpacity(0.04);
currentSplashColor = splashColor
?? toggleButtonsTheme.splashColor
?? theme.colorScheme.onSurface.withOpacity(0.16);
} else {
currentColor = disabledColor
?? toggleButtonsTheme.disabledColor
?? theme.colorScheme.onSurface.withOpacity(0.38);
}
final TextStyle currentTextStyle = textStyle ?? toggleButtonsTheme.textStyle ?? theme.textTheme.bodyText2!;
final BoxConstraints currentConstraints = constraints ?? toggleButtonsTheme.constraints ?? const BoxConstraints(minWidth: kMinInteractiveDimension, minHeight: kMinInteractiveDimension);
final Widget result = ClipRRect(
borderRadius: clipRadius,
child: RawMaterialButton(
textStyle: currentTextStyle.copyWith(
color: currentColor,
),
constraints: currentConstraints,
elevation: 0.0,
fillColor: currentFillColor,
focusColor: currentFocusColor,
focusElevation: 0,
highlightColor: highlightColor ?? theme.colorScheme.surface.withOpacity(0.0),
highlightElevation: 0.0,
hoverColor: currentHoverColor,
hoverElevation: 0,
splashColor: currentSplashColor,
focusNode: focusNode,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onPressed: onPressed,
mouseCursor: mouseCursor,
child: child,
),
);
return _SelectToggleButton(
key: key,
leadingBorderSide: leadingBorderSide,
borderSide: borderSide,
trailingBorderSide: trailingBorderSide,
borderRadius: borderRadius,
isFirstButton: isFirstButton,
isLastButton: isLastButton,
direction: direction,
verticalDirection: verticalDirection,
child: result,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagProperty('selected',
value: selected,
ifTrue: 'Button is selected',
ifFalse: 'Button is unselected',
));
}
}
@immutable @immutable
class _ResolveFillColor extends MaterialStateProperty<Color?> with Diagnosticable { class _ResolveFillColor extends MaterialStateProperty<Color?> with Diagnosticable {
_ResolveFillColor(this.primary); _ResolveFillColor(this.primary);
...@@ -1028,9 +866,70 @@ class _DefaultFillColor extends MaterialStateProperty<Color> with Diagnosticable ...@@ -1028,9 +866,70 @@ class _DefaultFillColor extends MaterialStateProperty<Color> with Diagnosticable
} }
} }
@immutable
class _ToggleButtonDefaultOverlay extends MaterialStateProperty<Color?> {
_ToggleButtonDefaultOverlay({
required this.selected,
required this.unselected,
this.colorScheme,
this.focusColor,
this.highlightColor,
this.hoverColor,
this.splashColor,
this.disabledColor,
});
final bool selected;
final bool unselected;
final ColorScheme? colorScheme;
final Color? focusColor;
final Color? highlightColor;
final Color? hoverColor;
final Color? splashColor;
final Color? disabledColor;
@override
Color? resolve(Set<MaterialState> states) {
if (selected) {
if (states.contains(MaterialState.hovered)) {
return hoverColor ?? colorScheme?.primary.withOpacity(0.04);
} else if (states.contains(MaterialState.focused)) {
return focusColor ?? colorScheme?.primary.withOpacity(0.12);
} else if (states.contains(MaterialState.pressed)) {
return splashColor ?? colorScheme?.primary.withOpacity(0.16);
}
} else if (unselected) {
if (states.contains(MaterialState.hovered)) {
return hoverColor ?? colorScheme?.onSurface.withOpacity(0.04);
} else if (states.contains(MaterialState.focused)) {
return focusColor ?? colorScheme?.onSurface.withOpacity(0.12);
} else if (states.contains(MaterialState.pressed)) {
return splashColor ?? highlightColor ?? colorScheme?.onSurface.withOpacity(0.16);
}
}
return null;
}
@override
String toString() {
return '''
{
selected:
hovered: $hoverColor, otherwise: ${colorScheme?.primary.withOpacity(0.04)},
focused: $focusColor, otherwise: ${colorScheme?.primary.withOpacity(0.12)},
pressed: $splashColor, otherwise: ${colorScheme?.primary.withOpacity(0.16)},
unselected:
hovered: $hoverColor, otherwise: ${colorScheme?.onSurface.withOpacity(0.04)},
focused: $focusColor, otherwise: ${colorScheme?.onSurface.withOpacity(0.12)},
pressed: $splashColor, otherwise: ${colorScheme?.onSurface.withOpacity(0.16)},
otherwise: null,
}
''';
}
}
class _SelectToggleButton extends SingleChildRenderObjectWidget { class _SelectToggleButton extends SingleChildRenderObjectWidget {
const _SelectToggleButton({ const _SelectToggleButton({
super.key,
required Widget super.child, required Widget super.child,
required this.leadingBorderSide, required this.leadingBorderSide,
required this.borderSide, required this.borderSide,
......
...@@ -12,6 +12,7 @@ import 'package:flutter/rendering.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart'; import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
const double _defaultBorderWidth = 1.0; const double _defaultBorderWidth = 1.0;
...@@ -26,7 +27,7 @@ void main() { ...@@ -26,7 +27,7 @@ void main() {
testWidgets('Initial toggle state is reflected', (WidgetTester tester) async { testWidgets('Initial toggle state is reflected', (WidgetTester tester) async {
TextStyle buttonTextStyle(String text) { TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant( return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, text), of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
} }
...@@ -61,11 +62,10 @@ void main() { ...@@ -61,11 +62,10 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
TextStyle buttonTextStyle(String text) { TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant( return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, text), of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
} }
final List<bool> isSelected = <bool>[false, true]; final List<bool> isSelected = <bool>[false, true];
final ThemeData theme = ThemeData(); final ThemeData theme = ThemeData();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -123,7 +123,7 @@ void main() { ...@@ -123,7 +123,7 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
TextStyle buttonTextStyle(String text) { TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant( return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, text), of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
} }
...@@ -220,14 +220,14 @@ void main() { ...@@ -220,14 +220,14 @@ void main() {
TextStyle textStyle; TextStyle textStyle;
textStyle = tester.widget<DefaultTextStyle>(find.descendant( textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'First child'), of: find.widgetWithText(TextButton, 'First child'),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
expect(textStyle.fontFamily, theme.textTheme.bodyText2!.fontFamily); expect(textStyle.fontFamily, theme.textTheme.bodyText2!.fontFamily);
expect(textStyle.decoration, theme.textTheme.bodyText2!.decoration); expect(textStyle.decoration, theme.textTheme.bodyText2!.decoration);
textStyle = tester.widget<DefaultTextStyle>(find.descendant( textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'Second child'), of: find.widgetWithText(TextButton, 'Second child'),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
expect(textStyle.fontFamily, theme.textTheme.bodyText2!.fontFamily); expect(textStyle.fontFamily, theme.textTheme.bodyText2!.fontFamily);
...@@ -257,7 +257,7 @@ void main() { ...@@ -257,7 +257,7 @@ void main() {
TextStyle textStyle; TextStyle textStyle;
textStyle = tester.widget<DefaultTextStyle>(find.descendant( textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'First child'), of: find.widgetWithText(TextButton, 'First child'),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
expect(textStyle.textBaseline, TextBaseline.ideographic); expect(textStyle.textBaseline, TextBaseline.ideographic);
...@@ -265,7 +265,7 @@ void main() { ...@@ -265,7 +265,7 @@ void main() {
expect(textStyle.color, isNot(Colors.orange)); expect(textStyle.color, isNot(Colors.orange));
textStyle = tester.widget<DefaultTextStyle>(find.descendant( textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'Second child'), of: find.widgetWithText(TextButton, 'Second child'),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
expect(textStyle.textBaseline, TextBaseline.ideographic); expect(textStyle.textBaseline, TextBaseline.ideographic);
...@@ -290,13 +290,13 @@ void main() { ...@@ -290,13 +290,13 @@ void main() {
), ),
); );
final Rect firstRect = tester.getRect(find.byType(RawMaterialButton).at(0)); final Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
expect(firstRect.width, 48.0); expect(firstRect.width, 48.0);
expect(firstRect.height, 48.0); expect(firstRect.height, 48.0);
final Rect secondRect = tester.getRect(find.byType(RawMaterialButton).at(1)); final Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
expect(secondRect.width, 48.0); expect(secondRect.width, 48.0);
expect(secondRect.height, 48.0); expect(secondRect.height, 48.0);
final Rect thirdRect = tester.getRect(find.byType(RawMaterialButton).at(2)); final Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
expect(thirdRect.width, 48.0); expect(thirdRect.width, 48.0);
expect(thirdRect.height, 48.0); expect(thirdRect.height, 48.0);
}); });
...@@ -323,13 +323,13 @@ void main() { ...@@ -323,13 +323,13 @@ void main() {
), ),
); );
Rect firstRect = tester.getRect(find.byType(RawMaterialButton).at(0)); Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
expect(firstRect.width, 50.0); expect(firstRect.width, 50.0);
expect(firstRect.height, 60.0); expect(firstRect.height, 60.0);
Rect secondRect = tester.getRect(find.byType(RawMaterialButton).at(1)); Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
expect(secondRect.width, 50.0); expect(secondRect.width, 50.0);
expect(secondRect.height, 60.0); expect(secondRect.height, 60.0);
Rect thirdRect = tester.getRect(find.byType(RawMaterialButton).at(2)); Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
expect(thirdRect.width, 50.0); expect(thirdRect.width, 50.0);
expect(thirdRect.height, 60.0); expect(thirdRect.height, 60.0);
...@@ -338,6 +338,7 @@ void main() { ...@@ -338,6 +338,7 @@ void main() {
Material( Material(
child: boilerplate( child: boilerplate(
child: ToggleButtons( child: ToggleButtons(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
constraints: const BoxConstraints( constraints: const BoxConstraints(
maxWidth: 20.0, maxWidth: 20.0,
maxHeight: 10.0, maxHeight: 10.0,
...@@ -354,13 +355,13 @@ void main() { ...@@ -354,13 +355,13 @@ void main() {
), ),
); );
firstRect = tester.getRect(find.byType(RawMaterialButton).at(0)); firstRect = tester.getRect(find.byType(TextButton).at(0));
expect(firstRect.width, 20.0); expect(firstRect.width, 20.0);
expect(firstRect.height, 10.0); expect(firstRect.height, 10.0);
secondRect = tester.getRect(find.byType(RawMaterialButton).at(1)); secondRect = tester.getRect(find.byType(TextButton).at(1));
expect(secondRect.width, 20.0); expect(secondRect.width, 20.0);
expect(secondRect.height, 10.0); expect(secondRect.height, 10.0);
thirdRect = tester.getRect(find.byType(RawMaterialButton).at(2)); thirdRect = tester.getRect(find.byType(TextButton).at(2));
expect(thirdRect.width, 20.0); expect(thirdRect.width, 20.0);
expect(thirdRect.height, 10.0); expect(thirdRect.height, 10.0);
}); });
...@@ -370,13 +371,13 @@ void main() { ...@@ -370,13 +371,13 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
TextStyle buttonTextStyle(String text) { TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant( return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, text), of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
} }
IconTheme iconTheme(IconData icon) { IconTheme iconTheme(IconData icon) {
return tester.widget(find.descendant( return tester.widget(find.descendant(
of: find.widgetWithIcon(RawMaterialButton, icon), of: find.widgetWithIcon(TextButton, icon),
matching: find.byType(IconTheme), matching: find.byType(IconTheme),
)); ));
} }
...@@ -468,13 +469,13 @@ void main() { ...@@ -468,13 +469,13 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
TextStyle buttonTextStyle(String text) { TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant( return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, text), of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
} }
IconTheme iconTheme(IconData icon) { IconTheme iconTheme(IconData icon) {
return tester.widget(find.descendant( return tester.widget(find.descendant(
of: find.widgetWithIcon(RawMaterialButton, icon), of: find.widgetWithIcon(TextButton, icon),
matching: find.byType(IconTheme), matching: find.byType(IconTheme),
)); ));
} }
...@@ -574,7 +575,7 @@ void main() { ...@@ -574,7 +575,7 @@ void main() {
); );
final Material material = tester.widget<Material>(find.descendant( final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.byType(Material), matching: find.byType(Material),
)); ));
expect( expect(
...@@ -603,7 +604,7 @@ void main() { ...@@ -603,7 +604,7 @@ void main() {
); );
final Material material = tester.widget<Material>(find.descendant( final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.byType(Material), matching: find.byType(Material),
)); ));
expect( expect(
...@@ -631,7 +632,7 @@ void main() { ...@@ -631,7 +632,7 @@ void main() {
); );
final Material material = tester.widget<Material>(find.descendant( final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.byType(Material), matching: find.byType(Material),
)); ));
expect( expect(
...@@ -661,7 +662,7 @@ void main() { ...@@ -661,7 +662,7 @@ void main() {
); );
final Material material = tester.widget<Material>(find.descendant( final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.byType(Material), matching: find.byType(Material),
)); ));
expect(material.color, customFillColor); expect(material.color, customFillColor);
...@@ -672,7 +673,7 @@ void main() { ...@@ -672,7 +673,7 @@ void main() {
Material buttonColor(String text) { Material buttonColor(String text) {
return tester.widget<Material>( return tester.widget<Material>(
find.descendant( find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.widgetWithText(Material, text), matching: find.widgetWithText(Material, text),
), ),
); );
...@@ -727,7 +728,7 @@ void main() { ...@@ -727,7 +728,7 @@ void main() {
Material buttonColor(String text) { Material buttonColor(String text) {
return tester.widget<Material>( return tester.widget<Material>(
find.descendant( find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.widgetWithText(Material, text), matching: find.widgetWithText(Material, text),
), ),
); );
...@@ -1936,4 +1937,115 @@ void main() { ...@@ -1936,4 +1937,115 @@ void main() {
await hoverGesture.removePointer(); await hoverGesture.removePointer();
}); });
testWidgets('Toggle buttons height matches MaterialTapTargetSize.padded height', (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(TextButton).at(0));
expect(firstRect.height, 48.0);
final Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
expect(secondRect.height, 48.0);
final Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
expect(thirdRect.height, 48.0);
});
testWidgets('Toggle buttons constraints size does not affect minimum input padding', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/97302
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
Material(
child: 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.isFocusable,
],
actions: <SemanticsAction>[
SemanticsAction.tap,
],
rect: const Rect.fromLTRB(0.0, 0.0, 87.0, 48.0),
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.isEnabled,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[
SemanticsAction.tap,
],
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0)
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.isEnabled,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[
SemanticsAction.tap,
],
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
),
],
),
ignoreId: true,
ignoreRect: true,
ignoreTransform: true,
),
);
semantics.dispose();
});
} }
...@@ -142,7 +142,7 @@ void main() { ...@@ -142,7 +142,7 @@ void main() {
TextStyle textStyle; TextStyle textStyle;
textStyle = tester.widget<DefaultTextStyle>(find.descendant( textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'First child'), of: find.widgetWithText(TextButton, 'First child'),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
expect(textStyle.textBaseline, TextBaseline.ideographic); expect(textStyle.textBaseline, TextBaseline.ideographic);
...@@ -150,7 +150,7 @@ void main() { ...@@ -150,7 +150,7 @@ void main() {
expect(textStyle.color, isNot(Colors.orange)); expect(textStyle.color, isNot(Colors.orange));
textStyle = tester.widget<DefaultTextStyle>(find.descendant( textStyle = tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, 'Second child'), of: find.widgetWithText(TextButton, 'Second child'),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
expect(textStyle.textBaseline, TextBaseline.ideographic); expect(textStyle.textBaseline, TextBaseline.ideographic);
...@@ -171,6 +171,7 @@ void main() { ...@@ -171,6 +171,7 @@ void main() {
), ),
), ),
child: ToggleButtons( child: ToggleButtons(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
isSelected: const <bool>[false, false, false], isSelected: const <bool>[false, false, false],
onPressed: (int index) {}, onPressed: (int index) {},
children: const <Widget>[ children: const <Widget>[
...@@ -184,13 +185,13 @@ void main() { ...@@ -184,13 +185,13 @@ void main() {
), ),
); );
Rect firstRect = tester.getRect(find.byType(RawMaterialButton).at(0)); Rect firstRect = tester.getRect(find.byType(TextButton).at(0));
expect(firstRect.width, 50.0); expect(firstRect.width, 50.0);
expect(firstRect.height, 60.0); expect(firstRect.height, 60.0);
Rect secondRect = tester.getRect(find.byType(RawMaterialButton).at(1)); Rect secondRect = tester.getRect(find.byType(TextButton).at(1));
expect(secondRect.width, 50.0); expect(secondRect.width, 50.0);
expect(secondRect.height, 60.0); expect(secondRect.height, 60.0);
Rect thirdRect = tester.getRect(find.byType(RawMaterialButton).at(2)); Rect thirdRect = tester.getRect(find.byType(TextButton).at(2));
expect(thirdRect.width, 50.0); expect(thirdRect.width, 50.0);
expect(thirdRect.height, 60.0); expect(thirdRect.height, 60.0);
...@@ -206,6 +207,7 @@ void main() { ...@@ -206,6 +207,7 @@ void main() {
), ),
), ),
child: ToggleButtons( child: ToggleButtons(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
isSelected: const <bool>[false, false, false], isSelected: const <bool>[false, false, false],
onPressed: (int index) {}, onPressed: (int index) {},
children: const <Widget>[ children: const <Widget>[
...@@ -219,13 +221,13 @@ void main() { ...@@ -219,13 +221,13 @@ void main() {
), ),
); );
firstRect = tester.getRect(find.byType(RawMaterialButton).at(0)); firstRect = tester.getRect(find.byType(TextButton).at(0));
expect(firstRect.width, 20.0); expect(firstRect.width, 20.0);
expect(firstRect.height, 10.0); expect(firstRect.height, 10.0);
secondRect = tester.getRect(find.byType(RawMaterialButton).at(1)); secondRect = tester.getRect(find.byType(TextButton).at(1));
expect(secondRect.width, 20.0); expect(secondRect.width, 20.0);
expect(secondRect.height, 10.0); expect(secondRect.height, 10.0);
thirdRect = tester.getRect(find.byType(RawMaterialButton).at(2)); thirdRect = tester.getRect(find.byType(TextButton).at(2));
expect(thirdRect.width, 20.0); expect(thirdRect.width, 20.0);
expect(thirdRect.height, 10.0); expect(thirdRect.height, 10.0);
}); });
...@@ -235,13 +237,13 @@ void main() { ...@@ -235,13 +237,13 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
TextStyle buttonTextStyle(String text) { TextStyle buttonTextStyle(String text) {
return tester.widget<DefaultTextStyle>(find.descendant( return tester.widget<DefaultTextStyle>(find.descendant(
of: find.widgetWithText(RawMaterialButton, text), of: find.widgetWithText(TextButton, text),
matching: find.byType(DefaultTextStyle), matching: find.byType(DefaultTextStyle),
)).style; )).style;
} }
IconTheme iconTheme(IconData icon) { IconTheme iconTheme(IconData icon) {
return tester.widget(find.descendant( return tester.widget(find.descendant(
of: find.widgetWithIcon(RawMaterialButton, icon), of: find.widgetWithIcon(TextButton, icon),
matching: find.byType(IconTheme), matching: find.byType(IconTheme),
)); ));
} }
...@@ -356,7 +358,7 @@ void main() { ...@@ -356,7 +358,7 @@ void main() {
); );
final Material material = tester.widget<Material>(find.descendant( final Material material = tester.widget<Material>(find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.byType(Material), matching: find.byType(Material),
)); ));
expect(material.color, customFillColor); expect(material.color, customFillColor);
...@@ -367,7 +369,7 @@ void main() { ...@@ -367,7 +369,7 @@ void main() {
Material buttonColor(String text) { Material buttonColor(String text) {
return tester.widget<Material>( return tester.widget<Material>(
find.descendant( find.descendant(
of: find.byType(RawMaterialButton), of: find.byType(TextButton),
matching: find.widgetWithText(Material, text), matching: find.widgetWithText(Material, text),
), ),
); );
...@@ -476,7 +478,6 @@ void main() { ...@@ -476,7 +478,6 @@ void main() {
inkFeatures, inkFeatures,
paints paints
..circle(color: splashColor) ..circle(color: splashColor)
..rect(color: highlightColor),
); );
await touchGesture.up(); await touchGesture.up();
...@@ -507,7 +508,6 @@ void main() { ...@@ -507,7 +508,6 @@ void main() {
await hoverGesture.removePointer(); await hoverGesture.removePointer();
}); });
testWidgets( testWidgets(
'Theme border width and border colors for enabled, selected and disabled states', 'Theme border width and border colors for enabled, selected and disabled states',
(WidgetTester tester) async { (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment