| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/gestures.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| PopupMenuThemeData _popupMenuThemeM2() { |
| return PopupMenuThemeData( |
| color: Colors.orange, |
| shape: const BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))), |
| elevation: 12.0, |
| textStyle: const TextStyle(color: Color(0xffffffff), textBaseline: TextBaseline.alphabetic), |
| mouseCursor: MaterialStateProperty.resolveWith<MouseCursor?>((Set<MaterialState> states) { |
| if (states.contains(MaterialState.disabled)) { |
| return SystemMouseCursors.contextMenu; |
| } |
| return SystemMouseCursors.alias; |
| }), |
| ); |
| } |
| |
| PopupMenuThemeData _popupMenuThemeM3() { |
| return PopupMenuThemeData( |
| color: Colors.orange, |
| shape: const BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))), |
| elevation: 12.0, |
| shadowColor: const Color(0xff00ff00), |
| surfaceTintColor: const Color(0xff00ff00), |
| labelTextStyle: MaterialStateProperty.resolveWith((Set<MaterialState> states) { |
| if (states.contains(MaterialState.disabled)) { |
| return const TextStyle(color: Color(0xfff99ff0), fontSize: 12.0); |
| } |
| return const TextStyle(color: Color(0xfff12099), fontSize: 17.0); |
| }), |
| mouseCursor: MaterialStateProperty.resolveWith<MouseCursor?>((Set<MaterialState> states) { |
| if (states.contains(MaterialState.disabled)) { |
| return SystemMouseCursors.contextMenu; |
| } |
| return SystemMouseCursors.alias; |
| }), |
| ); |
| } |
| |
| void main() { |
| test('PopupMenuThemeData copyWith, ==, hashCode basics', () { |
| expect(const PopupMenuThemeData(), const PopupMenuThemeData().copyWith()); |
| expect(const PopupMenuThemeData().hashCode, const PopupMenuThemeData().copyWith().hashCode); |
| }); |
| |
| test('PopupMenuThemeData null fields by default', () { |
| const PopupMenuThemeData popupMenuTheme = PopupMenuThemeData(); |
| expect(popupMenuTheme.color, null); |
| expect(popupMenuTheme.shape, null); |
| expect(popupMenuTheme.elevation, null); |
| expect(popupMenuTheme.shadowColor, null); |
| expect(popupMenuTheme.surfaceTintColor, null); |
| expect(popupMenuTheme.textStyle, null); |
| expect(popupMenuTheme.labelTextStyle, null); |
| expect(popupMenuTheme.enableFeedback, null); |
| expect(popupMenuTheme.mouseCursor, null); |
| }); |
| |
| testWidgets('Default PopupMenuThemeData debugFillProperties', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| const PopupMenuThemeData().debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, <String>[]); |
| }); |
| |
| testWidgets('PopupMenuThemeData implements debugFillProperties', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| PopupMenuThemeData( |
| color: const Color(0xFFFFFFFF), |
| shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))), |
| elevation: 2.0, |
| shadowColor: const Color(0xff00ff00), |
| surfaceTintColor: const Color(0xff00ff00), |
| textStyle: const TextStyle(color: Color(0xffffffff)), |
| labelTextStyle: MaterialStateProperty.resolveWith((Set<MaterialState> states) { |
| if (states.contains(MaterialState.disabled)) { |
| return const TextStyle(color: Color(0xfff99ff0), fontSize: 12.0); |
| } |
| return const TextStyle(color: Color(0xfff12099), fontSize: 17.0); |
| }), |
| mouseCursor: MaterialStateMouseCursor.clickable, |
| ).debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, <String>[ |
| 'color: Color(0xffffffff)', |
| 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))', |
| 'elevation: 2.0', |
| 'shadowColor: Color(0xff00ff00)', |
| 'surfaceTintColor: Color(0xff00ff00)', |
| 'text style: TextStyle(inherit: true, color: Color(0xffffffff))', |
| "labelTextStyle: Instance of '_MaterialStatePropertyWith<TextStyle?>'", |
| 'mouseCursor: MaterialStateMouseCursor(clickable)', |
| ]); |
| }); |
| |
| testWidgets('Passing no PopupMenuThemeData returns defaults', (WidgetTester tester) async { |
| final Key popupButtonKey = UniqueKey(); |
| final Key popupButtonApp = UniqueKey(); |
| final Key enabledPopupItemKey = UniqueKey(); |
| final Key disabledPopupItemKey = UniqueKey(); |
| final ThemeData theme = ThemeData(useMaterial3: true); |
| |
| await tester.pumpWidget(MaterialApp( |
| theme: theme, |
| key: popupButtonApp, |
| home: Material( |
| child: Column( |
| children: <Widget>[ |
| Padding( |
| // The padding makes sure the menu has enough space around it to |
| // get properly aligned when displayed (`_kMenuScreenPadding`). |
| padding: const EdgeInsets.all(8.0), |
| child: PopupMenuButton<void>( |
| key: popupButtonKey, |
| itemBuilder: (BuildContext context) { |
| return <PopupMenuEntry<void>>[ |
| PopupMenuItem<void>( |
| key: enabledPopupItemKey, |
| child: const Text('Enabled PopupMenuItem'), |
| ), |
| const PopupMenuDivider(), |
| PopupMenuItem<void>( |
| key: disabledPopupItemKey, |
| enabled: false, |
| child: const Text('Disabled PopupMenuItem'), |
| ), |
| ]; |
| }, |
| ), |
| ), |
| ], |
| ), |
| ), |
| )); |
| |
| await tester.tap(find.byKey(popupButtonKey)); |
| await tester.pumpAndSettle(); |
| |
| /// The last Material widget under popupButtonApp is the [PopupMenuButton] |
| /// specified above, so by finding the last descendent of popupButtonApp |
| /// that is of type Material, this code retrieves the built |
| /// [PopupMenuButton]. |
| final Material button = tester.widget<Material>( |
| find.descendant( |
| of: find.byKey(popupButtonApp), |
| matching: find.byType(Material), |
| ).last, |
| ); |
| expect(button.color, theme.colorScheme.surface); |
| expect(button.shadowColor, theme.colorScheme.shadow); |
| expect(button.surfaceTintColor, theme.colorScheme.surfaceTint); |
| expect(button.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(4.0))); |
| expect(button.elevation, 3.0); |
| |
| /// The last DefaultTextStyle widget under popupItemKey is the |
| /// [PopupMenuItem] specified above, so by finding the last descendent of |
| /// popupItemKey that is of type DefaultTextStyle, this code retrieves the |
| /// built [PopupMenuItem]. |
| final DefaultTextStyle enabledText = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(enabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(enabledText.style.fontFamily, 'Roboto'); |
| expect(enabledText.style.color, theme.colorScheme.onSurface); |
| /// Test disabled text color |
| final DefaultTextStyle disabledText = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(disabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(disabledText.style.color, theme.colorScheme.onSurface.withOpacity(0.38)); |
| |
| final Offset topLeftButton = tester.getTopLeft(find.byType(PopupMenuButton<void>)); |
| final Offset topLeftMenu = tester.getTopLeft(find.byWidget(button)); |
| expect(topLeftMenu, topLeftButton); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(gesture.removePointer); |
| await gesture.moveTo(tester.getCenter(find.byKey(disabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| SystemMouseCursors.basic, |
| ); |
| await gesture.down(tester.getCenter(find.byKey(enabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| SystemMouseCursors.click, |
| ); |
| }); |
| |
| testWidgets('Popup menu uses values from PopupMenuThemeData', (WidgetTester tester) async { |
| final PopupMenuThemeData popupMenuTheme = _popupMenuThemeM3(); |
| final Key popupButtonKey = UniqueKey(); |
| final Key popupButtonApp = UniqueKey(); |
| final Key enabledPopupItemKey = UniqueKey(); |
| final Key disabledPopupItemKey = UniqueKey(); |
| |
| await tester.pumpWidget(MaterialApp( |
| theme: ThemeData(useMaterial3: true, popupMenuTheme: popupMenuTheme), |
| key: popupButtonApp, |
| home: Material( |
| child: Column( |
| children: <Widget>[ |
| PopupMenuButton<void>( |
| // The padding is used in the positioning of the menu when the |
| // position is `PopupMenuPosition.under`. Setting it to zero makes |
| // it easier to test. |
| padding: EdgeInsets.zero, |
| key: popupButtonKey, |
| itemBuilder: (BuildContext context) { |
| return <PopupMenuEntry<Object>>[ |
| PopupMenuItem<Object>( |
| key: disabledPopupItemKey, |
| enabled: false, |
| child: const Text('disabled'), |
| ), |
| const PopupMenuDivider(), |
| PopupMenuItem<Object>( |
| key: enabledPopupItemKey, |
| onTap: () { }, |
| child: const Text('enabled'), |
| ), |
| ]; |
| }, |
| ), |
| ], |
| ), |
| ), |
| )); |
| |
| await tester.tap(find.byKey(popupButtonKey)); |
| await tester.pumpAndSettle(); |
| |
| /// The last Material widget under popupButtonApp is the [PopupMenuButton] |
| /// specified above, so by finding the last descendent of popupButtonApp |
| /// that is of type Material, this code retrieves the built |
| /// [PopupMenuButton]. |
| final Material button = tester.widget<Material>( |
| find.descendant( |
| of: find.byKey(popupButtonApp), |
| matching: find.byType(Material), |
| ).last, |
| ); |
| expect(button.color, Colors.orange); |
| expect(button.surfaceTintColor, const Color(0xff00ff00)); |
| expect(button.shadowColor, const Color(0xff00ff00)); |
| expect(button.shape, const BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12)))); |
| expect(button.elevation, 12.0); |
| |
| final DefaultTextStyle enabledText = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(enabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect( |
| enabledText.style, |
| popupMenuTheme.labelTextStyle?.resolve(enabled), |
| ); |
| /// Test disabled text color |
| final DefaultTextStyle disabledText = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(disabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect( |
| disabledText.style, |
| popupMenuTheme.labelTextStyle?.resolve(disabled), |
| ); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(gesture.removePointer); |
| await gesture.moveTo(tester.getCenter(find.byKey(disabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| popupMenuTheme.mouseCursor?.resolve(disabled), |
| ); |
| await gesture.down(tester.getCenter(find.byKey(enabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| popupMenuTheme.mouseCursor?.resolve(enabled), |
| ); |
| }); |
| |
| testWidgets('Popup menu widget properties take priority over theme', (WidgetTester tester) async { |
| final PopupMenuThemeData popupMenuTheme = _popupMenuThemeM3(); |
| final Key popupButtonKey = UniqueKey(); |
| final Key popupButtonApp = UniqueKey(); |
| final Key popupItemKey = UniqueKey(); |
| |
| const Color color = Colors.purple; |
| const Color surfaceTintColor = Colors.amber; |
| const Color shadowColor = Colors.green; |
| const ShapeBorder shape = RoundedRectangleBorder( |
| borderRadius: BorderRadius.all(Radius.circular(9.0)), |
| ); |
| const double elevation = 7.0; |
| const TextStyle textStyle = TextStyle(color: Color(0xffffffef), fontSize: 19.0); |
| const MouseCursor cursor = SystemMouseCursors.forbidden; |
| |
| await tester.pumpWidget(MaterialApp( |
| theme: ThemeData(useMaterial3: true, popupMenuTheme: popupMenuTheme), |
| key: popupButtonApp, |
| home: Material( |
| child: Column( |
| children: <Widget>[ |
| PopupMenuButton<void>( |
| key: popupButtonKey, |
| elevation: elevation, |
| shadowColor: shadowColor, |
| surfaceTintColor: surfaceTintColor, |
| color: color, |
| shape: shape, |
| itemBuilder: (BuildContext context) { |
| return <PopupMenuEntry<void>>[ |
| PopupMenuItem<void>( |
| key: popupItemKey, |
| labelTextStyle: MaterialStateProperty.all<TextStyle>(textStyle), |
| mouseCursor: cursor, |
| child: const Text('Example'), |
| ), |
| ]; |
| }, |
| ), |
| ], |
| ), |
| ), |
| )); |
| |
| await tester.tap(find.byKey(popupButtonKey)); |
| await tester.pumpAndSettle(); |
| |
| /// The last Material widget under popupButtonApp is the [PopupMenuButton] |
| /// specified above, so by finding the last descendent of popupButtonApp |
| /// that is of type Material, this code retrieves the built |
| /// [PopupMenuButton]. |
| final Material button = tester.widget<Material>( |
| find.descendant( |
| of: find.byKey(popupButtonApp), |
| matching: find.byType(Material), |
| ).last, |
| ); |
| expect(button.color, color); |
| expect(button.shape, shape); |
| expect(button.elevation, elevation); |
| expect(button.shadowColor, shadowColor); |
| expect(button.surfaceTintColor, surfaceTintColor); |
| |
| /// The last DefaultTextStyle widget under popupItemKey is the |
| /// [PopupMenuItem] specified above, so by finding the last descendent of |
| /// popupItemKey that is of type DefaultTextStyle, this code retrieves the |
| /// built [PopupMenuItem]. |
| final DefaultTextStyle text = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(popupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(text.style, textStyle); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(gesture.removePointer); |
| await gesture.moveTo(tester.getCenter(find.byKey(popupItemKey))); |
| await tester.pumpAndSettle(); |
| expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), cursor); |
| }); |
| |
| group('Material 2', () { |
| // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3 |
| // is turned on by default, these tests can be removed. |
| |
| testWidgets('Passing no PopupMenuThemeData returns defaults', (WidgetTester tester) async { |
| final Key popupButtonKey = UniqueKey(); |
| final Key popupButtonApp = UniqueKey(); |
| final Key enabledPopupItemKey = UniqueKey(); |
| final Key disabledPopupItemKey = UniqueKey(); |
| final ThemeData theme = ThemeData(); |
| |
| await tester.pumpWidget(MaterialApp( |
| theme: theme, |
| key: popupButtonApp, |
| home: Material( |
| child: Column( |
| children: <Widget>[ |
| Padding( |
| // The padding makes sure the menu has enough space around it to |
| // get properly aligned when displayed (`_kMenuScreenPadding`). |
| padding: const EdgeInsets.all(8.0), |
| child: PopupMenuButton<void>( |
| key: popupButtonKey, |
| itemBuilder: (BuildContext context) { |
| return <PopupMenuEntry<void>>[ |
| PopupMenuItem<void>( |
| key: enabledPopupItemKey, |
| child: const Text('Enabled PopupMenuItem'), |
| ), |
| const PopupMenuDivider(), |
| PopupMenuItem<void>( |
| key: disabledPopupItemKey, |
| enabled: false, |
| child: const Text('Disabled PopupMenuItem'), |
| ), |
| ]; |
| }, |
| ), |
| ), |
| ], |
| ), |
| ), |
| )); |
| |
| await tester.tap(find.byKey(popupButtonKey)); |
| await tester.pumpAndSettle(); |
| |
| /// The last Material widget under popupButtonApp is the [PopupMenuButton] |
| /// specified above, so by finding the last descendent of popupButtonApp |
| /// that is of type Material, this code retrieves the built |
| /// [PopupMenuButton]. |
| final Material button = tester.widget<Material>( |
| find.descendant( |
| of: find.byKey(popupButtonApp), |
| matching: find.byType(Material), |
| ).last, |
| ); |
| expect(button.color, null); |
| expect(button.shape, null); |
| expect(button.elevation, 8.0); |
| |
| /// The last DefaultTextStyle widget under popupItemKey is the |
| /// [PopupMenuItem] specified above, so by finding the last descendent of |
| /// popupItemKey that is of type DefaultTextStyle, this code retrieves the |
| /// built [PopupMenuItem]. |
| final DefaultTextStyle enabledText = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(enabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(enabledText.style.fontFamily, 'Roboto'); |
| expect(enabledText.style.color, const Color(0xdd000000)); |
| /// Test disabled text color |
| final DefaultTextStyle disabledText = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(disabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(disabledText.style.color, theme.disabledColor); |
| |
| final Offset topLeftButton = tester.getTopLeft(find.byType(PopupMenuButton<void>)); |
| final Offset topLeftMenu = tester.getTopLeft(find.byWidget(button)); |
| expect(topLeftMenu, topLeftButton); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(gesture.removePointer); |
| await gesture.moveTo(tester.getCenter(find.byKey(disabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| SystemMouseCursors.basic, |
| ); |
| await gesture.down(tester.getCenter(find.byKey(enabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| SystemMouseCursors.click, |
| ); |
| }); |
| |
| testWidgets('Popup menu uses values from PopupMenuThemeData', (WidgetTester tester) async { |
| final PopupMenuThemeData popupMenuTheme = _popupMenuThemeM2(); |
| final Key popupButtonKey = UniqueKey(); |
| final Key popupButtonApp = UniqueKey(); |
| final Key enabledPopupItemKey = UniqueKey(); |
| final Key disabledPopupItemKey = UniqueKey(); |
| |
| await tester.pumpWidget(MaterialApp( |
| theme: ThemeData(popupMenuTheme: popupMenuTheme), |
| key: popupButtonApp, |
| home: Material( |
| child: Column( |
| children: <Widget>[ |
| PopupMenuButton<void>( |
| // The padding is used in the positioning of the menu when the |
| // position is `PopupMenuPosition.under`. Setting it to zero makes |
| // it easier to test. |
| padding: EdgeInsets.zero, |
| key: popupButtonKey, |
| itemBuilder: (BuildContext context) { |
| return <PopupMenuEntry<Object>>[ |
| PopupMenuItem<Object>( |
| key: disabledPopupItemKey, |
| enabled: false, |
| child: const Text('disabled'), |
| ), |
| const PopupMenuDivider(), |
| PopupMenuItem<Object>( |
| key: enabledPopupItemKey, |
| onTap: () { }, |
| child: const Text('enabled'), |
| ), |
| ]; |
| }, |
| ), |
| ], |
| ), |
| ), |
| )); |
| |
| await tester.tap(find.byKey(popupButtonKey)); |
| await tester.pumpAndSettle(); |
| |
| /// The last Material widget under popupButtonApp is the [PopupMenuButton] |
| /// specified above, so by finding the last descendent of popupButtonApp |
| /// that is of type Material, this code retrieves the built |
| /// [PopupMenuButton]. |
| final Material button = tester.widget<Material>( |
| find.descendant( |
| of: find.byKey(popupButtonApp), |
| matching: find.byType(Material), |
| ).last, |
| ); |
| expect(button.color, popupMenuTheme.color); |
| expect(button.shape, popupMenuTheme.shape); |
| expect(button.elevation, popupMenuTheme.elevation); |
| |
| /// The last DefaultTextStyle widget under popupItemKey is the |
| /// [PopupMenuItem] specified above, so by finding the last descendent of |
| /// popupItemKey that is of type DefaultTextStyle, this code retrieves the |
| /// built [PopupMenuItem]. |
| final DefaultTextStyle text = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(enabledPopupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(text.style, popupMenuTheme.textStyle); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(gesture.removePointer); |
| await gesture.moveTo(tester.getCenter(find.byKey(disabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| popupMenuTheme.mouseCursor?.resolve(disabled), |
| ); |
| await gesture.down(tester.getCenter(find.byKey(enabledPopupItemKey))); |
| await tester.pumpAndSettle(); |
| expect( |
| RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), |
| popupMenuTheme.mouseCursor?.resolve(enabled), |
| ); |
| }); |
| |
| testWidgets('Popup menu widget properties take priority over theme', (WidgetTester tester) async { |
| final PopupMenuThemeData popupMenuTheme = _popupMenuThemeM2(); |
| final Key popupButtonKey = UniqueKey(); |
| final Key popupButtonApp = UniqueKey(); |
| final Key popupItemKey = UniqueKey(); |
| |
| const Color color = Colors.purple; |
| const Color surfaceTintColor = Colors.amber; |
| const Color shadowColor = Colors.green; |
| const ShapeBorder shape = RoundedRectangleBorder( |
| borderRadius: BorderRadius.all(Radius.circular(9.0)), |
| ); |
| const double elevation = 7.0; |
| const TextStyle textStyle = TextStyle(color: Color(0xffffffef), fontSize: 19.0); |
| const MouseCursor cursor = SystemMouseCursors.forbidden; |
| |
| await tester.pumpWidget(MaterialApp( |
| theme: ThemeData(useMaterial3: true, popupMenuTheme: popupMenuTheme), |
| key: popupButtonApp, |
| home: Material( |
| child: Column( |
| children: <Widget>[ |
| PopupMenuButton<void>( |
| key: popupButtonKey, |
| elevation: elevation, |
| shadowColor: shadowColor, |
| surfaceTintColor: surfaceTintColor, |
| color: color, |
| shape: shape, |
| itemBuilder: (BuildContext context) { |
| return <PopupMenuEntry<void>>[ |
| PopupMenuItem<void>( |
| key: popupItemKey, |
| labelTextStyle: MaterialStateProperty.all<TextStyle>(textStyle), |
| mouseCursor: cursor, |
| child: const Text('Example'), |
| ), |
| ]; |
| }, |
| ), |
| ], |
| ), |
| ), |
| )); |
| |
| await tester.tap(find.byKey(popupButtonKey)); |
| await tester.pumpAndSettle(); |
| |
| /// The last Material widget under popupButtonApp is the [PopupMenuButton] |
| /// specified above, so by finding the last descendent of popupButtonApp |
| /// that is of type Material, this code retrieves the built |
| /// [PopupMenuButton]. |
| final Material button = tester.widget<Material>( |
| find.descendant( |
| of: find.byKey(popupButtonApp), |
| matching: find.byType(Material), |
| ).last, |
| ); |
| expect(button.color, color); |
| expect(button.shape, shape); |
| expect(button.elevation, elevation); |
| expect(button.shadowColor, shadowColor); |
| expect(button.surfaceTintColor, surfaceTintColor); |
| |
| /// The last DefaultTextStyle widget under popupItemKey is the |
| /// [PopupMenuItem] specified above, so by finding the last descendent of |
| /// popupItemKey that is of type DefaultTextStyle, this code retrieves the |
| /// built [PopupMenuItem]. |
| final DefaultTextStyle text = tester.widget<DefaultTextStyle>( |
| find.descendant( |
| of: find.byKey(popupItemKey), |
| matching: find.byType(DefaultTextStyle), |
| ).last, |
| ); |
| expect(text.style, textStyle); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(gesture.removePointer); |
| await gesture.moveTo(tester.getCenter(find.byKey(popupItemKey))); |
| await tester.pumpAndSettle(); |
| expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), cursor); |
| }); |
| }); |
| } |
| |
| Set<MaterialState> enabled = <MaterialState>{}; |
| Set<MaterialState> disabled = <MaterialState>{MaterialState.disabled}; |