Add missing overlayColor property in styleFrom methods (#146685)

fixes [Add missing `overlayColor` property  in `styleFrom` methods](https://github.com/flutter/flutter/issues/146636)

### Code sample

<details>
<summary>expand to view the code sample</summary> 

```dart
import 'package:flutter/material.dart';

enum Sizes { extraSmall, small, medium, large, extraLarge }

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              Text('styleFrom(overlayColor: Colors.red)', style: Theme.of(context).textTheme.titleLarge),
              TextButton(
                style: TextButton.styleFrom(overlayColor: Colors.red),
                onPressed: () {},
                child: const Text('TextButton'),
              ),
              IconButton(
                style: IconButton.styleFrom(
                  overlayColor: Colors.red,
                ),
                onPressed: () {},
                icon: const Icon(Icons.add),
              ),
              MenuBar(
                children: [
                  MenuItemButton(
                    style: MenuItemButton.styleFrom(overlayColor: Colors.red),
                    child: const Text('MenuItemButton'),
                    onPressed: () {},
                  ),
                  SubmenuButton(
                    style: SubmenuButton.styleFrom(overlayColor: Colors.red),
                    menuChildren: [
                      MenuItemButton(
                        child: const Text('MenuItemButton'),
                        onPressed: () {},
                      ),
                    ],
                    child: const Text('SubmenuButton'),
                  ),
                ],
              ),
              SegmentedButton<Sizes>(
                style:
                    SegmentedButton.styleFrom(overlayColor: Colors.red),
                segments: const <ButtonSegment<Sizes>>[
                  ButtonSegment<Sizes>(
                      value: Sizes.extraSmall, label: Text('XS')),
                  ButtonSegment<Sizes>(value: Sizes.small, label: Text('S')),
                  ButtonSegment<Sizes>(value: Sizes.medium, label: Text('M')),
                  ButtonSegment<Sizes>(
                    value: Sizes.large,
                    label: Text('L'),
                  ),
                  ButtonSegment<Sizes>(
                      value: Sizes.extraLarge, label: Text('XL')),
                ],
                selected: const {Sizes.medium},
                onSelectionChanged: (Set<Sizes> newSelection) {},
                multiSelectionEnabled: true,
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```

</details>

### Preview

![ScreenRecording2024-04-12at15 25 58-ezgif com-video-to-gif-converter](https://github.com/flutter/flutter/assets/48603081/89b9638d-f369-4ef1-b501-17c9c74b2541)
This commit is contained in:
Taha Tesser 2024-04-24 14:56:32 +03:00 committed by GitHub
parent ffea970ce8
commit fa85f69e47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 611 additions and 74 deletions

View File

@ -120,27 +120,27 @@ class _${blockName}DefaultsM3 extends SegmentedButtonThemeData {
@override
Widget? get selectedIcon => const Icon(Icons.check);
static MaterialStateProperty<Color?> resolveStateColor(Color? unselectedColor, Color? selectedColor){
static MaterialStateProperty<Color?> resolveStateColor(Color? unselectedColor, Color? selectedColor, Color? overlayColor){
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return selectedColor?.withOpacity(0.1);
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
}
if (states.contains(MaterialState.hovered)) {
return selectedColor?.withOpacity(0.08);
return (overlayColor ?? selectedColor)?.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return selectedColor?.withOpacity(0.1);
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
}
} else {
if (states.contains(MaterialState.pressed)) {
return unselectedColor?.withOpacity(0.1);
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
}
if (states.contains(MaterialState.hovered)) {
return unselectedColor?.withOpacity(0.08);
return (overlayColor ?? unselectedColor)?.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return unselectedColor?.withOpacity(0.1);
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
}
}
return Colors.transparent;

View File

@ -573,9 +573,15 @@ class IconButton extends StatelessWidget {
/// [ButtonStyle.foregroundColor] value. Specify a value for [foregroundColor]
/// to specify the color of the button's icons. The [hoverColor], [focusColor]
/// and [highlightColor] colors are used to indicate the hover, focus,
/// and pressed states. Use [backgroundColor] for the button's background
/// fill color. Use [disabledForegroundColor] and [disabledBackgroundColor]
/// to specify the button's disabled icon and fill color.
/// and pressed states if [overlayColor] isn't specified.
///
/// If [overlayColor] is specified and its value is [Colors.transparent]
/// then the pressed/focused/hovered highlights are effectively defeated.
/// Otherwise a [MaterialStateProperty] with the same opacities as the
/// default is created.
///
/// Use [backgroundColor] for the button's background fill color. Use [disabledForegroundColor]
/// and [disabledBackgroundColor] to specify the button's disabled icon and fill color.
///
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
/// parameters are used to construct [ButtonStyle].mouseCursor.
@ -611,6 +617,7 @@ class IconButton extends StatelessWidget {
Color? highlightColor,
Color? shadowColor,
Color? surfaceTintColor,
Color? overlayColor,
double? elevation,
Size? minimumSize,
Size? fixedSize,
@ -634,15 +641,19 @@ class IconButton extends StatelessWidget {
final MaterialStateProperty<Color?>? buttonForegroundColor = (foregroundColor == null && disabledForegroundColor == null)
? null
: _IconButtonDefaultForeground(foregroundColor, disabledForegroundColor);
final MaterialStateProperty<Color?>? overlayColor = (foregroundColor == null && hoverColor == null && focusColor == null && highlightColor == null)
final MaterialStateProperty<Color?>? overlayColorProp = (foregroundColor == null &&
hoverColor == null && focusColor == null && highlightColor == null && overlayColor == null)
? null
: _IconButtonDefaultOverlay(foregroundColor, focusColor, hoverColor, highlightColor);
: switch (overlayColor) {
(final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
_ => _IconButtonDefaultOverlay(foregroundColor, focusColor, hoverColor, highlightColor, overlayColor),
};
final MaterialStateProperty<MouseCursor?> mouseCursor = _IconButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
return ButtonStyle(
backgroundColor: buttonBackgroundColor,
foregroundColor: buttonForegroundColor,
overlayColor: overlayColor,
overlayColor: overlayColorProp,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
elevation: ButtonStyleButton.allOrNull<double>(elevation),
@ -1023,34 +1034,41 @@ class _IconButtonDefaultForeground extends MaterialStateProperty<Color?> {
@immutable
class _IconButtonDefaultOverlay extends MaterialStateProperty<Color?> {
_IconButtonDefaultOverlay(this.foregroundColor, this.focusColor, this.hoverColor, this.highlightColor);
_IconButtonDefaultOverlay(
this.foregroundColor,
this.focusColor,
this.hoverColor,
this.highlightColor,
this.overlayColor,
);
final Color? foregroundColor;
final Color? focusColor;
final Color? hoverColor;
final Color? highlightColor;
final Color? overlayColor;
@override
Color? resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return highlightColor ?? foregroundColor?.withOpacity(0.1);
return highlightColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
}
if (states.contains(MaterialState.hovered)) {
return hoverColor ?? foregroundColor?.withOpacity(0.08);
return hoverColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return focusColor ?? foregroundColor?.withOpacity(0.1);
return focusColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
}
}
if (states.contains(MaterialState.pressed)) {
return highlightColor ?? foregroundColor?.withOpacity(0.1);
return highlightColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
}
if (states.contains(MaterialState.hovered)) {
return hoverColor ?? foregroundColor?.withOpacity(0.08);
return hoverColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return focusColor ?? foregroundColor?.withOpacity(0.1);
return focusColor ?? (overlayColor ?? foregroundColor)?.withOpacity(0.1);
}
return null;
}

View File

@ -1022,6 +1022,7 @@ class MenuItemButton extends StatefulWidget {
Color? surfaceTintColor,
Color? iconColor,
TextStyle? textStyle,
Color? overlayColor,
double? elevation,
EdgeInsetsGeometry? padding,
Size? minimumSize,
@ -1047,6 +1048,7 @@ class MenuItemButton extends StatefulWidget {
surfaceTintColor: surfaceTintColor,
iconColor: iconColor,
textStyle: textStyle,
overlayColor: overlayColor,
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
@ -1775,6 +1777,7 @@ class SubmenuButton extends StatefulWidget {
Color? surfaceTintColor,
Color? iconColor,
TextStyle? textStyle,
Color? overlayColor,
double? elevation,
EdgeInsetsGeometry? padding,
Size? minimumSize,
@ -1800,6 +1803,7 @@ class SubmenuButton extends StatefulWidget {
surfaceTintColor: surfaceTintColor,
iconColor: iconColor,
textStyle: textStyle,
overlayColor: overlayColor,
elevation: elevation,
padding: padding,
minimumSize: minimumSize,

View File

@ -203,7 +203,12 @@ class SegmentedButton<T> extends StatefulWidget {
///
/// The [foregroundColor], [selectedForegroundColor], and [disabledForegroundColor]
/// colors are used to create a [MaterialStateProperty] [ButtonStyle.foregroundColor],
/// and a derived [ButtonStyle.overlayColor].
/// and a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified.
///
/// If [overlayColor] is specified and its value is [Colors.transparent]
/// then the pressed/focused/hovered highlights are effectively defeated.
/// Otherwise a [MaterialStateProperty] with the same opacities as the
/// default is created.
///
/// The [backgroundColor], [selectedBackgroundColor] and [disabledBackgroundColor]
/// colors are used to create a [MaterialStateProperty] [ButtonStyle.backgroundColor].
@ -261,6 +266,7 @@ class SegmentedButton<T> extends StatefulWidget {
Color? disabledBackgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
Color? overlayColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
@ -286,9 +292,13 @@ class SegmentedButton<T> extends StatefulWidget {
(backgroundColor == null && disabledBackgroundColor == null && selectedBackgroundColor == null)
? null
: _SegmentButtonDefaultColor(backgroundColor, disabledBackgroundColor, selectedBackgroundColor);
final MaterialStateProperty<Color?>? overlayColor = (foregroundColor == null && selectedForegroundColor == null)
final MaterialStateProperty<Color?>? overlayColorProp = (foregroundColor == null &&
selectedForegroundColor == null && overlayColor == null)
? null
: _SegmentedButtonDefaultsM3.resolveStateColor(foregroundColor, selectedForegroundColor);
: switch (overlayColor) {
(final Color overlayColor) when overlayColor.value == 0 => const MaterialStatePropertyAll<Color?>(Colors.transparent),
_ => _SegmentedButtonDefaultsM3.resolveStateColor(foregroundColor, selectedForegroundColor, overlayColor),
};
return TextButton.styleFrom(
textStyle: textStyle,
shadowColor: shadowColor,
@ -311,7 +321,7 @@ class SegmentedButton<T> extends StatefulWidget {
).copyWith(
foregroundColor: foregroundColorProp,
backgroundColor: backgroundColorProp,
overlayColor: overlayColor,
overlayColor: overlayColorProp,
);
}
@ -1066,27 +1076,27 @@ class _SegmentedButtonDefaultsM3 extends SegmentedButtonThemeData {
@override
Widget? get selectedIcon => const Icon(Icons.check);
static MaterialStateProperty<Color?> resolveStateColor(Color? unselectedColor, Color? selectedColor){
static MaterialStateProperty<Color?> resolveStateColor(Color? unselectedColor, Color? selectedColor, Color? overlayColor){
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
if (states.contains(MaterialState.pressed)) {
return selectedColor?.withOpacity(0.1);
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
}
if (states.contains(MaterialState.hovered)) {
return selectedColor?.withOpacity(0.08);
return (overlayColor ?? selectedColor)?.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return selectedColor?.withOpacity(0.1);
return (overlayColor ?? selectedColor)?.withOpacity(0.1);
}
} else {
if (states.contains(MaterialState.pressed)) {
return unselectedColor?.withOpacity(0.1);
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
}
if (states.contains(MaterialState.hovered)) {
return unselectedColor?.withOpacity(0.08);
return (overlayColor ?? unselectedColor)?.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return unselectedColor?.withOpacity(0.1);
return (overlayColor ?? unselectedColor)?.withOpacity(0.1);
}
}
return Colors.transparent;

View File

@ -7,6 +7,7 @@ import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart';
@ -27,6 +28,10 @@ void main() {
mockOnPressedFunction = MockOnPressedFunction();
});
RenderObject getOverlayColor(WidgetTester tester) {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
testWidgets('test icon is findable by key', (WidgetTester tester) async {
const ValueKey<String> key = ValueKey<String>('icon-button');
await tester.pumpWidget(
@ -1222,10 +1227,6 @@ void main() {
),
);
RenderObject overlayColor() {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
@ -1234,12 +1235,12 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect()..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect()..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.1)));
// Remove pressed and hovered states
await gesture.up();
await tester.pumpAndSettle();
@ -1249,7 +1250,7 @@ void main() {
// Focused.
focusNode.requestFocus();
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.1)));
focusNode.dispose();
});
@ -1363,10 +1364,6 @@ void main() {
),
);
RenderObject overlayColor() {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
@ -1375,12 +1372,12 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onPrimary.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onPrimary.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect()..rect(color: theme.colorScheme.onPrimary.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect()..rect(color: theme.colorScheme.onPrimary.withOpacity(0.1)));
// Remove pressed and hovered states
await gesture.up();
await tester.pumpAndSettle();
@ -1390,7 +1387,7 @@ void main() {
// Focused.
focusNode.requestFocus();
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onPrimary.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onPrimary.withOpacity(0.1)));
focusNode.dispose();
});
@ -1619,10 +1616,6 @@ void main() {
),
);
RenderObject overlayColor() {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
@ -1631,12 +1624,12 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSecondaryContainer.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSecondaryContainer.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect()..rect(color: theme.colorScheme.onSecondaryContainer.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect()..rect(color: theme.colorScheme.onSecondaryContainer.withOpacity(0.1)));
// Remove pressed and hovered states
await gesture.up();
await tester.pumpAndSettle();
@ -1646,7 +1639,7 @@ void main() {
// Focused.
focusNode.requestFocus();
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSecondaryContainer.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSecondaryContainer.withOpacity(0.1)));
focusNode.dispose();
});
@ -1875,10 +1868,6 @@ void main() {
),
);
RenderObject overlayColor() {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
@ -1887,12 +1876,12 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect()..rect(color: theme.colorScheme.onSurface.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect()..rect(color: theme.colorScheme.onSurface.withOpacity(0.1)));
// Remove pressed and hovered states
await gesture.up();
await tester.pumpAndSettle();
@ -1902,7 +1891,7 @@ void main() {
// Focused.
focusNode.requestFocus();
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSurfaceVariant.withOpacity(0.08)));
focusNode.dispose();
});
@ -2586,6 +2575,156 @@ void main() {
expect(box.size, equals(const Size(48, 48)));
});
testWidgets('IconButton.styleFrom overlayColor overrides default overlay color', (WidgetTester tester) async {
const Color overlayColor = Color(0xffff0000);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: IconButton(
style: IconButton.styleFrom(overlayColor: overlayColor),
onPressed: () { },
icon: const Icon(Icons.add),
),
),
),
),
);
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1)));
});
testWidgets('IconButton.styleFrom highlight, hover, focus colors overrides overlayColor', (WidgetTester tester) async {
const Color hoverColor = Color(0xff0000f2);
const Color highlightColor = Color(0xff0000f1);
const Color focusColor = Color(0xff0000f3);
const Color overlayColor = Color(0xffff0000);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: IconButton(
style: IconButton.styleFrom(
hoverColor: hoverColor,
highlightColor: highlightColor,
focusColor: focusColor,
overlayColor: overlayColor,
),
onPressed: () { },
icon: const Icon(Icons.add),
),
),
),
),
);
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: hoverColor));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: hoverColor)
..rect(color: highlightColor),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: focusColor));
});
testWidgets('IconButton.styleFrom with transparent overlayColor', (WidgetTester tester) async {
const Color overlayColor = Colors.transparent;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: IconButton(
style: IconButton.styleFrom(overlayColor: overlayColor),
onPressed: () { },
icon: const Icon(Icons.add),
),
),
),
),
);
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor)
..rect(color: overlayColor),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor));
});
group('IconTheme tests in Material 3', () {
testWidgets('IconTheme overrides default values in M3', (WidgetTester tester) async {
// Theme's IconTheme

View File

@ -2,10 +2,16 @@
// 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/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
RenderObject getOverlayColor(WidgetTester tester) {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
test('IconButtonThemeData lerp special cases', () {
expect(IconButtonThemeData.lerp(null, null, 0), null);
const IconButtonThemeData data = IconButtonThemeData();
@ -254,4 +260,57 @@ void main() {
material = tester.widget<Material>(buttonMaterialFinder);
expect(material.shadowColor, shadowColor);
});
testWidgets('IconButtonTheme IconButton.styleFrom overlayColor overrides default overlay color', (WidgetTester tester) async {
const Color overlayColor = Color(0xffff0000);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: IconButtonTheme(
data: IconButtonThemeData(
style: IconButton.styleFrom(
overlayColor: overlayColor,
),
),
child: IconButton(
onPressed: () { },
icon: const Icon(Icons.add),
),
),
),
),
),
);
// Hovered.
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1)));
});
}

View File

@ -166,6 +166,10 @@ void main() {
);
}
RenderObject getOverlayColor(WidgetTester tester) {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
testWidgets('Menu responds to density changes', (WidgetTester tester) async {
Widget buildMenu({VisualDensity? visualDensity = VisualDensity.standard}) {
return MaterialApp(
@ -2494,6 +2498,40 @@ void main() {
final AssertionError exception = tester.takeException() as AssertionError;
expect(exception, isAssertionError);
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgets('MenuItemButton.styleFrom overlayColor overrides default overlay color', (WidgetTester tester) async {
const Color overlayColor = Color(0xffff0000);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: MenuItemButton(
style: MenuItemButton.styleFrom(overlayColor: overlayColor),
onPressed: () {},
child: const Text('MenuItem'),
),
),
));
// Hovered.
final Offset center = tester.getCenter(find.byType(MenuItemButton));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
});
});
group('Layout', () {
@ -3709,6 +3747,45 @@ void main() {
);
expect(intrinsicWidthFinder, findsOneWidget);
});
testWidgets('SubmenuButton.styleFrom overlayColor overrides default overlay color', (WidgetTester tester) async {
const Color overlayColor = Color(0xffff00ff);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: SubmenuButton(
style: SubmenuButton.styleFrom(overlayColor: overlayColor),
menuChildren: <Widget>[
MenuItemButton(
onPressed: () {},
child: const Text('MenuItemButton'),
),
],
child: const Text('Submenu'),
),
),
));
// Hovered.
final Offset center = tester.getCenter(find.byType(SubmenuButton));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
});
}
List<Widget> createTestMenus({

View File

@ -9,6 +9,7 @@ import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
@ -21,6 +22,10 @@ Widget boilerplate({required Widget child}) {
}
void main() {
RenderObject getOverlayColor(WidgetTester tester) {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
testWidgets('SegmentsButton when compositing does not crash', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/135747
// If the render object holds on to a stale canvas reference, this will
@ -584,10 +589,6 @@ void main() {
),
);
RenderObject overlayColor() {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(TextButton),
matching: find.byType(Material),
@ -601,13 +602,13 @@ void main() {
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: theme.colorScheme.onSurface.withOpacity(0.08)));
expect(material.textStyle?.color, theme.colorScheme.onSurface);
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect()..rect(color: theme.colorScheme.onSurface.withOpacity(0.1)));
expect(getOverlayColor(tester), paints..rect()..rect(color: theme.colorScheme.onSurface.withOpacity(0.1)));
expect(material.textStyle?.color, theme.colorScheme.onSurface);
});
@ -763,16 +764,13 @@ void main() {
);
// Test foreground color is applied to the overlay color.
RenderObject overlayColor() {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.down(tester.getCenter(find.text('1')));
await tester.pumpAndSettle();
expect(overlayColor(), paints..rect(color: foregroundColor.withOpacity(0.08)));
expect(getOverlayColor(tester), paints..rect(color: foregroundColor.withOpacity(0.08)));
});
testWidgets('Disabled SegmentedButton has correct states when rebuilding', (WidgetTester tester) async {
@ -909,6 +907,144 @@ void main() {
// The width of the SegmentedButton must be less than the width of the parent widget.
expect(segmentedButtonWidth, lessThan(screenWidth));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/145527
testWidgets('SegmentedButton.styleFrom overlayColor overrides default overlay color', (WidgetTester tester) async {
const Color overlayColor = Color(0xffff0000);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: SegmentedButton<int>(
style: IconButton.styleFrom(overlayColor: overlayColor),
segments: const <ButtonSegment<int>>[
ButtonSegment<int>(
value: 0,
label: Text('Option 1'),
),
ButtonSegment<int>(
value: 1,
label: Text('Option 2'),
),
],
onSelectionChanged: (Set<int> selected) {},
selected: const <int>{1},
),
),
),
),
);
// Hovered selected segment,
Offset center = tester.getCenter(find.text('Option 1'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Hovered unselected segment,
center = tester.getCenter(find.text('Option 2'));
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Highlighted unselected segment (pressed).
center = tester.getCenter(find.text('Option 1'));
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Highlighted selected segment (pressed)
center = tester.getCenter(find.text('Option 2'));
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused unselected segment.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1)));
// Focused selected segment.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1)));
});
testWidgets('SegmentedButton.styleFrom with transparent overlayColor', (WidgetTester tester) async {
const Color overlayColor = Colors.transparent;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: SegmentedButton<int>(
style: IconButton.styleFrom(overlayColor: overlayColor),
segments: const <ButtonSegment<int>>[
ButtonSegment<int>(
value: 0,
label: Text('Option'),
),
],
onSelectionChanged: (Set<int> selected) {},
selected: const <int>{0},
),
),
),
),
);
// Hovered,
final Offset center = tester.getCenter(find.text('Option'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor));
// Highlighted (pressed).
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor)
..rect(color: overlayColor),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor));
});
}
Set<MaterialState> enabled = const <MaterialState>{};

View File

@ -3,10 +3,15 @@
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
RenderObject getOverlayColor(WidgetTester tester) {
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
}
test('SegmentedButtonThemeData copyWith, ==, hashCode basics', () {
expect(const SegmentedButtonThemeData(), const SegmentedButtonThemeData().copyWith());
@ -476,4 +481,93 @@ void main() {
expect(selectedIcon, findsNothing);
}
});
testWidgets('SegmentedButtonTheme SegmentedButton.styleFrom overlayColor overrides default overlay color', (WidgetTester tester) async {
const Color overlayColor = Color(0xffff0000);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
segmentedButtonTheme: SegmentedButtonThemeData(
style: SegmentedButton.styleFrom(overlayColor: overlayColor),
),
),
home: Scaffold(
body: Center(
child: SegmentedButton<int>(
segments: const <ButtonSegment<int>>[
ButtonSegment<int>(
value: 0,
label: Text('Option 1'),
),
ButtonSegment<int>(
value: 1,
label: Text('Option 2'),
),
],
onSelectionChanged: (Set<int> selected) {},
selected: const <int>{1},
),
),
),
),
);
// Hovered selected segment,
Offset center = tester.getCenter(find.text('Option 1'));
final TestGesture gesture = await tester.createGesture(
kind: PointerDeviceKind.mouse,
);
await gesture.addPointer();
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Hovered unselected segment,
center = tester.getCenter(find.text('Option 2'));
await gesture.moveTo(center);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08)));
// Highlighted unselected segment (pressed).
center = tester.getCenter(find.text('Option 1'));
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Highlighted selected segment (pressed)
center = tester.getCenter(find.text('Option 2'));
await gesture.down(center);
await tester.pumpAndSettle();
expect(
getOverlayColor(tester),
paints
..rect(color: overlayColor.withOpacity(0.08))
..rect(color: overlayColor.withOpacity(0.1)),
);
// Remove pressed and hovered states,
await gesture.up();
await tester.pumpAndSettle();
await gesture.moveTo(const Offset(0, 50));
await tester.pumpAndSettle();
// Focused unselected segment.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1)));
// Focused selected segment.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1)));
});
}