diff --git a/dev/tools/gen_defaults/lib/menu_template.dart b/dev/tools/gen_defaults/lib/menu_template.dart index 50dc6c4e2f..6d2d6b4430 100644 --- a/dev/tools/gen_defaults/lib/menu_template.dart +++ b/dev/tools/gen_defaults/lib/menu_template.dart @@ -65,6 +65,7 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { final BuildContext context; late final ColorScheme _colors = Theme.of(context).colorScheme; + late final TextTheme _textTheme = Theme.of(context).textTheme; @override MaterialStateProperty? get backgroundColor { @@ -180,7 +181,9 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty get textStyle { - return MaterialStatePropertyAll(${textStyle('md.comp.list.list-item.label-text')}); + // TODO(tahatesser): This is taken from https://m3.material.io/components/menus/specs + // Update this when the token is available. + return MaterialStatePropertyAll(_textTheme.labelLarge); } @override diff --git a/examples/api/test/material/menu_anchor/menu_accelerator_label.0_test.dart b/examples/api/test/material/menu_anchor/menu_accelerator_label.0_test.dart index 89cadb7db3..5e69f5d8c1 100644 --- a/examples/api/test/material/menu_anchor/menu_accelerator_label.0_test.dart +++ b/examples/api/test/material/menu_anchor/menu_accelerator_label.0_test.dart @@ -27,10 +27,11 @@ void main() { await tester.pump(); expect(find.text('About', findRichText: true), findsOneWidget); - expect( - tester.getRect(findMenu('About')), - equals(const Rect.fromLTRB(4.0, 48.0, 110.5, 208.0)), - ); + expect(tester.getRect(findMenu('About')).left, equals(4.0)); + expect(tester.getRect(findMenu('About')).top, equals(48.0)); + expect(tester.getRect(findMenu('About')).right, closeTo(98.5, 0.1)); + expect(tester.getRect(findMenu('About')).bottom, equals(208.0)); + expect(find.text('Save', findRichText: true), findsOneWidget); expect(find.text('Quit', findRichText: true), findsOneWidget); expect(find.text('Magnify', findRichText: true), findsNothing); diff --git a/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart b/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart index 0ac0e43ddb..fdb3eeea4e 100644 --- a/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart +++ b/examples/api/test/material/menu_anchor/menu_anchor.1_test.dart @@ -21,13 +21,19 @@ void main() { await tester.tapAt(const Offset(100, 200), buttons: kSecondaryButton); await tester.pumpAndSettle(); - expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(100.0, 200.0, 433.0, 360.0))); + expect(tester.getRect(findMenu()).left, equals(100.0)); + expect(tester.getRect(findMenu()).top, equals(200.0)); + expect(tester.getRect(findMenu()).right, closeTo(389.8, 0.1)); + expect(tester.getRect(findMenu()).bottom, equals(360.0)); // Make sure tapping in a different place causes the menu to move. await tester.tapAt(const Offset(200, 100), buttons: kSecondaryButton); await tester.pump(); - expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(200.0, 100.0, 533.0, 260.0))); + expect(tester.getRect(findMenu()).left, equals(200.0)); + expect(tester.getRect(findMenu()).top, equals(100.0)); + expect(tester.getRect(findMenu()).right, closeTo(489.8, 0.1)); + expect(tester.getRect(findMenu()).bottom, equals(260.0)); expect(find.text(example.MenuEntry.about.label), findsOneWidget); expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget); diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 51a972a339..27baf5951b 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -229,7 +229,7 @@ class DropdownMenu extends StatefulWidget { /// The text style for the [TextField] of the [DropdownMenu]; /// - /// Defaults to the overall theme's [TextTheme.labelLarge] + /// Defaults to the overall theme's [TextTheme.bodyLarge] /// if the dropdown menu theme's value is null. final TextStyle? textStyle; @@ -916,7 +916,7 @@ class _DropdownMenuDefaultsM3 extends DropdownMenuThemeData { late final ThemeData _theme = Theme.of(context); @override - TextStyle? get textStyle => _theme.textTheme.labelLarge; + TextStyle? get textStyle => _theme.textTheme.bodyLarge; @override MenuStyle get menuStyle { diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart index f2dfeeea5e..3ad5543407 100644 --- a/packages/flutter/lib/src/material/menu_anchor.dart +++ b/packages/flutter/lib/src/material/menu_anchor.dart @@ -27,6 +27,7 @@ import 'menu_style.dart'; import 'menu_theme.dart'; import 'radio.dart'; import 'text_button.dart'; +import 'text_theme.dart'; import 'theme.dart'; import 'theme_data.dart'; @@ -3676,6 +3677,7 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { final BuildContext context; late final ColorScheme _colors = Theme.of(context).colorScheme; + late final TextTheme _textTheme = Theme.of(context).textTheme; @override MaterialStateProperty? get backgroundColor { @@ -3791,7 +3793,9 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { @override MaterialStateProperty get textStyle { - return MaterialStatePropertyAll(Theme.of(context).textTheme.bodyLarge); + // TODO(tahatesser): This is taken from https://m3.material.io/components/menus/specs + // Update this when the token is available. + return MaterialStatePropertyAll(_textTheme.labelLarge); } @override diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index 45802fdb91..5d8a2c5314 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - import 'dart:ui'; import 'package:flutter/material.dart'; @@ -39,15 +38,19 @@ void main() { await tester.pumpWidget(buildTest(themeData, menuChildren)); final EditableText editableText = tester.widget(find.byType(EditableText)); - expect(editableText.style.color, themeData.textTheme.labelLarge!.color); - expect(editableText.style.background, themeData.textTheme.labelLarge!.background); - expect(editableText.style.shadows, themeData.textTheme.labelLarge!.shadows); - expect(editableText.style.decoration, themeData.textTheme.labelLarge!.decoration); - expect(editableText.style.locale, themeData.textTheme.labelLarge!.locale); - expect(editableText.style.wordSpacing, themeData.textTheme.labelLarge!.wordSpacing); + expect(editableText.style.color, themeData.textTheme.bodyLarge!.color); + expect(editableText.style.background, themeData.textTheme.bodyLarge!.background); + expect(editableText.style.shadows, themeData.textTheme.bodyLarge!.shadows); + expect(editableText.style.decoration, themeData.textTheme.bodyLarge!.decoration); + expect(editableText.style.locale, themeData.textTheme.bodyLarge!.locale); + expect(editableText.style.wordSpacing, themeData.textTheme.bodyLarge!.wordSpacing); + expect(editableText.style.fontSize, 16.0); + expect(editableText.style.height, 1.5); final TextField textField = tester.widget(find.byType(TextField)); expect(textField.decoration?.border, const OutlineInputBorder()); + expect(textField.style?.fontSize, 16.0); + expect(textField.style?.height, 1.5); await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_drop_down).first); await tester.pump(); @@ -74,6 +77,8 @@ void main() { expect(material.elevation, 0.0); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle?.color, themeData.colorScheme.onSurface); + expect(material.textStyle?.fontSize, 14.0); + expect(material.textStyle?.height, 1.43); }); testWidgets('DropdownMenu can be disabled', (WidgetTester tester) async { @@ -177,7 +182,7 @@ void main() { final Finder textField = find.byType(TextField); final double anchorWidth = tester.getSize(textField).width; - expect(anchorWidth, 195.0); + expect(anchorWidth, closeTo(180.5, 0.1)); await tester.tap(find.byType(DropdownMenu)); await tester.pumpAndSettle(); @@ -187,7 +192,7 @@ void main() { matching: find.byType(Material), ); final double menuWidth = tester.getSize(menuMaterial).width; - expect(menuWidth, 195.0); + expect(menuWidth, closeTo(180.5, 0.1)); // The text field should have same width as the menu // when the width property is not null. @@ -391,7 +396,8 @@ void main() { matching: find.byType(Padding), ).first; final Size menuViewSize = tester.getSize(menuView); - expect(menuViewSize, const Size(195.0, 304.0)); // 304 = 288 + vertical padding(2 * 8) + expect(menuViewSize.width, closeTo(180.6, 0.1)); + expect(menuViewSize.height, equals(304.0)); // 304 = 288 + vertical padding(2 * 8) // Constrains the menu height. await tester.pumpWidget(Container()); @@ -407,7 +413,8 @@ void main() { ).first; final Size updatedMenuSize = tester.getSize(updatedMenu); - expect(updatedMenuSize, const Size(195.0, 100.0)); + expect(updatedMenuSize.width, closeTo(180.6, 0.1)); + expect(updatedMenuSize.height, equals(100.0)); }); testWidgets('The text in the menu button should be aligned with the text of ' @@ -1518,6 +1525,52 @@ void main() { expect(find.text('Item 5').hitTestable(), findsOneWidget); }); + // This is a regression test for https://github.com/flutter/flutter/issues/131676. + testWidgets('Material3 - DropdownMenu uses correct text styles', (WidgetTester tester) async { + const TextStyle inputTextThemeStyle = TextStyle( + fontSize: 18.5, + fontStyle: FontStyle.italic, + wordSpacing: 1.2, + decoration: TextDecoration.lineThrough, + ); + const TextStyle menuItemTextThemeStyle = TextStyle( + fontSize: 20.5, + fontStyle: FontStyle.italic, + wordSpacing: 2.1, + decoration: TextDecoration.underline, + ); + final ThemeData themeData = ThemeData( + useMaterial3: true, + textTheme: const TextTheme( + bodyLarge: inputTextThemeStyle, + labelLarge: menuItemTextThemeStyle, + ), + ); + await tester.pumpWidget(buildTest(themeData, menuChildren)); + + // Test input text style uses the TextTheme.bodyLarge. + final EditableText editableText = tester.widget(find.byType(EditableText)); + expect(editableText.style.fontSize, inputTextThemeStyle.fontSize); + expect(editableText.style.fontStyle, inputTextThemeStyle.fontStyle); + expect(editableText.style.wordSpacing, inputTextThemeStyle.wordSpacing); + expect(editableText.style.decoration, inputTextThemeStyle.decoration); + + // Open the menu. + await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_drop_down).first); + await tester.pump(); + + final Finder buttonMaterial = find.descendant( + of: find.byType(TextButton), + matching: find.byType(Material), + ).last; + + // Test menu item text style uses the TextTheme.labelLarge. + final Material material = tester.widget(buttonMaterial); + expect(material.textStyle?.fontSize, menuItemTextThemeStyle.fontSize); + expect(material.textStyle?.fontStyle, menuItemTextThemeStyle.fontStyle); + expect(material.textStyle?.wordSpacing, menuItemTextThemeStyle.wordSpacing); + expect(material.textStyle?.decoration, menuItemTextThemeStyle.decoration); + }); } enum TestMenu { diff --git a/packages/flutter/test/material/menu_anchor_test.dart b/packages/flutter/test/material/menu_anchor_test.dart index e7a745490f..5416882eab 100644 --- a/packages/flutter/test/material/menu_anchor_test.dart +++ b/packages/flutter/test/material/menu_anchor_test.dart @@ -237,7 +237,7 @@ void main() { ); }); - testWidgets('menu defaults colors', (WidgetTester tester) async { + testWidgets('Menu defaults', (WidgetTester tester) async { final ThemeData themeData = ThemeData(); await tester.pumpWidget( MaterialApp( @@ -278,6 +278,8 @@ void main() { expect(material.elevation, 0.0); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle?.color, themeData.colorScheme.onSurface); + expect(material.textStyle?.fontSize, 14.0); + expect(material.textStyle?.height, 1.43); // vertical menu await tester.tap(find.text(TestMenu.mainMenu1.label)); @@ -305,6 +307,8 @@ void main() { expect(material.elevation, 0.0); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle?.color, themeData.colorScheme.onSurface); + expect(material.textStyle?.fontSize, 14.0); + expect(material.textStyle?.height, 1.43); await tester.tap(find.text(TestMenu.mainMenu0.label)); await tester.pump(); @@ -315,7 +319,7 @@ void main() { expect(iconRichText.text.style?.color, themeData.colorScheme.onSurfaceVariant); }); - testWidgets('menu defaults - disabled', (WidgetTester tester) async { + testWidgets('Menu defaults - disabled', (WidgetTester tester) async { final ThemeData themeData = ThemeData(); await tester.pumpWidget( MaterialApp( @@ -3205,6 +3209,7 @@ void main() { style: SubmenuButton.styleFrom(fixedSize: const Size(88.0, 36.0)), menuChildren: [ MenuItemButton( + style: SubmenuButton.styleFrom(fixedSize: const Size(120.0, 36.0)), child: const Text('Item 0'), onPressed: () {}, ), @@ -3250,17 +3255,17 @@ void main() { ), TestSemantics( id: 6, - rect: const Rect.fromLTRB(0.0, 0.0, 123.0, 64.0), + rect: const Rect.fromLTRB(0.0, 0.0, 120.0, 64.0), children: [ TestSemantics( id: 7, - rect: const Rect.fromLTRB(0.0, 0.0, 123.0, 48.0), + rect: const Rect.fromLTRB(0.0, 0.0, 120.0, 48.0), flags: [SemanticsFlag.hasImplicitScrolling], children: [ TestSemantics( id: 8, label: 'Item 0', - rect: const Rect.fromLTRB(0.0, 0.0, 123.0, 48.0), + rect: const Rect.fromLTRB(0.0, 0.0, 120.0, 48.0), flags: [SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isFocusable], actions: [SemanticsAction.tap], ), @@ -3320,6 +3325,62 @@ void main() { semantics.dispose(); }); }); + + // This is a regression test for https://github.com/flutter/flutter/issues/131676. + testWidgets('Material3 - Menu uses correct text styles', (WidgetTester tester) async { + const TextStyle menuTextStyle = TextStyle( + fontSize: 18.5, + fontStyle: FontStyle.italic, + wordSpacing: 1.2, + decoration: TextDecoration.lineThrough, + ); + final ThemeData themeData = ThemeData( + textTheme: const TextTheme( + labelLarge: menuTextStyle, + ) + ); + await tester.pumpWidget( + MaterialApp( + theme: themeData, + home: Material( + child: MenuBar( + controller: controller, + children: createTestMenus( + onPressed: onPressed, + onOpen: onOpen, + onClose: onClose, + ), + ), + ), + ), + ); + + // Test menu button text style uses the TextTheme.labelLarge. + Finder buttonMaterial = find.descendant( + of: find.byType(TextButton), + matching: find.byType(Material), + ).first; + Material material = tester.widget(buttonMaterial); + expect(material.textStyle?.fontSize, menuTextStyle.fontSize); + expect(material.textStyle?.fontStyle, menuTextStyle.fontStyle); + expect(material.textStyle?.wordSpacing, menuTextStyle.wordSpacing); + expect(material.textStyle?.decoration, menuTextStyle.decoration); + + // Open the menu. + await tester.tap(find.text(TestMenu.mainMenu1.label)); + await tester.pump(); + + // Test menu item text style uses the TextTheme.labelLarge. + buttonMaterial = find.descendant( + of: find.widgetWithText(TextButton, TestMenu.subMenu10.label), + matching: find.byType(Material), + ).first; + material = tester.widget(buttonMaterial); + expect(material.textStyle?.fontSize, menuTextStyle.fontSize); + expect(material.textStyle?.fontStyle, menuTextStyle.fontStyle); + expect(material.textStyle?.wordSpacing, menuTextStyle.wordSpacing); + expect(material.textStyle?.decoration, menuTextStyle.decoration); + }); } List createTestMenus({ diff --git a/packages/flutter/test/material/menu_style_test.dart b/packages/flutter/test/material/menu_style_test.dart index dc501b68bc..74ce6b8887 100644 --- a/packages/flutter/test/material/menu_style_test.dart +++ b/packages/flutter/test/material/menu_style_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -81,8 +82,10 @@ void main() { expect(tester.getRect(findMenuPanels().first).size, equals(const Size(600.0, 60.0))); // MenuTheme affects menus. - expect(tester.getRect(findMenuPanels().at(1)), equals(const Rect.fromLTRB(104.0, 54.0, 204.0, 154.0))); - expect(tester.getRect(findMenuPanels().at(1)).size, equals(const Size(100.0, 100.0))); + if (!kIsWeb || isCanvasKit) { // https://github.com/flutter/flutter/issues/99933 + expect(tester.getRect(findMenuPanels().at(1)), equals(const Rect.fromLTRB(104.0, 54.0, 204.0, 154.0))); + expect(tester.getRect(findMenuPanels().at(1)).size, equals(const Size(100.0, 100.0))); + } }); testWidgets('maximumSize affects geometry', (WidgetTester tester) async {