icon.dart 7.23 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/rendering.dart';
6

7
import 'basic.dart';
8
import 'debug.dart';
9 10
import 'framework.dart';
import 'icon_data.dart';
Adam Barth's avatar
Adam Barth committed
11
import 'icon_theme.dart';
Ian Hickson's avatar
Ian Hickson committed
12
import 'icon_theme_data.dart';
13

14 15
/// A graphical icon widget drawn with a glyph from a font described in
/// an [IconData] such as material's predefined [IconData]s in [Icons].
16
///
17 18
/// Icons are not interactive. For an interactive icon, consider material's
/// [IconButton].
19
///
Ian Hickson's avatar
Ian Hickson committed
20 21 22 23
/// There must be an ambient [Directionality] widget when using [Icon].
/// Typically this is introduced automatically by the [WidgetsApp] or
/// [MaterialApp].
///
24 25 26
/// This widget assumes that the rendered icon is squared. Non-squared icons may
/// render incorrectly.
///
27
/// {@tool snippet}
28
///
29 30 31
/// This example shows how to create a [Row] of [Icon]s in different colors and
/// sizes. The first [Icon] uses a [semanticLabel] to announce in accessibility
/// modes like TalkBack and VoiceOver.
32
///
33
/// ![A row of icons representing a pink heart, a green musical note, and a blue umbrella](https://flutter.github.io/assets-for-api-docs/assets/widgets/icon.png)
34
///
35
/// ```dart
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/// Row(
///   mainAxisAlignment: MainAxisAlignment.spaceAround,
///   children: const <Widget>[
///     Icon(
///       Icons.favorite,
///       color: Colors.pink,
///       size: 24.0,
///       semanticLabel: 'Text to announce in accessibility modes',
///     ),
///     Icon(
///       Icons.audiotrack,
///       color: Colors.green,
///       size: 30.0,
///     ),
///     Icon(
///       Icons.beach_access,
///       color: Colors.blue,
///       size: 36.0,
///     ),
///   ],
56 57 58 59
/// )
/// ```
/// {@end-tool}
///
60 61
/// See also:
///
62 63 64
///  * [IconButton], for interactive icons.
///  * [Icons], for the list of available icons for use with this class.
///  * [IconTheme], which provides ambient configuration for icons.
Ian Hickson's avatar
Ian Hickson committed
65
///  * [ImageIcon], for showing icons from [AssetImage]s or other [ImageProvider]s.
66
class Icon extends StatelessWidget {
67 68
  /// Creates an icon.
  ///
69
  /// The [size] and [color] default to the value given by the current [IconTheme].
70 71
  const Icon(
    this.icon, {
72
    Key? key,
73
    this.size,
74 75
    this.color,
    this.semanticLabel,
76
    this.textDirection,
77 78 79 80
  }) : super(key: key);

  /// The icon to display. The available icons are described in [Icons].
  ///
Ian Hickson's avatar
Ian Hickson committed
81 82
  /// The icon can be null, in which case the widget will render as an empty
  /// space of the specified [size].
83
  final IconData? icon;
84

Adam Barth's avatar
Adam Barth committed
85 86 87
  /// The size of the icon in logical pixels.
  ///
  /// Icons occupy a square with width and height equal to size.
88 89 90 91
  ///
  /// 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.
92 93
  ///
  /// If this [Icon] is being placed inside an [IconButton], then use
94
  /// [IconButton.iconSize] instead, so that the [IconButton] can make the splash
95 96
  /// area the appropriate size as well. The [IconButton] uses an [IconTheme] to
  /// pass down the size to the [Icon].
97
  final double? size;
Adam Barth's avatar
Adam Barth committed
98 99

  /// The color to use when drawing the icon.
100
  ///
101
  /// Defaults to the current [IconTheme] color, if any.
102
  ///
103 104
  /// The color (whether specified explicitly here or obtained from the
  /// [IconTheme]) will be further adjusted by the opacity of the current
105
  /// [IconTheme], if any.
106
  ///
107 108 109
  /// In material apps, if there is a [Theme] without any [IconTheme]s
  /// specified, icon colors default to white if the theme is dark
  /// and black if the theme is light.
110
  ///
111 112
  /// If no [IconTheme] and no [Theme] is specified, icons will default to
  /// black.
113
  ///
114 115 116
  /// See [Theme] to set the current theme and [ThemeData.brightness]
  /// for setting the current theme's brightness.
  ///
117
  /// {@tool snippet}
118
  /// Typically, a Material Design color will be used, as follows:
119 120
  ///
  /// ```dart
121
  /// Icon(
122
  ///   Icons.widgets,
123 124
  ///   color: Colors.blue.shade400,
  /// )
125
  /// ```
126
  /// {@end-tool}
127
  final Color? color;
128

129 130
  /// Semantic label for the icon.
  ///
131
  /// Announced in accessibility modes (e.g TalkBack/VoiceOver).
132 133
  /// This label does not show in the UI.
  ///
134 135
  ///  * [SemanticsProperties.label], which is set to [semanticLabel] in the
  ///    underlying	 [Semantics] widget.
136
  final String? semanticLabel;
137

138 139 140 141 142 143 144 145 146 147 148 149 150
  /// The text direction to use for rendering the icon.
  ///
  /// If this is null, the ambient [Directionality] is used instead.
  ///
  /// Some icons follow the reading direction. For example, "back" buttons point
  /// left in left-to-right environments and right in right-to-left
  /// environments. Such icons have their [IconData.matchTextDirection] field
  /// set to true, and the [Icon] widget uses the [textDirection] to determine
  /// the orientation in which to draw the icon.
  ///
  /// This property has no effect if the [icon]'s [IconData.matchTextDirection]
  /// field is false, but for consistency a text direction value must always be
  /// specified, either directly using this property or using [Directionality].
151
  final TextDirection? textDirection;
152

153
  @override
154
  Widget build(BuildContext context) {
155
    assert(this.textDirection != null || debugCheckHasDirectionality(context));
156
    final TextDirection textDirection = this.textDirection ?? Directionality.of(context);
Ian Hickson's avatar
Ian Hickson committed
157

158
    final IconThemeData iconTheme = IconTheme.of(context);
Ian Hickson's avatar
Ian Hickson committed
159

160
    final double? iconSize = size ?? iconTheme.size;
161

162
    if (icon == null) {
163
      return Semantics(
164
        label: semanticLabel,
165
        child: SizedBox(width: iconSize, height: iconSize),
166
      );
167
    }
Hans Muller's avatar
Hans Muller committed
168

169 170
    final double iconOpacity = iconTheme.opacity ?? 1.0;
    Color iconColor = color ?? iconTheme.color!;
Hans Muller's avatar
Hans Muller committed
171
    if (iconOpacity != 1.0)
172
      iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity);
173

174
    Widget iconWidget = RichText(
175
      overflow: TextOverflow.visible, // Never clip.
176
      textDirection: textDirection, // Since we already fetched it for the assert...
177
      text: TextSpan(
178
        text: String.fromCharCode(icon!.codePoint),
179
        style: TextStyle(
180 181 182
          inherit: false,
          color: iconColor,
          fontSize: iconSize,
183 184
          fontFamily: icon!.fontFamily,
          package: icon!.fontPackage,
185 186 187 188
        ),
      ),
    );

189
    if (icon!.matchTextDirection) {
190 191
      switch (textDirection) {
        case TextDirection.rtl:
192 193
          iconWidget = Transform(
            transform: Matrix4.identity()..scale(-1.0, 1.0, 1.0),
194 195 196 197 198 199 200 201 202 203
            alignment: Alignment.center,
            transformHitTests: false,
            child: iconWidget,
          );
          break;
        case TextDirection.ltr:
          break;
      }
    }

204
    return Semantics(
205
      label: semanticLabel,
206 207
      child: ExcludeSemantics(
        child: SizedBox(
208 209
          width: iconSize,
          height: iconSize,
210
          child: Center(
211
            child: iconWidget,
Ian Hickson's avatar
Ian Hickson committed
212 213 214
          ),
        ),
      ),
215 216
    );
  }
Hixie's avatar
Hixie committed
217

218
  @override
219 220
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
221
    properties.add(IconDataProperty('icon', icon, ifNull: '<empty>', showName: false));
222
    properties.add(DoubleProperty('size', size, defaultValue: null));
223
    properties.add(ColorProperty('color', color, defaultValue: null));
Hixie's avatar
Hixie committed
224
  }
225
}