Commit cff31a3f authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

IconThemeData.size (#4633)

parent 5809a0cb
...@@ -26,29 +26,34 @@ import 'theme.dart'; ...@@ -26,29 +26,34 @@ import 'theme.dart';
/// ///
/// See also: /// See also:
/// ///
/// * [IconButton], for interactive icons /// * [IconButton], for interactive icons.
/// * [Icons], for the list of available icons for use with this class /// * [Icons], for the list of available icons for use with this class.
/// * [IconTheme], which provides ambient configuration for icons.
class Icon extends StatelessWidget { class Icon extends StatelessWidget {
/// Creates an icon. /// Creates an icon.
/// ///
/// The [size] argument most not be null. /// The [size] and [color] default to the value given by the current [IconTheme].
Icon({ const Icon({
Key key, Key key,
this.icon, this.icon,
this.size: 24.0, this.size,
this.color this.color
}) : super(key: key) { }) : super(key: key);
assert(size != null);
} /// The icon to display. The available icons are described in [Icons].
///
/// If null, no icon is shown.
final IconData icon;
/// The size of the icon in logical pixels. /// The size of the icon in logical pixels.
/// ///
/// Icons occupy a square with width and height equal to size. /// Icons occupy a square with width and height equal to size.
///
/// Defaults to the current [IconTheme] size, if any. If there is no
/// [IconTheme], or it does not specify an explicit size, then it defaults to
/// 24.0.
final double size; final double size;
/// The icon to display. The available icons are described in [Icons].
final IconData icon;
/// The color to use when drawing the icon. /// The color to use when drawing the icon.
/// ///
/// Defaults to the current [IconTheme] color, if any. If there is /// Defaults to the current [IconTheme] color, if any. If there is
...@@ -56,6 +61,9 @@ class Icon extends StatelessWidget { ...@@ -56,6 +61,9 @@ class Icon extends StatelessWidget {
/// and black if the theme is light. See [Theme] to set the current /// and black if the theme is light. See [Theme] to set the current
/// theme and [ThemeData.brightness] for setting the current theme's /// theme and [ThemeData.brightness] for setting the current theme's
/// brightness. /// brightness.
///
/// The given color will be adjusted by the opacity of the current
/// [IconTheme], if any.
final Color color; final Color color;
Color _getDefaultColorForBrightness(Brightness brightness) { Color _getDefaultColorForBrightness(Brightness brightness) {
...@@ -75,8 +83,10 @@ class Icon extends StatelessWidget { ...@@ -75,8 +83,10 @@ class Icon extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double iconSize = size ?? IconTheme.of(context)?.size ?? 24.0;
if (icon == null) if (icon == null)
return new SizedBox(width: size, height: size); return new SizedBox(width: iconSize, height: iconSize);
final double iconOpacity = IconTheme.of(context)?.opacity ?? 1.0; final double iconOpacity = IconTheme.of(context)?.opacity ?? 1.0;
Color iconColor = color ?? _getDefaultColor(context); Color iconColor = color ?? _getDefaultColor(context);
...@@ -85,15 +95,15 @@ class Icon extends StatelessWidget { ...@@ -85,15 +95,15 @@ class Icon extends StatelessWidget {
return new ExcludeSemantics( return new ExcludeSemantics(
child: new SizedBox( child: new SizedBox(
width: size, width: iconSize,
height: size, height: iconSize,
child: new Center( child: new Center(
child: new Text( child: new Text(
new String.fromCharCode(icon.codePoint), new String.fromCharCode(icon.codePoint),
style: new TextStyle( style: new TextStyle(
inherit: false, inherit: false,
color: iconColor, color: iconColor,
fontSize: size, fontSize: iconSize,
fontFamily: 'MaterialIcons' fontFamily: 'MaterialIcons'
) )
) )
...@@ -105,9 +115,14 @@ class Icon extends StatelessWidget { ...@@ -105,9 +115,14 @@ class Icon extends StatelessWidget {
@override @override
void debugFillDescription(List<String> description) { void debugFillDescription(List<String> description) {
super.debugFillDescription(description); super.debugFillDescription(description);
description.add('$icon'); if (icon != null) {
description.add('size: $size'); description.add('$icon');
if (this.color != null) } else {
description.add('<empty>');
}
if (size != null)
description.add('size: $size');
if (color != null)
description.add('color: $color'); description.add('color: $color');
} }
} }
...@@ -7,9 +7,10 @@ import 'package:meta/meta.dart'; ...@@ -7,9 +7,10 @@ import 'package:meta/meta.dart';
import 'icon_theme_data.dart'; import 'icon_theme_data.dart';
/// Controls the color and opacity of icons in a widget subtree. /// Controls the default color, opacity, and size of icons in a widget subtree.
class IconTheme extends InheritedWidget { class IconTheme extends InheritedWidget {
/// Creates an icon theme that controls the color and opacity of descendant widgets. /// Creates an icon theme that controls the color, opacity, and size of
/// descendant widgets.
/// ///
/// Both [data] and [child] arguments must not be null. /// Both [data] and [child] arguments must not be null.
IconTheme({ IconTheme({
...@@ -21,7 +22,7 @@ class IconTheme extends InheritedWidget { ...@@ -21,7 +22,7 @@ class IconTheme extends InheritedWidget {
assert(child != null); assert(child != null);
} }
/// The color and opacity to use for icons in this subtree. /// The color, opacity, and size to use for icons in this subtree.
final IconThemeData data; final IconThemeData data;
/// The data from the closest instance of this class that encloses the given context. /// The data from the closest instance of this class that encloses the given context.
......
...@@ -5,16 +5,16 @@ ...@@ -5,16 +5,16 @@
import 'dart:ui' as ui show lerpDouble; import 'dart:ui' as ui show lerpDouble;
import 'dart:ui' show Color, hashValues; import 'dart:ui' show Color, hashValues;
/// Defines the color and opacity of icons. /// Defines the color, opacity, and size of icons.
/// ///
/// Used by [IconTheme] to control the color and opacity of icons in a widget /// Used by [IconTheme] to control the color, opacity, and size of icons in a
/// subtree. /// widget subtree.
class IconThemeData { class IconThemeData {
/// Creates an icon theme data. /// Creates an icon theme data.
/// ///
/// 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: 1.0 }) : _opacity = opacity; const IconThemeData({ this.color, double opacity: 1.0, this.size }) : _opacity = opacity;
/// The default color for icons. /// The default color for icons.
final Color color; final Color color;
...@@ -23,25 +23,42 @@ class IconThemeData { ...@@ -23,25 +23,42 @@ class IconThemeData {
double get opacity => (_opacity ?? 1.0).clamp(0.0, 1.0); double get opacity => (_opacity ?? 1.0).clamp(0.0, 1.0);
final double _opacity; final double _opacity;
/// The default size for icons.
final double size;
/// Linearly interpolate between two icon theme data objects. /// Linearly interpolate between two icon theme data objects.
static IconThemeData lerp(IconThemeData begin, IconThemeData end, double t) { static IconThemeData lerp(IconThemeData begin, IconThemeData end, double t) {
return new IconThemeData( return new IconThemeData(
color: Color.lerp(begin.color, end.color, t), color: Color.lerp(begin.color, end.color, t),
opacity: ui.lerpDouble(begin.opacity, end.opacity, t) opacity: ui.lerpDouble(begin.opacity, end.opacity, t),
size: ui.lerpDouble(begin.size, end.size, t)
); );
} }
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
if (other is! IconThemeData) if (other.runtimeType != runtimeType)
return false; return false;
final IconThemeData typedOther = other; final IconThemeData typedOther = other;
return color == typedOther.color && opacity == typedOther.opacity; return color == typedOther.color
&& opacity == typedOther.opacity
&& size == typedOther.size;
} }
@override @override
int get hashCode => hashValues(color, opacity); int get hashCode => hashValues(color, opacity, size);
@override @override
String toString() => '$color'; String toString() {
List<String> result = <String>[];
if (color != null)
result.add('color: $color');
if (opacity != 1.0)
result.add('opacity: $opacity');
if (size != null)
result.add('size: $size');
if (result.length == 0)
return '<no theme>';
return result.join(', ');
}
} }
// Copyright 2015 The Chromium 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new Icon()
)
);
RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(24.0)));
});
testWidgets('Icon sizing - no theme, explicit size', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new Icon(
size: 96.0
)
)
);
RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(96.0)));
});
testWidgets('Icon sizing - sized theme', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new IconTheme(
data: new IconThemeData(size: 36.0),
child: new Icon()
)
)
);
RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(36.0)));
});
testWidgets('Icon sizing - sized theme, explicit size', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new IconTheme(
data: new IconThemeData(size: 36.0),
child: new Icon(
size: 48.0
)
)
)
);
RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(48.0)));
});
testWidgets('Icon sizing - sizeless theme, default size', (WidgetTester tester) async {
await tester.pumpWidget(
new Center(
child: new IconTheme(
data: new IconThemeData(),
child: new Icon()
)
)
);
RenderBox renderObject = tester.renderObject(find.byType(Icon));
expect(renderObject.size, equals(const Size.square(24.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