Unverified Commit ed8468b9 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add `splashRadius` property to `IconTheme` (#93478)

parent b865e23a
...@@ -29,7 +29,7 @@ class CupertinoIconThemeData extends IconThemeData with Diagnosticable { ...@@ -29,7 +29,7 @@ class CupertinoIconThemeData extends IconThemeData with Diagnosticable {
/// Creates a copy of this icon theme but with the given fields replaced with /// Creates a copy of this icon theme but with the given fields replaced with
/// the new values. /// the new values.
@override @override
CupertinoIconThemeData copyWith({ Color? color, double? opacity, double? size }) { CupertinoIconThemeData copyWith({ Color? color, double? opacity, double? size, double? splashRadius }) {
return CupertinoIconThemeData( return CupertinoIconThemeData(
color: color ?? this.color, color: color ?? this.color,
opacity: opacity ?? this.opacity, opacity: opacity ?? this.opacity,
......
...@@ -306,6 +306,7 @@ class IconButton extends StatelessWidget { ...@@ -306,6 +306,7 @@ class IconButton extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final IconThemeData iconTheme = IconTheme.of(context);
Color? currentColor; Color? currentColor;
if (onPressed != null) if (onPressed != null)
currentColor = color; currentColor = color;
...@@ -319,7 +320,7 @@ class IconButton extends StatelessWidget { ...@@ -319,7 +320,7 @@ class IconButton extends StatelessWidget {
minHeight: _kMinButtonSize, minHeight: _kMinButtonSize,
); );
final BoxConstraints adjustedConstraints = effectiveVisualDensity.effectiveConstraints(unadjustedConstraints); final BoxConstraints adjustedConstraints = effectiveVisualDensity.effectiveConstraints(unadjustedConstraints);
final double effectiveIconSize = iconSize ?? IconTheme.of(context).size ?? 24.0; final double effectiveIconSize = iconSize ?? iconTheme.size ?? 24.0;
Widget result = ConstrainedBox( Widget result = ConstrainedBox(
constraints: adjustedConstraints, constraints: adjustedConstraints,
...@@ -363,11 +364,12 @@ class IconButton extends StatelessWidget { ...@@ -363,11 +364,12 @@ class IconButton extends StatelessWidget {
hoverColor: hoverColor ?? theme.hoverColor, hoverColor: hoverColor ?? theme.hoverColor,
highlightColor: highlightColor ?? theme.highlightColor, highlightColor: highlightColor ?? theme.highlightColor,
splashColor: splashColor ?? theme.splashColor, splashColor: splashColor ?? theme.splashColor,
radius: splashRadius ?? math.max( radius: splashRadius ?? iconTheme.splashRadius ??
Material.defaultSplashRadius, math.max(
(effectiveIconSize + math.min(padding.horizontal, padding.vertical)) * 0.7, Material.defaultSplashRadius,
// x 0.5 for diameter -> radius and + 40% overflow derived from other Material apps. (effectiveIconSize + math.min(padding.horizontal, padding.vertical)) * 0.7,
), // x 0.5 for diameter -> radius and + 40% overflow derived from other Material apps.
),
child: result, child: result,
), ),
); );
......
...@@ -23,7 +23,7 @@ class IconThemeData with Diagnosticable { ...@@ -23,7 +23,7 @@ class IconThemeData with Diagnosticable {
/// ///
/// The opacity applies to both explicit and default icon colors. The value /// The opacity applies to both explicit and default icon colors. The value
/// is clamped between 0.0 and 1.0. /// is clamped between 0.0 and 1.0.
const IconThemeData({this.color, double? opacity, this.size}) : _opacity = opacity; const IconThemeData({this.color, double? opacity, this.size, this.splashRadius}) : _opacity = opacity;
/// Creates an icon theme with some reasonable default values. /// Creates an icon theme with some reasonable default values.
/// ///
...@@ -31,15 +31,17 @@ class IconThemeData with Diagnosticable { ...@@ -31,15 +31,17 @@ class IconThemeData with Diagnosticable {
const IconThemeData.fallback() const IconThemeData.fallback()
: color = const Color(0xFF000000), : color = const Color(0xFF000000),
_opacity = 1.0, _opacity = 1.0,
size = 24.0; size = 24.0,
splashRadius = null;
/// Creates a copy of this icon theme but with the given fields replaced with /// Creates a copy of this icon theme but with the given fields replaced with
/// the new values. /// the new values.
IconThemeData copyWith({ Color? color, double? opacity, double? size }) { IconThemeData copyWith({ Color? color, double? opacity, double? size, double? splashRadius }) {
return IconThemeData( return IconThemeData(
color: color ?? this.color, color: color ?? this.color,
opacity: opacity ?? this.opacity, opacity: opacity ?? this.opacity,
size: size ?? this.size, size: size ?? this.size,
splashRadius: splashRadius ?? this.splashRadius,
); );
} }
...@@ -53,6 +55,7 @@ class IconThemeData with Diagnosticable { ...@@ -53,6 +55,7 @@ class IconThemeData with Diagnosticable {
color: other.color, color: other.color,
opacity: other.opacity, opacity: other.opacity,
size: other.size, size: other.size,
splashRadius: other.splashRadius,
); );
} }
...@@ -87,6 +90,9 @@ class IconThemeData with Diagnosticable { ...@@ -87,6 +90,9 @@ class IconThemeData with Diagnosticable {
/// The default size for icons. /// The default size for icons.
final double? size; final double? size;
/// The default splash radius for [IconButton]s.
final double? splashRadius;
/// Linearly interpolate between two icon theme data objects. /// Linearly interpolate between two icon theme data objects.
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
...@@ -96,6 +102,7 @@ class IconThemeData with Diagnosticable { ...@@ -96,6 +102,7 @@ class IconThemeData with Diagnosticable {
color: Color.lerp(a?.color, b?.color, t), color: Color.lerp(a?.color, b?.color, t),
opacity: ui.lerpDouble(a?.opacity, b?.opacity, t), opacity: ui.lerpDouble(a?.opacity, b?.opacity, t),
size: ui.lerpDouble(a?.size, b?.size, t), size: ui.lerpDouble(a?.size, b?.size, t),
splashRadius: ui.lerpDouble(a?.splashRadius, b?.splashRadius, t),
); );
} }
...@@ -106,11 +113,12 @@ class IconThemeData with Diagnosticable { ...@@ -106,11 +113,12 @@ class IconThemeData with Diagnosticable {
return other is IconThemeData return other is IconThemeData
&& other.color == color && other.color == color
&& other.opacity == opacity && other.opacity == opacity
&& other.size == size; && other.size == size
&& other.splashRadius == splashRadius;
} }
@override @override
int get hashCode => hashValues(color, opacity, size); int get hashCode => hashValues(color, opacity, size, splashRadius);
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
...@@ -118,5 +126,6 @@ class IconThemeData with Diagnosticable { ...@@ -118,5 +126,6 @@ class IconThemeData with Diagnosticable {
properties.add(ColorProperty('color', color, defaultValue: null)); properties.add(ColorProperty('color', color, defaultValue: null));
properties.add(DoubleProperty('opacity', opacity, defaultValue: null)); properties.add(DoubleProperty('opacity', opacity, defaultValue: null));
properties.add(DoubleProperty('size', size, defaultValue: null)); properties.add(DoubleProperty('size', size, defaultValue: null));
properties.add(DoubleProperty('splashRadius', splashRadius, defaultValue: null));
} }
} }
...@@ -480,6 +480,40 @@ void main() { ...@@ -480,6 +480,40 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('IconButton can inherit splashRadius from IconTheme', (WidgetTester tester) async {
const double splashRadius = 30.0;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: IconTheme(
data: const IconThemeData(
splashRadius: splashRadius,
),
child: IconButton(
icon: const Icon(Icons.android),
onPressed: () { /* enable the button */ },
),
),
),
),
),
);
final Offset center = tester.getCenter(find.byType(IconButton));
final TestGesture gesture = await tester.startGesture(center);
await tester.pump(); // Start gesture.
await tester.pump(const Duration(milliseconds: 1000)); // Wait for splash to be well under way.
expect(
Material.of(tester.element(find.byType(IconButton))),
paints
..circle(radius: splashRadius),
);
await gesture.up();
});
testWidgets('IconButton Semantics (enabled)', (WidgetTester tester) async { testWidgets('IconButton Semantics (enabled)', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
......
...@@ -7,7 +7,7 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('IconThemeData control test', () { test('IconThemeData control test', () {
const IconThemeData data = IconThemeData(color: Color(0xAAAAAAAA), opacity: 0.5, size: 16.0); const IconThemeData data = IconThemeData(color: Color(0xAAAAAAAA), opacity: 0.5, size: 16.0, splashRadius: 30.0);
expect(data, hasOneLineDescription); expect(data, hasOneLineDescription);
expect(data, equals(data.copyWith())); expect(data, equals(data.copyWith()));
...@@ -17,24 +17,27 @@ void main() { ...@@ -17,24 +17,27 @@ void main() {
expect(lerped.color, const Color(0xBF7F7F7F)); expect(lerped.color, const Color(0xBF7F7F7F));
expect(lerped.opacity, 0.625); expect(lerped.opacity, 0.625);
expect(lerped.size, 18.0); expect(lerped.size, 18.0);
expect(lerped.splashRadius, 22.5);
}); });
test('IconThemeData lerp with first null', () { test('IconThemeData lerp with first null', () {
const IconThemeData data = IconThemeData(color: Color(0xFFFFFFFF), opacity: 1.0, size: 16.0); const IconThemeData data = IconThemeData(color: Color(0xFFFFFFFF), opacity: 1.0, size: 16.0, splashRadius: 30.0);
final IconThemeData lerped = IconThemeData.lerp(null, data, 0.25); final IconThemeData lerped = IconThemeData.lerp(null, data, 0.25);
expect(lerped.color, const Color(0x40FFFFFF)); expect(lerped.color, const Color(0x40FFFFFF));
expect(lerped.opacity, 0.25); expect(lerped.opacity, 0.25);
expect(lerped.size, 4.0); expect(lerped.size, 4.0);
expect(lerped.splashRadius, 7.5);
}); });
test('IconThemeData lerp with second null', () { test('IconThemeData lerp with second null', () {
const IconThemeData data = IconThemeData(color: Color(0xFFFFFFFF), opacity: 1.0, size: 16.0); const IconThemeData data = IconThemeData(color: Color(0xFFFFFFFF), opacity: 1.0, size: 16.0, splashRadius: 30.0);
final IconThemeData lerped = IconThemeData.lerp(data, null, 0.25); final IconThemeData lerped = IconThemeData.lerp(data, null, 0.25);
expect(lerped.color, const Color(0xBFFFFFFF)); expect(lerped.color, const Color(0xBFFFFFFF));
expect(lerped.opacity, 0.75); expect(lerped.opacity, 0.75);
expect(lerped.size, 12.0); expect(lerped.size, 12.0);
expect(lerped.splashRadius, 22.5);
}); });
test('IconThemeData lerp with both null', () { test('IconThemeData lerp with both null', () {
...@@ -42,5 +45,6 @@ void main() { ...@@ -42,5 +45,6 @@ void main() {
expect(lerped.color, null); expect(lerped.color, null);
expect(lerped.opacity, null); expect(lerped.opacity, null);
expect(lerped.size, null); expect(lerped.size, null);
expect(lerped.splashRadius, null);
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment