// 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']) 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'; import '../widgets/semantics_tester.dart'; const double _defaultBorderWidth = 1.0; Widget boilerplate({required 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(TextButton, 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(TextButton, 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(TextButton, 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 and isSelected properties have to be the same length', (WidgetTester tester) async { await expectLater( () => tester.pumpWidget( Material( child: boilerplate( child: ToggleButtons( isSelected: const <bool>[false], children: const <Widget>[ Text('First child'), Text('Second child'), ], ), ), ), ), throwsA(isAssertionError.having( (AssertionError error) => error.toString(), '.toString()', allOf( contains('children.length'), contains('isSelected.length'), ), )), ); }, ); testWidgets('Default text style is applied', (WidgetTester tester) async { final ThemeData theme = ThemeData(); await tester.pumpWidget( 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(TextButton, '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(TextButton, '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(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( 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.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( 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(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: ToggleButtons( tapTargetSize: MaterialTapTargetSize.shrinkWrap, constraints: const BoxConstraints( maxWidth: 20.0, maxHeight: 10.0, ), isSelected: const <bool>[false, false, false], onPressed: (int index) {}, children: const <Widget>[ Icon(Icons.check), Icon(Icons.access_alarm), Icon(Icons.cake), ], ), ), ), ); firstRect = tester.getRect(find.byType(TextButton).at(0)); expect(firstRect.width, 20.0); expect(firstRect.height, 10.0); secondRect = tester.getRect(find.byType(TextButton).at(1)); expect(secondRect.width, 20.0); expect(secondRect.height, 10.0); thirdRect = tester.getRect(find.byType(TextButton).at(2)); expect(thirdRect.width, 20.0); expect(thirdRect.height, 10.0); }); testWidgets( 'Default text/icon colors for enabled, selected and disabled states', (WidgetTester tester) async { TextStyle buttonTextStyle(String text) { return tester.widget<DefaultTextStyle>(find.descendant( of: find.widgetWithText(TextButton, text), matching: find.byType(DefaultTextStyle), )).style; } IconTheme iconTheme(IconData icon) { return tester.widget(find.descendant( of: find.widgetWithIcon(TextButton, icon), matching: find.byType(IconTheme), )); } final ThemeData theme = ThemeData(); await tester.pumpWidget( 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(TextButton, text), matching: find.byType(DefaultTextStyle), )).style; } IconTheme iconTheme(IconData icon) { return tester.widget(find.descendant( of: find.widgetWithIcon(TextButton, icon), matching: find.byType(IconTheme), )); } final ThemeData theme = ThemeData(); const Color enabledColor = Colors.lime; const Color selectedColor = Colors.green; const Color disabledColor = Colors.yellow; // Tests are ineffective if the custom colors are the same as the theme's expect(theme.colorScheme.onSurface, isNot(enabledColor)); expect(theme.colorScheme.primary, isNot(selectedColor)); expect(theme.colorScheme.onSurface.withOpacity(0.38), isNot(disabledColor)); await tester.pumpWidget( 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(TextButton), matching: find.byType(Material), )); expect( material.color, theme.colorScheme.surface.withOpacity(0.0), ); expect(material.type, MaterialType.button); }); testWidgets('Default button fillColor - selected', (WidgetTester tester) async { final ThemeData theme = ThemeData(); await tester.pumpWidget( 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(TextButton), matching: find.byType(Material), )); expect( material.color, theme.colorScheme.primary.withOpacity(0.12), ); expect(material.type, MaterialType.button); }); testWidgets('Default button fillColor - disabled', (WidgetTester tester) async { final ThemeData theme = ThemeData(); await tester.pumpWidget( Material( child: boilerplate( child: ToggleButtons( isSelected: const <bool>[true], children: <Widget>[ Row(children: const <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( 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(TextButton), matching: find.byType(Material), )); expect(material.color, customFillColor); expect(material.type, MaterialType.button); }); testWidgets('Custom button fillColor - Non MaterialState', (WidgetTester tester) async { Material buttonColor(String text) { return tester.widget<Material>( find.descendant( of: find.byType(TextButton), matching: find.widgetWithText(Material, text), ), ); } final ThemeData theme = ThemeData(); const Color selectedFillColor = Colors.yellow; await tester.pumpWidget( Material( child: 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( Material( child: boilerplate( child: ToggleButtons( fillColor: selectedFillColor, isSelected: const <bool>[false, true], children: const <Widget>[ Text('First child'), Text('Second child'), ], ), ), ), ); await tester.pumpAndSettle(); expect(buttonColor('First child').color, theme.colorScheme.surface.withOpacity(0.0)); expect(buttonColor('Second child').color, theme.colorScheme.surface.withOpacity(0.0)); }); testWidgets('Custom button fillColor - MaterialState', (WidgetTester tester) async { Material buttonColor(String text) { return tester.widget<Material>( find.descendant( of: find.byType(TextButton), matching: find.widgetWithText(Material, text), ), ); } const Color selectedFillColor = Colors.orange; const Color defaultFillColor = Colors.blue; Color getFillColor(Set<MaterialState> states) { if (states.contains(MaterialState.selected)) { return selectedFillColor; } return defaultFillColor; } await tester.pumpWidget( Material( child: boilerplate( child: ToggleButtons( fillColor: MaterialStateColor.resolveWith(getFillColor), isSelected: const <bool>[false, true], onPressed: (int index) {}, children: const <Widget>[ Text('First child'), Text('Second child'), ], ), ), ), ); await tester.pumpAndSettle(); expect(buttonColor('First child').color, defaultFillColor); expect(buttonColor('Second child').color, selectedFillColor); // disabled await tester.pumpWidget( Material( child: boilerplate( child: ToggleButtons( fillColor: MaterialStateColor.resolveWith(getFillColor), isSelected: const <bool>[false, true], children: const <Widget>[ Text('First child'), Text('Second child'), ], ), ), ), ); await tester.pumpAndSettle(); expect(buttonColor('First child').color, defaultFillColor); expect(buttonColor('Second child').color, defaultFillColor); }); testWidgets('Default InkWell colors - unselected', (WidgetTester tester) async { final ThemeData theme = ThemeData(); final FocusNode focusNode = FocusNode(); await tester.pumpWidget( 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(); await hoverGesture.moveTo(Offset.zero); inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) { return object.runtimeType.toString() == '_RenderInkFeatures'; }); expect( inkFeatures, paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.04)), ); // focusColor focusNode.requestFocus(); await tester.pumpAndSettle(); inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) { return object.runtimeType.toString() == '_RenderInkFeatures'; }); expect(inkFeatures, paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.12))); await hoverGesture.removePointer(); }); 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.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: theme.colorScheme.primary.withOpacity(0.12))); await hoverGesture.removePointer(); }); 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.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(); }); 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 // physical model ..path() ..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 // physical model ..path() ..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 // physical model ..path() ..path( style: PaintingStyle.stroke, color: theme.colorScheme.onSurface.withOpacity(0.12), strokeWidth: defaultBorderWidth, ), ); }, ); testWidgets( 'Custom border width and border colors for enabled, selected and disabled states', (WidgetTester tester) async { const Color borderColor = Color(0xff4caf50); const Color selectedBorderColor = Color(0xffcddc39); const Color disabledBorderColor = Color(0xffffeb3b); const double customWidth = 2.0; await tester.pumpWidget( 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 // physical model ..path() ..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 // physical model ..path() ..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 // physical model ..path() ..path( style: PaintingStyle.stroke, color: disabledBorderColor, strokeWidth: customWidth, ), ); }, ); testWidgets('Height of segmented control is determined by tallest widget', (WidgetTester tester) async { final List<Widget> children = <Widget>[ Container( constraints: const BoxConstraints.tightFor(height: 100.0), ), Container( constraints: const BoxConstraints.tightFor(height: 400.0), // tallest widget ), Container( constraints: const BoxConstraints.tightFor(height: 200.0), ), ]; await tester.pumpWidget( 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, moreOrLessEquals(materialButtonDy - 2.0, epsilon: 0.001)); expect(firstToggleButtonDy, moreOrLessEquals(textDy - 4.0, epsilon: 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 // physical model ..path() // leading side, top and bottom - enabled ..path( style: PaintingStyle.stroke, color: theme.colorScheme.onSurface.withOpacity(0.12), strokeWidth: _defaultBorderWidth, ), ); // The middle buttons paint a leading side path first, followed by a // top and bottom side path expect( toggleButtonRenderObject[1], paints // physical model ..path() // leading side - selected ..path( style: PaintingStyle.stroke, color: theme.colorScheme.onSurface.withOpacity(0.12), strokeWidth: _defaultBorderWidth, ) // top and bottom - selected ..path( style: PaintingStyle.stroke, color: theme.colorScheme.onSurface.withOpacity(0.12), strokeWidth: _defaultBorderWidth, ), ); // The last button paints a leading side path first, followed by // a trailing, top and bottom side path expect( toggleButtonRenderObject[2], paints // physical model ..path() // leading side - selected, since previous button is selected ..path( style: PaintingStyle.stroke, color: theme.colorScheme.onSurface.withOpacity(0.12), strokeWidth: _defaultBorderWidth, ) // trailing side, top and bottom - enabled ..path( style: PaintingStyle.stroke, color: theme.colorScheme.onSurface.withOpacity(0.12), strokeWidth: _defaultBorderWidth, ), ); }, ); testWidgets( 'Properly draws borders based on state when direction is vertical and verticalDirection is down.', (WidgetTester tester) async { final ThemeData theme = ThemeData(); await tester.pumpWidget( Material( child: 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( Material( child: 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('Tap target size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async { Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) { return Theme( data: ThemeData(materialTapTargetSize: tapTargetSize), child: Material( child: boilerplate( 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, 34.0)); }); testWidgets('Tap target size is configurable', (WidgetTester tester) async { Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) { return Material( child: 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(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('Tap target size is configurable for vertical axis', (WidgetTester tester) async { Widget buildFrame(MaterialTapTargetSize tapTargetSize, Key key) { return Material( child: 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('Border radius paint test when there is only one button', (WidgetTester tester) async { final ThemeData theme = ThemeData(); await tester.pumpWidget( Material( child: 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('toggle_buttons.oneButton.boardsPaint.png'), ); }); testWidgets('Border radius paint test when Radius.x or Radius.y equal 0.0', (WidgetTester tester) async { await tester.pumpWidget( Material( child: 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('toggle_buttons.oneButton.boardsPaint2.png'), ); }); testWidgets('ToggleButtons implements debugFillProperties', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); ToggleButtons( direction: Axis.vertical, verticalDirection: VerticalDirection.up, borderWidth: 3.0, color: Colors.green, selectedBorderColor: Colors.pink, disabledColor: Colors.blue, disabledBorderColor: Colors.yellow, borderRadius: const BorderRadius.all(Radius.circular(7.0)), isSelected: const <bool>[false, true, false], onPressed: (int index) {}, children: const <Widget>[ Text('First child'), Text('Second child'), Text('Third child'), ], ).debugFillProperties(builder); final List<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()).toList(); expect(description, <String>[ 'Buttons are enabled', 'color: MaterialColor(primary value: Color(0xff4caf50))', 'disabledColor: MaterialColor(primary value: Color(0xff2196f3))', 'selectedBorderColor: MaterialColor(primary value: Color(0xffe91e63))', 'disabledBorderColor: MaterialColor(primary value: Color(0xffffeb3b))', 'borderRadius: BorderRadius.circular(7.0)', 'borderWidth: 3.0', 'direction: Axis.vertical', 'verticalDirection: VerticalDirection.up', ]); }); testWidgets('ToggleButtons changes mouse cursor when the button is hovered', (WidgetTester tester) async { await tester.pumpWidget( 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'))); 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); }); testWidgets('ToggleButtons focus, hover, and highlight elevations are 0', (WidgetTester tester) async { final List<FocusNode> focusNodes = <FocusNode>[FocusNode(), FocusNode()]; await tester.pumpWidget( Material( child: 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(); }); 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(); }); }