Unverified Commit 49632fc3 authored by Mateus Felipe C. C. Pinto's avatar Mateus Felipe C. C. Pinto Committed by GitHub

Provide parameter to Icon and IconThemeData for they to consider the context's...

Provide parameter to Icon and IconThemeData for they to consider the context's text scaler (#135708)

Provide a parameter `applyTextScaling` to both `Icon` and `IconDataTheme`. When `true`, the context's `TextScaler` will apply it's `scale` method to the icon size.

Fixes #115466
parent 44981755
......@@ -19,6 +19,7 @@ class CupertinoIconThemeData extends IconThemeData with Diagnosticable {
super.color,
super.opacity,
super.shadows,
super.applyTextScaling,
});
/// Called by [IconTheme.of] to resolve [color] against the given [BuildContext].
......@@ -40,6 +41,7 @@ class CupertinoIconThemeData extends IconThemeData with Diagnosticable {
Color? color,
double? opacity,
List<Shadow>? shadows,
bool? applyTextScaling,
}) {
return CupertinoIconThemeData(
size: size ?? this.size,
......@@ -50,6 +52,7 @@ class CupertinoIconThemeData extends IconThemeData with Diagnosticable {
color: color ?? this.color,
opacity: opacity ?? this.opacity,
shadows: shadows ?? this.shadows,
applyTextScaling: applyTextScaling ?? this.applyTextScaling,
);
}
......
......@@ -27,10 +27,11 @@ import 'text_span.dart';
export 'package:flutter/services.dart' show TextRange, TextSelection;
// The default font size if none is specified. This should be kept in
// sync with the default values in text_style.dart, as well as the
// defaults set in the engine (eg, LibTxt's text_style.h, paragraph_style.h).
const double _kDefaultFontSize = 14.0;
/// The default font size if none is specified.
///
/// This should be kept in sync with the defaults set in the engine (e.g.,
/// LibTxt's text_style.h, paragraph_style.h).
const double kDefaultFontSize = 14.0;
/// How overflowing text should be handled.
///
......@@ -971,7 +972,7 @@ class TextPainter {
// Use the default font size to multiply by as RichText does not
// perform inheriting [TextStyle]s and would otherwise
// fail to apply textScaler.
fontSize: textScaler.scale(_kDefaultFontSize),
fontSize: textScaler.scale(kDefaultFontSize),
maxLines: maxLines,
textHeightBehavior: _textHeightBehavior,
ellipsis: ellipsis,
......
......@@ -29,11 +29,6 @@ const String _kColorForegroundWarning = 'Cannot provide both a color and a foreg
const String _kColorBackgroundWarning = 'Cannot provide both a backgroundColor and a background\n'
'The backgroundColor argument is just a shorthand for "background: Paint()..color = color".';
// The default font size if none is specified. This should be kept in
// sync with the default values in text_painter.dart, as well as the
// defaults set in the engine (eg, LibTxt's text_style.h, paragraph_style.h).
const double _kDefaultFontSize = 14.0;
// Examples can assume:
// late BuildContext context;
......@@ -1353,7 +1348,7 @@ class TextStyle with Diagnosticable {
fontWeight: fontWeight ?? this.fontWeight,
fontStyle: fontStyle ?? this.fontStyle,
fontFamily: fontFamily ?? this.fontFamily,
fontSize: textScaler.scale(fontSize ?? this.fontSize ?? _kDefaultFontSize),
fontSize: textScaler.scale(fontSize ?? this.fontSize ?? kDefaultFontSize),
height: height ?? this.height,
textHeightBehavior: effectiveTextHeightBehavior,
strutStyle: strutStyle == null ? null : ui.StrutStyle(
......
......@@ -13,6 +13,7 @@ import 'framework.dart';
import 'icon_data.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
import 'media_query.dart';
/// A graphical icon widget drawn with a glyph from a font described in
/// an [IconData] such as material's predefined [IconData]s in [Icons].
......@@ -80,6 +81,7 @@ class Icon extends StatelessWidget {
this.shadows,
this.semanticLabel,
this.textDirection,
this.applyTextScaling,
}) : assert(fill == null || (0.0 <= fill && fill <= 1.0)),
assert(weight == null || (0.0 < weight)),
assert(opticalSize == null || (0.0 < opticalSize));
......@@ -231,6 +233,16 @@ class Icon extends StatelessWidget {
/// specified, either directly using this property or using [Directionality].
final TextDirection? textDirection;
/// Whether to scale the size of this widget using the ambient [MediaQuery]'s [TextScaler].
///
/// This is specially useful when you have an icon associated with a text, as
/// scaling the text without scaling the icon would result in a confusing
/// interface.
///
/// Defaults to the nearest [IconTheme]'s
/// [IconThemeData.applyTextScaling].
final bool? applyTextScaling;
@override
Widget build(BuildContext context) {
assert(this.textDirection != null || debugCheckHasDirectionality(context));
......@@ -238,7 +250,11 @@ class Icon extends StatelessWidget {
final IconThemeData iconTheme = IconTheme.of(context);
final double? iconSize = size ?? iconTheme.size;
final bool applyTextScaling = this.applyTextScaling ?? iconTheme.applyTextScaling ?? false;
final double tentativeIconSize = size ?? iconTheme.size ?? kDefaultFontSize;
final double iconSize = applyTextScaling ? MediaQuery.textScalerOf(context).scale(tentativeIconSize) : tentativeIconSize;
final double? iconFill = fill ?? iconTheme.fill;
......@@ -332,5 +348,6 @@ class Icon extends StatelessWidget {
properties.add(IterableProperty<Shadow>('shadows', shadows, defaultValue: null));
properties.add(StringProperty('semanticLabel', semanticLabel, defaultValue: null));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('applyTextScaling', applyTextScaling, defaultValue: null));
}
}
......@@ -76,6 +76,7 @@ class IconTheme extends InheritedTheme {
color: iconThemeData.color ?? const IconThemeData.fallback().color,
opacity: iconThemeData.opacity ?? const IconThemeData.fallback().opacity,
shadows: iconThemeData.shadows ?? const IconThemeData.fallback().shadows,
applyTextScaling: iconThemeData.applyTextScaling ?? const IconThemeData.fallback().applyTextScaling,
);
}
......
......@@ -31,6 +31,7 @@ class IconThemeData with Diagnosticable {
this.color,
double? opacity,
this.shadows,
this.applyTextScaling,
}) : _opacity = opacity,
assert(fill == null || (0.0 <= fill && fill <= 1.0)),
assert(weight == null || (0.0 < weight)),
......@@ -48,7 +49,8 @@ class IconThemeData with Diagnosticable {
opticalSize = 48.0,
color = const Color(0xFF000000),
_opacity = 1.0,
shadows = null;
shadows = null,
applyTextScaling = false;
/// Creates a copy of this icon theme but with the given fields replaced with
/// the new values.
......@@ -61,6 +63,7 @@ class IconThemeData with Diagnosticable {
Color? color,
double? opacity,
List<Shadow>? shadows,
bool? applyTextScaling,
}) {
return IconThemeData(
size: size ?? this.size,
......@@ -71,6 +74,7 @@ class IconThemeData with Diagnosticable {
color: color ?? this.color,
opacity: opacity ?? this.opacity,
shadows: shadows ?? this.shadows,
applyTextScaling: applyTextScaling ?? this.applyTextScaling,
);
}
......@@ -90,6 +94,7 @@ class IconThemeData with Diagnosticable {
color: other.color,
opacity: other.opacity,
shadows: other.shadows,
applyTextScaling: other.applyTextScaling,
);
}
......@@ -118,7 +123,8 @@ class IconThemeData with Diagnosticable {
&& grade != null
&& opticalSize != null
&& color != null
&& opacity != null;
&& opacity != null
&& applyTextScaling != null;
/// The default for [Icon.size].
///
......@@ -163,6 +169,9 @@ class IconThemeData with Diagnosticable {
/// The default for [Icon.shadows].
final List<Shadow>? shadows;
/// The default for [Icon.applyTextScaling].
final bool? applyTextScaling;
/// Linearly interpolate between two icon theme data objects.
///
/// {@macro dart.ui.shadow.lerp}
......@@ -179,6 +188,7 @@ class IconThemeData with Diagnosticable {
color: Color.lerp(a?.color, b?.color, t),
opacity: ui.lerpDouble(a?.opacity, b?.opacity, t),
shadows: Shadow.lerpList(a?.shadows, b?.shadows, t),
applyTextScaling: t < 0.5 ? a?.applyTextScaling : b?.applyTextScaling,
);
}
......@@ -195,7 +205,8 @@ class IconThemeData with Diagnosticable {
&& other.opticalSize == opticalSize
&& other.color == color
&& other.opacity == opacity
&& listEquals(other.shadows, shadows);
&& listEquals(other.shadows, shadows)
&& other.applyTextScaling == applyTextScaling;
}
@override
......@@ -208,6 +219,7 @@ class IconThemeData with Diagnosticable {
color,
opacity,
shadows == null ? null : Object.hashAll(shadows!),
applyTextScaling,
);
@override
......@@ -221,5 +233,6 @@ class IconThemeData with Diagnosticable {
properties.add(ColorProperty('color', color, defaultValue: null));
properties.add(DoubleProperty('opacity', opacity, defaultValue: null));
properties.add(IterableProperty<Shadow>('shadows', shadows, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('applyTextScaling', applyTextScaling, defaultValue: null));
}
}
......@@ -10,8 +10,6 @@ import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
const double _kEngineDefaultFontSize = 14.0;
// Examples can assume:
// late WidgetSpan myWidgetSpan;
......@@ -100,7 +98,7 @@ class WidgetSpan extends PlaceholderSpan {
final List<Widget> widgets = <Widget>[];
// _kEngineDefaultFontSize is the default font size to use when none of the
// ancestor spans specifies one.
final List<double> fontSizeStack = <double>[_kEngineDefaultFontSize];
final List<double> fontSizeStack = <double>[kDefaultFontSize];
int index = 0;
// This assumes an InlineSpan tree's logical order is equivalent to preorder.
bool visitSubtree(InlineSpan span) {
......
......@@ -15,7 +15,8 @@ void main() {
grade: 0.0,
opticalSize: 48.0,
color: Color(0xAAAAAAAA),
opacity: 0.5
opacity: 0.5,
applyTextScaling: true,
);
late IconThemeData retrieved;
......
......@@ -113,6 +113,126 @@ void main() {
expect(renderObject.size, equals(const Size.square(24.0)));
});
testWidgetsWithLeakTracking('Icon sizing - no theme, default size, considering text scaler', (WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(
textScaler: _TextDoubler(),
),
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: Icon(
null,
applyTextScaling: true,
),
),
),
),
);
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(48.0)));
});
testWidgetsWithLeakTracking('Icon sizing - no theme, explicit size, considering text scaler', (WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(
textScaler: _TextDoubler(),
),
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: Icon(
null,
size: 96.0,
applyTextScaling: true,
),
),
),
),
);
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(192.0)));
});
testWidgetsWithLeakTracking('Icon sizing - sized theme, considering text scaler', (WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(
textScaler: _TextDoubler(),
),
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: IconTheme(
data: IconThemeData(
size: 36.0,
applyTextScaling: true,
),
child: Icon(null),
),
),
),
),
);
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(72.0)));
});
testWidgetsWithLeakTracking('Icon sizing - sized theme, explicit size, considering text scale', (WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(
textScaler: _TextDoubler(),
),
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: IconTheme(
data: IconThemeData(
size: 36.0,
applyTextScaling: true,
),
child: Icon(
null,
size: 48.0,
applyTextScaling: false,
),
),
),
),
),
);
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(48.0)));
});
testWidgetsWithLeakTracking('Icon sizing - sizeless theme, default size, default consideration for text scaler', (WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(
textScaler: _TextDoubler(),
),
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: IconTheme(
data: IconThemeData(),
child: Icon(null),
),
),
),
),
);
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(24.0)));
});
testWidgetsWithLeakTracking('Icon with custom font', (WidgetTester tester) async {
await tester.pumpWidget(
......@@ -335,3 +455,13 @@ void main() {
expect(() => Icon(Icons.abc, opticalSize: 0), throwsAssertionError);
});
}
final class _TextDoubler extends TextScaler {
const _TextDoubler();
@override
double scale(double fontSize) => fontSize * 2.0;
@override
double get textScaleFactor => 2.0;
}
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